diff options
author | Tomáš Rylek <trylek@microsoft.com> | 2020-12-08 05:19:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-08 05:19:44 +0300 |
commit | 69e114c1abf91241a0eeecf1ecceab4711b8aa62 (patch) | |
tree | b81a0b35748f5e598412bcc504335cdbd322cd43 /src/coreclr/md | |
parent | 0ec07945a9759a72a689edbb01e69b232e26e05a (diff) |
December infra rollout - remove duplicated 'src' from coreclr subrepo (src/coreclr/src becomes src/coreclr) (#44973)
* Move src/coreclr/src/Directory.Build.targets to src/coreclr
Merge src/coreclr/src/CMakeLists.txt into src/coreclr/CMakeLists.txt
* Mechanical move of src/coreclr/src to src/coreclr
* Scripts adjustments to reflect the changed paths
Diffstat (limited to 'src/coreclr/md')
130 files changed, 69914 insertions, 0 deletions
diff --git a/src/coreclr/md/CMakeLists.txt b/src/coreclr/md/CMakeLists.txt new file mode 100644 index 00000000000..53a675b4f50 --- /dev/null +++ b/src/coreclr/md/CMakeLists.txt @@ -0,0 +1,21 @@ +add_compile_definitions(FEATURE_METADATA_EMIT) +add_compile_definitions(FEATURE_METADATA_INTERNAL_APIS) +add_compile_definitions($<$<OR:$<BOOL:$<TARGET_PROPERTY:DAC_COMPONENT>>,$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>>:FEATURE_METADATA_EMIT_IN_DEBUGGER>) +add_compile_definitions($<$<NOT:$<OR:$<BOOL:$<TARGET_PROPERTY:DAC_COMPONENT>>,$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>>>:FEATURE_METADATA_IN_VM>) +add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>:FEATURE_METADATA_CUSTOM_DATA_SOURCE>) +add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>:FEATURE_METADATA_DEBUGGEE_DATA_SOURCE>) +add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>:FEATURE_METADATA_LOAD_TRUSTED_IMAGES>) +add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>:FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN>) +add_compile_definitions($<$<NOT:$<OR:$<BOOL:$<TARGET_PROPERTY:DAC_COMPONENT>>,$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>>>:FEATURE_METADATA_VERIFY_LAYOUTS>) + +if (CLR_CMAKE_HOST_WIN32) + set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$<AND:$<BOOL:$<TARGET_PROPERTY:DBI_COMPONENT>>,$<CONFIG:DEBUG>>:Debug>) +endif() + +add_subdirectory(compiler) +add_subdirectory(runtime) +add_subdirectory(enc) +add_subdirectory(hotdata) +add_subdirectory(ceefilegen) +add_subdirectory(datasource) +add_subdirectory(staticmd) diff --git a/src/coreclr/md/ceefilegen/CMakeLists.txt b/src/coreclr/md/ceefilegen/CMakeLists.txt new file mode 100644 index 00000000000..aaae8888908 --- /dev/null +++ b/src/coreclr/md/ceefilegen/CMakeLists.txt @@ -0,0 +1,35 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_definitions(-D__TODO_PORT_TO_WRAPPERS__) +include_directories("../inc") + +set(CEEFILEGEN_SOURCES + blobfetcher.cpp + cceegen.cpp + ceegentokenmapper.cpp + ceesectionstring.cpp + pesectionman.cpp +) + +set(CEEFILEGEN_HEADERS + ../../inc/corpriv.h + ../../inc/blobfetcher.h + ../../inc/ceegen.h + ../../inc/ceegentokenmapper.h + ../../inc/ceesectionstring.h + ../../inc/pesectionman.h + ../../inc/utilcode.h +) + +if (CLR_CMAKE_TARGET_WIN32) + list(APPEND CEEFILEGEN_SOURCES ${CEEFILEGEN_HEADERS}) +endif (CLR_CMAKE_TARGET_WIN32) + +add_library_clr(ceefgen_obj + OBJECT + ${CEEFILEGEN_SOURCES} +) +target_precompile_headers(ceefgen_obj PRIVATE stdafx.h) + +add_library(ceefgen INTERFACE) +target_sources(ceefgen INTERFACE $<TARGET_OBJECTS:ceefgen_obj>) diff --git a/src/coreclr/md/ceefilegen/blobfetcher.cpp b/src/coreclr/md/ceefilegen/blobfetcher.cpp new file mode 100644 index 00000000000..a4c21029ea0 --- /dev/null +++ b/src/coreclr/md/ceefilegen/blobfetcher.cpp @@ -0,0 +1,398 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// Implementation for CBlobFetcher +// + +// +// +//***************************************************************************** +#include "stdafx.h" // for ASSERTE and friends +#include "blobfetcher.h" +#include "log.h" + +//----------------------------------------------------------------------------- +// round up to a certain alignment +static inline unsigned roundUp(unsigned val, unsigned align) { + _ASSERTE((align & (align - 1)) == 0); // align must be a power of 2 + + return((val + (align-1)) & ~(align-1)); +} + +//----------------------------------------------------------------------------- +// round up to a certain alignment +static inline unsigned padForAlign(unsigned val, unsigned align) { + _ASSERTE((align & (align - 1)) == 0); // align must be a power of 2 + return ((-int(val)) & (align-1)); +} + +//***************************************************************************** +// Pillar implementation +//***************************************************************************** +//----------------------------------------------------------------------------- +CBlobFetcher::CPillar::CPillar() +{ + m_dataAlloc = NULL; + m_dataStart = NULL; + m_dataCur = NULL; + m_dataEnd = NULL; + + // Default initial size is 4K bytes. + m_nTargetSize = 0x1000; +} + +//----------------------------------------------------------------------------- +CBlobFetcher::CPillar::~CPillar() { +// Sanity check to make sure nobody messed up the pts + _ASSERTE((m_dataCur >= m_dataStart) && (m_dataCur <= m_dataEnd)); + + delete [] m_dataAlloc; +} + + +//----------------------------------------------------------------------------- +// Transfer ownership of data, so src will lose data and this will get it. +// Data itself will remain untouched, just ptrs & ownership change +//----------------------------------------------------------------------------- +void CBlobFetcher::CPillar::StealDataFrom(CBlobFetcher::CPillar & src) +{ +// We should only be moving into an empty Pillar + _ASSERTE(m_dataStart == NULL); + + + m_dataAlloc = src.m_dataAlloc; + m_dataStart = src.m_dataStart; + m_dataCur = src.m_dataCur; + m_dataEnd = src.m_dataEnd; + + m_nTargetSize = src.m_nTargetSize; + +// Take away src's claim to data. This prevents multiple ownership and double deleting + src.m_dataAlloc = src.m_dataStart = src.m_dataCur = src.m_dataEnd = NULL; + +} + +//----------------------------------------------------------------------------- +// Allocate a block in this particular pillar +//----------------------------------------------------------------------------- +/* make a new block 'len' bytes long' However, move the pointer 'pad' bytes + over so that the memory has the correct alignment characteristics. + + If the return value is NULL, there are two possibilities: + - This CPillar reserved less memory than needed for the current allocation. + - We are out-of-memory. In this case, CPillar:GetDataLen() will be 0. + */ + +char * CBlobFetcher::CPillar::MakeNewBlock(unsigned len, unsigned pad) { + + _ASSERTE(pad < maxAlign); + + // Make sure we have memory in this block to allocate + if (m_dataStart == NULL) { + + // make sure allocate at least as big as length + unsigned nNewTargetSize = max(m_nTargetSize, len); + + // + // We need to allocate memory with an offset of "pad" from + // being "maxAlign" aligned. (data % maxAlign == pad). + // Since "new" doesn't do this, allocate some extra + // to handle the worst possible alignment case. + // + unsigned allocationSize = nNewTargetSize + (maxAlign-1); + // Check for integer overflow + if (allocationSize < nNewTargetSize) + { // Integer overflow happened, fail the allocation + return NULL; + } + + m_dataAlloc = new (nothrow) char[allocationSize]; + + if (m_dataAlloc == NULL) + return NULL; + + // Ensure that no uninitialized values are placed into the pe file. + // While most of the logic carefully memset's appropriate pad bytes to 0, at least + // one place has been found where that wasn't true. + memset(m_dataAlloc, 0, allocationSize); + + m_nTargetSize = nNewTargetSize; + + m_dataStart = m_dataAlloc + + ((pad - (UINT_PTR)(m_dataAlloc)) & (((UINT_PTR)maxAlign)-1)); + + _ASSERTE((UINT_PTR)(m_dataStart) % maxAlign == pad); + + m_dataCur = m_dataStart; + + m_dataEnd = &m_dataStart[m_nTargetSize]; + } + + _ASSERTE(m_dataCur >= m_dataStart); + _ASSERTE((int) len > 0); + + // If this block is full, then get out, we'll have to try another block + if (m_dataCur + len > m_dataEnd) { + return NULL; + } + + char* ret = m_dataCur; + m_dataCur += len; + _ASSERTE(m_dataCur <= m_dataEnd); + return(ret); +} + + +//***************************************************************************** +// Blob Fetcher Implementation +//***************************************************************************** + +//----------------------------------------------------------------------------- +CBlobFetcher::CBlobFetcher() +{ + // Setup storage + m_pIndex = NULL; + m_nIndexMax = 1; // start off with arbitrary small size @@@ (minimum is 1) + m_nIndexUsed = 0; + _ASSERTE(m_nIndexUsed < m_nIndexMax); // use <, not <= + + m_nDataLen = 0; + + m_pIndex = new CPillar[m_nIndexMax]; + _ASSERTE(m_pIndex); + //<TODO>@FUTURE: what do we do here if we run out of memory??!!</TODO> +} + +//----------------------------------------------------------------------------- +CBlobFetcher::~CBlobFetcher() +{ + delete [] m_pIndex; +} + + +//----------------------------------------------------------------------------- +// Dynamic mem allocation, but we can't move old blocks (since others +// have pointers to them), so we need a fancy way to grow +// Returns NULL if the memory could not be allocated. +//----------------------------------------------------------------------------- +char* CBlobFetcher::MakeNewBlock(unsigned len, unsigned align) { + + _ASSERTE(m_pIndex); + _ASSERTE(0 < align && align <= maxAlign); + + // deal with alignment + unsigned pad = padForAlign(m_nDataLen, align); + char* pChRet = NULL; + if (pad != 0) { + pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(pad, 0); + + // Did we run out of memory? + if (pChRet == NULL && m_pIndex[m_nIndexUsed].GetDataLen() == 0) + return NULL; + + // if don't have space for the pad, then need to allocate a new pillar + // the allocation will handle the padding for the alignment of m_nDataLen + if (pChRet) { + memset(pChRet, 0, pad); + m_nDataLen += pad; + pad = 0; + } + } +#ifdef _DEBUG + if (pChRet) + _ASSERTE((m_nDataLen % align) == 0); +#endif + + // Quickly computing total data length is tough since we have alignment problems + // We'll do it by getting the length of all the completely full pillars so far + // and then adding on the size of the current pillar + unsigned nPreDataLen = m_nDataLen - m_pIndex[m_nIndexUsed].GetDataLen(); + + pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(len + pad, 0); + + // Did we run out of memory? + if (pChRet == NULL && m_pIndex[m_nIndexUsed].GetDataLen() == NULL) + return NULL; + + if (pChRet == NULL) { + + nPreDataLen = m_nDataLen; + + if (m_nIndexUsed + 1 == m_nIndexMax) { + // entire array of pillars are full, re-org + + const unsigned nNewMax = m_nIndexMax * 2; // arbitrary new size + + CPillar* pNewIndex = new (nothrow) CPillar[nNewMax]; + if (pNewIndex == NULL) + return NULL; + + // Copy old stuff + for(unsigned i = 0; i < m_nIndexMax; i++) + pNewIndex[i].StealDataFrom(m_pIndex[i]); + + delete [] m_pIndex; + + m_nIndexMax = nNewMax; + m_pIndex = pNewIndex; + + STRESS_LOG2(LF_LOADER, LL_INFO10, "CBlobFetcher %08X reallocates m_pIndex %08X\n", this, m_pIndex); + } + + m_nIndexUsed ++; // current pillar is full, move to next + + // Make sure the new pillar is large enough to hold the data + // How we do this is *totally arbitrary* and has been optimized for how + // we intend to use this. + + unsigned minSizeOfNewPillar = (3 * m_nDataLen) / 2; + if (minSizeOfNewPillar < len) + minSizeOfNewPillar = len; + + if (m_pIndex[m_nIndexUsed].GetAllocateSize() < minSizeOfNewPillar) { + m_pIndex[m_nIndexUsed].SetAllocateSize(roundUp(minSizeOfNewPillar, maxAlign)); + } + + // Under stress, we have seen that m_pIndex[0] is empty, but + // m_pIndex[1] is not. This assert tries to catch that scenario. + _ASSERTE(m_pIndex[0].GetDataLen() != 0); + + // Now that we're on new pillar, try again + pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(len + pad, m_nDataLen % maxAlign); + if (pChRet == NULL) + return NULL; + _ASSERTE(pChRet); + + // The current pointer picks up at the same alignment that the last block left off + _ASSERTE(nPreDataLen % maxAlign == ((UINT_PTR) pChRet) % maxAlign); + } + + if (pad != 0) { + memset(pChRet, 0, pad); + pChRet += pad; + } + + m_nDataLen = nPreDataLen + m_pIndex[m_nIndexUsed].GetDataLen(); + + _ASSERTE(((unsigned) m_nDataLen - len) % align == 0); + _ASSERTE((UINT_PTR(pChRet) % align) == 0); + return pChRet; +} + +//----------------------------------------------------------------------------- +// Index segment as if this were linear (middle weight function) +//----------------------------------------------------------------------------- +char * CBlobFetcher::ComputePointer(unsigned offset) const +{ + _ASSERTE(m_pIndex); + unsigned idx = 0; + + if (offset == 0) { + // if ask for a 0 offset and no data, return NULL + if (m_pIndex[0].GetDataLen() == 0) + { + return NULL; + } + } + else + { + while (offset >= m_pIndex[idx].GetDataLen()) { + offset -= m_pIndex[idx].GetDataLen(); + idx ++; + // Overflow - have asked for an offset greater than what exists + if (idx > m_nIndexUsed) { + _ASSERTE(!"CBlobFetcher::ComputePointer() Overflow"); + return NULL; + } + } + } + + char * ptr = (char*) (m_pIndex[idx].GetRawDataStart() + offset); + return ptr; +} + +//----------------------------------------------------------------------------- +// See if a pointer came from this blob fetcher +//----------------------------------------------------------------------------- +BOOL CBlobFetcher::ContainsPointer( __in char *ptr) const +{ + _ASSERTE(m_pIndex); + + CPillar *p = m_pIndex; + CPillar *pEnd = p + m_nIndexUsed; + + unsigned offset = 0; + + while (p <= pEnd) { + if (p->Contains(ptr)) + return TRUE; + + offset += p->GetDataLen(); + p++; + } + + return FALSE; +} + +//----------------------------------------------------------------------------- +// Find a pointer as if this were linear (middle weight function) +//----------------------------------------------------------------------------- +unsigned CBlobFetcher::ComputeOffset(__in char *ptr) const +{ + _ASSERTE(m_pIndex); + + CPillar *p = m_pIndex; + CPillar *pEnd = p + m_nIndexUsed; + + unsigned offset = 0; + + while (p <= pEnd) { + if (p->Contains(ptr)) + return offset + p->GetOffset(ptr); + + offset += p->GetDataLen(); + p++; + } + + _ASSERTE(!"Pointer not found"); + return 0; +} + + +//Take the data from our previous blob and copy it into our new blob +//after whatever was already in that blob. +HRESULT CBlobFetcher::Merge(CBlobFetcher *destination) { + unsigned dataLen; + char *dataBlock; + char *dataCurr; + unsigned idx; + _ASSERTE(destination); + + dataLen = GetDataLen(); + _ASSERTE( dataLen >= 0 ); + + // Make sure there actually is data in the previous blob before trying to append it. + if ( 0 == dataLen ) + { + return S_OK; + } + + //Get the length of our data and get a new block large enough to hold all of it. + dataBlock = destination->MakeNewBlock(dataLen, 1); + if (dataBlock == NULL) { + return E_OUTOFMEMORY; + } + + //Copy all of the bytes using the write algorithm from PEWriter.cpp + dataCurr=dataBlock; + for (idx=0; idx<=m_nIndexUsed; idx++) { + if (m_pIndex[idx].GetDataLen()>0) { + _ASSERTE(dataCurr<dataBlock+dataLen); + memcpy(dataCurr, m_pIndex[idx].GetRawDataStart(), m_pIndex[idx].GetDataLen()); + dataCurr+=m_pIndex[idx].GetDataLen(); + } + } + + return S_OK; + +} diff --git a/src/coreclr/md/ceefilegen/cceegen.cpp b/src/coreclr/md/ceefilegen/cceegen.cpp new file mode 100644 index 00000000000..9be6c99ff9b --- /dev/null +++ b/src/coreclr/md/ceefilegen/cceegen.cpp @@ -0,0 +1,715 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// + + +#include "stdafx.h" + +#include "corerror.h" + +#ifdef EnC_SUPPORTED +#define ENC_DELTA_HACK +#endif + + +//***************************************************************************** +// Creation for new CCeeGen instances +// +// Both allocate and call virtual Init() (Can't call v-func in a ctor, +// but we want to create in 1 call); +//***************************************************************************** + +HRESULT STDMETHODCALLTYPE CreateICeeGen(REFIID riid, void **pCeeGen) +{ + if (riid != IID_ICeeGen) + return E_NOTIMPL; + if (!pCeeGen) + return E_POINTER; + CCeeGen *pCeeFileGen; + HRESULT hr = CCeeGen::CreateNewInstance(pCeeFileGen); + if (FAILED(hr)) + return hr; + pCeeFileGen->AddRef(); + *(CCeeGen**)pCeeGen = pCeeFileGen; + return S_OK; +} + +HRESULT CCeeGen::CreateNewInstance(CCeeGen* & pGen) // static, public +{ + NewHolder<CCeeGen> pGenHolder(new CCeeGen()); + _ASSERTE(pGenHolder != NULL); + TESTANDRETURNMEMORY(pGenHolder); + + pGenHolder->m_peSectionMan = new PESectionMan; + _ASSERTE(pGenHolder->m_peSectionMan != NULL); + TESTANDRETURNMEMORY(pGenHolder->m_peSectionMan); + + HRESULT hr = pGenHolder->m_peSectionMan->Init(); + if (FAILED(hr)) + { + pGenHolder->Cleanup(); + return hr; + } + + hr = pGenHolder->Init(); + if (FAILED(hr)) + { + // Init() calls Cleanup() on failure + return hr; + } + + pGen = pGenHolder.Extract(); + return hr; +} + +STDMETHODIMP CCeeGen::QueryInterface(REFIID riid, void** ppv) +{ + if (!ppv) + return E_POINTER; + + *ppv = NULL; + + if (riid == IID_IUnknown) + *ppv = (IUnknown*)(ICeeGen*)this; + else if (riid == IID_ICeeGen) + *ppv = (ICeeGen*)this; + else if (riid == IID_ICeeGenInternal) + *ppv = (ICeeGenInternal*)this; + if (*ppv == NULL) + return E_NOINTERFACE; + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CCeeGen::AddRef(void) +{ + return InterlockedIncrement(&m_cRefs); +} + +STDMETHODIMP_(ULONG) CCeeGen::Release(void) +{ + if (InterlockedDecrement(&m_cRefs) == 0) { + Cleanup(); + delete this; + return 0; + } + return 1; +} + +STDMETHODIMP CCeeGen::SetInitialGrowth(DWORD growth) +{ + getIlSection().SetInitialGrowth(growth); + + return S_OK; +} + +STDMETHODIMP CCeeGen::EmitString (__in LPWSTR lpString, ULONG *RVA) +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + if (! RVA) + IfFailGo(E_POINTER); + hr = getStringSection().getEmittedStringRef(lpString, RVA); +ErrExit: + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +STDMETHODIMP CCeeGen::GetString(ULONG RVA, __inout LPWSTR *lpString) +{ + HRESULT hr = E_FAIL; + BEGIN_ENTRYPOINT_NOTHROW; + + if (! lpString) + IfFailGo(E_POINTER); + *lpString = (LPWSTR)getStringSection().computePointer(RVA); + + +ErrExit: + + END_ENTRYPOINT_NOTHROW; + if (*lpString) + return S_OK; + return hr; +} + +STDMETHODIMP CCeeGen::AllocateMethodBuffer(ULONG cchBuffer, UCHAR **lpBuffer, ULONG *RVA) +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + ULONG methodOffset = 0; + + if (! cchBuffer) + IfFailGo(E_INVALIDARG); + if (! lpBuffer || ! RVA) + IfFailGo(E_POINTER); + *lpBuffer = (UCHAR*) getIlSection().getBlock(cchBuffer, 4); // Dword align + IfNullGo(*lpBuffer); + + // have to compute the method offset after getting the block, not + // before (since alignment might shift it up + // for in-memory, just return address and will calc later + methodOffset = getIlSection().dataLen() - cchBuffer; + + *RVA = methodOffset; + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +STDMETHODIMP CCeeGen::GetMethodBuffer(ULONG RVA, UCHAR **lpBuffer) +{ + HRESULT hr = E_FAIL; + BEGIN_ENTRYPOINT_NOTHROW; + + if (! lpBuffer) + IfFailGo(E_POINTER); + *lpBuffer = (UCHAR*)getIlSection().computePointer(RVA); + + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + if (lpBuffer != NULL && *lpBuffer != 0) + return S_OK; + + return hr; +} + +STDMETHODIMP CCeeGen::ComputePointer(HCEESECTION section, ULONG RVA, UCHAR **lpBuffer) +{ + HRESULT hr = E_FAIL; + BEGIN_ENTRYPOINT_NOTHROW; + + if (! lpBuffer) + IfFailGo(E_POINTER); + *lpBuffer = (UCHAR*) ((CeeSection *)section)->computePointer(RVA); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + if (lpBuffer != NULL && *lpBuffer != 0) + return S_OK; + return hr; +} + +STDMETHODIMP CCeeGen::GetIMapTokenIface ( + IUnknown **pIMapToken) +{ + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(!"E_NOTIMPL"); + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} + +STDMETHODIMP CCeeGen::AddNotificationHandler ( + IUnknown *pHandler) +{ + BEGIN_ENTRYPOINT_NOTHROW; + _ASSERTE(!"E_NOTIMPL"); + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} + +STDMETHODIMP CCeeGen::GenerateCeeFile () +{ + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(!"E_NOTIMPL"); + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} + +STDMETHODIMP CCeeGen::GenerateCeeMemoryImage (void **) +{ + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(!"E_NOTIMPL"); + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} + +STDMETHODIMP CCeeGen::GetIlSection ( + HCEESECTION *section) +{ + BEGIN_ENTRYPOINT_NOTHROW; + + *section = (HCEESECTION)(m_sections[m_ilIdx]); + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} + +STDMETHODIMP CCeeGen::GetStringSection(HCEESECTION *section) +{ + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(!"E_NOTIMPL"); + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} + +STDMETHODIMP CCeeGen::AddSectionReloc ( + HCEESECTION section, + ULONG offset, + HCEESECTION relativeTo, + CeeSectionRelocType relocType) +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = m_sections[m_ilIdx]->addSectReloc(offset, *(m_sections[m_ilIdx]), relocType); + END_ENTRYPOINT_NOTHROW; + return hr; +} + +STDMETHODIMP CCeeGen::GetSectionCreate ( + const char *name, + DWORD flags, + HCEESECTION *section) +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + short sectionIdx; + hr = getSectionCreate (name, flags, (CeeSection **)section, §ionIdx); + END_ENTRYPOINT_NOTHROW; + return hr; +} + +STDMETHODIMP CCeeGen::GetSectionDataLen ( + HCEESECTION section, + ULONG *dataLen) +{ + BEGIN_ENTRYPOINT_NOTHROW; + + CeeSection *pSection = (CeeSection*) section; + *dataLen = pSection->dataLen(); + END_ENTRYPOINT_NOTHROW; + + return NOERROR; +} + +STDMETHODIMP CCeeGen::GetSectionBlock ( + HCEESECTION section, + ULONG len, + ULONG align, + void **ppBytes) +{ + BEGIN_ENTRYPOINT_NOTHROW; + + CeeSection *pSection = (CeeSection*) section; + *ppBytes = (BYTE *)pSection->getBlock(len, align); + END_ENTRYPOINT_NOTHROW; + + if (*ppBytes == 0) + return E_OUTOFMEMORY; + return NOERROR; +} + +STDMETHODIMP CCeeGen::TruncateSection ( + HCEESECTION section, + ULONG len) +{ + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(!"E_NOTIMPL"); + END_ENTRYPOINT_NOTHROW; + return E_NOTIMPL; +} + + + +CCeeGen::CCeeGen() // protected ctor +{ +// All other init done in InitCommon() + m_cRefs = 0; + m_peSectionMan = NULL; + m_pTokenMap = NULL; + m_pRemapHandler = NULL; + +} + +// Shared init code between derived classes, called by virtual Init() +HRESULT CCeeGen::Init() // not-virtual, protected +{ +// Public, Virtual init must create our SectionManager, and +// Common init does the rest + _ASSERTE(m_peSectionMan != NULL); + + HRESULT hr = S_OK; + + PESection *section = NULL; + CeeSection *ceeSection = NULL; + + m_corHeader = NULL; + + m_numSections = 0; + m_allocSections = 10; + m_sections = new CeeSection * [ m_allocSections ]; + if (m_sections == NULL) { + hr = E_OUTOFMEMORY; + goto LExit; + } + + m_pTokenMap = NULL; + m_fTokenMapSupported = FALSE; + m_pRemapHandler = NULL; + + // These text section needs special support for handling string management now that we have + // merged the sections together, so create it with an underlying CeeSectionString rather than the + // more generic CeeSection + + hr = m_peSectionMan->getSectionCreate(".text", sdExecute, §ion); + if (FAILED(hr)) { + goto LExit; + } + + ceeSection = new CeeSectionString(*this, *section); + if (ceeSection == NULL) { + hr = E_OUTOFMEMORY; + goto LExit; + } + + hr = addSection(ceeSection, &m_stringIdx); + + m_textIdx = m_stringIdx; + + m_metaIdx = m_textIdx; // meta section is actually in .text + m_ilIdx = m_textIdx; // il section is actually in .text + m_corHdrIdx = -1; + m_encMode = FALSE; + +LExit: + if (FAILED(hr)) { + Cleanup(); + } + + return hr; +} + +// For EnC mode, generate strings into .rdata section rather than .text section +HRESULT CCeeGen::setEnCMode() +{ + PESection *section = NULL; + HRESULT hr = m_peSectionMan->getSectionCreate(".rdata", sdExecute, §ion); + TESTANDRETURNHR(hr); + CeeSection *ceeSection = new CeeSectionString(*this, *section); + if (ceeSection == NULL) + { + return E_OUTOFMEMORY; + } + hr = addSection(ceeSection, &m_stringIdx); + if (SUCCEEDED(hr)) + m_encMode = TRUE; + return hr; +} + + +HRESULT CCeeGen::cloneInstance(CCeeGen *destination) { //public, virtual + _ASSERTE(destination); + + destination->m_pTokenMap = m_pTokenMap; + destination->m_fTokenMapSupported = m_fTokenMapSupported; + destination->m_pRemapHandler = m_pRemapHandler; + + //Create a deep copy of the section manager (and each of it's sections); + return m_peSectionMan->cloneInstance(destination->m_peSectionMan); +} + +HRESULT CCeeGen::Cleanup() // virtual +{ + HRESULT hr; + for (int i = 0; i < m_numSections; i++) { + delete m_sections[i]; + } + + delete [] m_sections; + + CeeGenTokenMapper *pMapper = m_pTokenMap; + if (pMapper) { + if (pMapper->m_pIImport) { + IMetaDataEmit *pIIEmit; + if (SUCCEEDED( hr = pMapper->m_pIImport->QueryInterface(IID_IMetaDataEmit, (void **) &pIIEmit))) + { + pIIEmit->SetHandler(NULL); + pIIEmit->Release(); + } + _ASSERTE(SUCCEEDED(hr)); + pMapper->m_pIImport->Release(); + } + pMapper->Release(); + m_pTokenMap = NULL; + } + + if (m_pRemapHandler) + { + m_pRemapHandler->Release(); + m_pRemapHandler = NULL; + } + + if (m_peSectionMan) { + m_peSectionMan->Cleanup(); + delete m_peSectionMan; + } + + return S_OK; +} + +HRESULT CCeeGen::addSection(CeeSection *section, short *sectionIdx) +{ + if (m_numSections >= m_allocSections) + { + _ASSERTE(m_allocSections > 0); + while (m_numSections >= m_allocSections) + m_allocSections <<= 1; + CeeSection **newSections = new CeeSection * [m_allocSections]; + if (newSections == NULL) + return E_OUTOFMEMORY; + CopyMemory(newSections, m_sections, m_numSections * sizeof(*m_sections)); + if (m_sections != NULL) + delete [] m_sections; + m_sections = newSections; + } + + if (sectionIdx) + *sectionIdx = m_numSections; + + m_sections[m_numSections++] = section; + return S_OK; +} + +HRESULT CCeeGen::getSectionCreate (const char *name, DWORD flags, CeeSection **section, short *sectionIdx) +{ + if (strcmp(name, ".il") == 0) + name = ".text"; + else if (strcmp(name, ".meta") == 0) + name = ".text"; + else if (strcmp(name, ".rdata") == 0 && !m_encMode) + name = ".text"; + for (int i=0; i<m_numSections; i++) { + if (strcmp((const char *)m_sections[i]->name(), name) == 0) { + if (section) + *section = m_sections[i]; + if (sectionIdx) + *sectionIdx = i; + return S_OK; + } + } + PESection *pewSect = NULL; + HRESULT hr = m_peSectionMan->getSectionCreate(name, flags, &pewSect); + TESTANDRETURNHR(hr); + CeeSection *newSect = new CeeSection(*this, *pewSect); + // if this fails, the PESection will get zapped in the destructor for CCeeGen + if (newSect == NULL) + { + return E_OUTOFMEMORY; + } + + hr = addSection(newSect, sectionIdx); + TESTANDRETURNHR(hr); + if (section) + *section = newSect; + return S_OK; +} + + +HRESULT CCeeGen::emitMetaData(IMetaDataEmit *emitter, CeeSection* section, DWORD offset, BYTE* buffer, unsigned buffLen) +{ + HRESULT hr = S_OK; + + ReleaseHolder<IStream> metaStream(NULL); + + IfFailRet((HRESULT)CreateStreamOnHGlobal(NULL, TRUE, &metaStream)); + + if (! m_fTokenMapSupported) { + IUnknown *pMapTokenIface; + IfFailGoto(getMapTokenIface(&pMapTokenIface, emitter), Exit); + + // Set a callback for token remap and save the tokens which change. + IfFailGoto(emitter->SetHandler(pMapTokenIface), Exit); + } + + // generate the metadata + IfFailGoto(emitter->SaveToStream(metaStream, 0), Exit); + + // get size of stream and get sufficient storage for it + + if (section == 0) { + section = &getMetaSection(); + STATSTG statStg; + IfFailGoto((HRESULT)(metaStream->Stat(&statStg, STATFLAG_NONAME)), Exit); + + buffLen = statStg.cbSize.u.LowPart; + if(m_objSwitch) + { + CeeSection* pSect; + DWORD flags = IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE | IMAGE_SCN_ALIGN_1BYTES; // 0x00100A00 + IfFailGoto(getSectionCreate(".cormeta",flags,&pSect,&m_metaIdx), Exit); + } + buffer = (BYTE *)section->getBlock(buffLen, sizeof(DWORD)); + IfNullGoto(buffer, Exit); + offset = getMetaSection().dataLen() - buffLen; + } + else { + _ASSERTE(buffer[buffLen-1] || true); // Dereference 'buffer' + _ASSERTE(section->computeOffset(PCHAR(buffer)) == offset); + } + + // reset seek pointer and read from stream + { + LARGE_INTEGER disp = { {0, 0} }; + IfFailGoto((HRESULT)metaStream->Seek(disp, STREAM_SEEK_SET, NULL), Exit); + } + ULONG metaDataLen; + IfFailGoto((HRESULT)metaStream->Read(buffer, buffLen+1, &metaDataLen), Exit); + + _ASSERTE(metaDataLen <= buffLen); + +#ifdef ENC_DELTA_HACK + { + extern int __cdecl fclose(FILE *); + WCHAR szFileName[256]; + DWORD len = GetEnvironmentVariable(W("COMP_ENC_EMIT"), szFileName, ARRAYSIZE(szFileName)); + _ASSERTE(len < (ARRAYSIZE(szFileName) + 6)); // +6 for the .dmeta + if (len > 0 && len < (ARRAYSIZE(szFileName) + 6)) + { + wcscat_s(szFileName, ARRAYSIZE(szFileName), W(".dmeta")); + FILE *pDelta; + int ec = _wfopen_s(&pDelta, szFileName, W("wb")); + if (FAILED(ec)) { return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); } + fwrite(buffer, 1, metaDataLen, pDelta); + fclose(pDelta); + } + } +#endif + + + // Set meta virtual address to offset of metadata within .meta, and + // and add a reloc for this offset, which will get turned + // into an rva when the pewriter writes out the file. + + m_corHeader->MetaData.VirtualAddress = VAL32(offset); + getCorHeaderSection().addSectReloc(m_corHeaderOffset + offsetof(IMAGE_COR20_HEADER, MetaData), *section, srRelocAbsolute); + m_corHeader->MetaData.Size = VAL32(metaDataLen); + +Exit: + + if (! m_fTokenMapSupported) { + // Remove the handler that we set + hr = emitter->SetHandler(NULL); + } + +#ifdef _DEBUG + if (FAILED(hr) && hr != E_OUTOFMEMORY) + _ASSERTE(!"Unexpected Failure"); +#endif + + return hr; +} + +// Create the COM header - it goes at front of .meta section +// Need to do this before the meta data is copied in, but don't do at +// the same time because may not have metadata +HRESULT CCeeGen::allocateCorHeader() +{ + HRESULT hr = S_OK; + CeeSection *corHeaderSection = NULL; + if (m_corHdrIdx < 0) { + hr = getSectionCreate(".text0", sdExecute, &corHeaderSection, &m_corHdrIdx); + TESTANDRETURNHR(hr); + + m_corHeaderOffset = corHeaderSection->dataLen(); + m_corHeader = (IMAGE_COR20_HEADER*)corHeaderSection->getBlock(sizeof(IMAGE_COR20_HEADER)); + if (! m_corHeader) + return E_OUTOFMEMORY; + memset(m_corHeader, 0, sizeof(IMAGE_COR20_HEADER)); + } + return S_OK; +} + +HRESULT CCeeGen::getMethodRVA(ULONG codeOffset, ULONG *codeRVA) +{ + _ASSERTE(codeRVA); + // for runtime conversion, just return the offset and will calculate real address when need the code + *codeRVA = codeOffset; + return S_OK; +} + +HRESULT CCeeGen::getMapTokenIface(IUnknown **pIMapToken, IMetaDataEmit *emitter) +{ + if (! pIMapToken) + return E_POINTER; + if (! m_pTokenMap) { + // Allocate the token mapper. As code is generated, each moved token will be added to + // the mapper and the client will also add a TokenMap reloc for it so we can update later + CeeGenTokenMapper *pMapper = new CeeGenTokenMapper; + if (pMapper == NULL) + { + return E_OUTOFMEMORY; + } + + if (emitter) { + HRESULT hr; + hr = emitter->QueryInterface(IID_IMetaDataImport, (PVOID *) &pMapper->m_pIImport); + _ASSERTE(SUCCEEDED(hr)); + } + m_pTokenMap = pMapper; + m_fTokenMapSupported = (emitter == 0); + + // If we've been holding onto a token remap handler waiting + // for the token mapper to get created, add it to the token + // mapper now and release our hold on it. + if (m_pRemapHandler && m_pTokenMap) + { + m_pTokenMap->AddTokenMapper(m_pRemapHandler); + m_pRemapHandler->Release(); + m_pRemapHandler = NULL; + } + } + *pIMapToken = getTokenMapper()->GetMapTokenIface(); + return S_OK; +} + +HRESULT CCeeGen::addNotificationHandler(IUnknown *pHandler) +{ + // Null is no good... + if (!pHandler) + return E_POINTER; + + HRESULT hr = S_OK; + IMapToken *pIMapToken = NULL; + + // Is this an IMapToken? If so, we can put it to good use... + if (SUCCEEDED(pHandler->QueryInterface(IID_IMapToken, + (void**)&pIMapToken))) + { + // You gotta have a token mapper to use an IMapToken, though. + if (m_pTokenMap) + { + hr = m_pTokenMap->AddTokenMapper(pIMapToken); + pIMapToken->Release(); + } + else + { + // Hold onto it for later, just in case a token mapper + // gets created. We're holding a reference to it here, + // too. + m_pRemapHandler = pIMapToken; + } + } + + return hr; +} diff --git a/src/coreclr/md/ceefilegen/ceegentokenmapper.cpp b/src/coreclr/md/ceefilegen/ceegentokenmapper.cpp new file mode 100644 index 00000000000..c94597c68f8 --- /dev/null +++ b/src/coreclr/md/ceefilegen/ceegentokenmapper.cpp @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// CeeGenTokenMapper.cpp +// + +// +// This helper class tracks mapped tokens from their old value to the new value +// which can happen when the data is optimized on save. +// +//***************************************************************************** +#include "stdafx.h" +#include "ceegentokenmapper.h" + + +//***************************************************************************** +// At this point, only a select set of token values are stored for remap. +// If others should become required, this needs to get updated. +//***************************************************************************** +int CeeGenTokenMapper::IndexForType(mdToken tk) +{ +#ifdef CEEGEN_TRACKED_TOKEN +#undef CEEGEN_TRACKED_TOKEN +#endif +#define CEEGEN_TRACKED_TOKEN(type) case INDEX_OF_TYPE(mdt ## type): return (tkix ## type); + + int iType = INDEX_OF_TYPE(TypeFromToken(tk)); + switch(iType) + { + CEEGEN_TRACKED_TOKENS() + } + + return (-1); +} + + +//***************************************************************************** +// Called by the meta data engine when a token is remapped to a new location. +// This value is recorded in the m_rgMap array based on type and rid of the +// from token value. +//***************************************************************************** +HRESULT STDMETHODCALLTYPE CeeGenTokenMapper::Map( + mdToken tkFrom, + mdToken tkTo) +{ + HRESULT hr = S_OK; + mdToken *pToken = NULL; + ULONG ridFrom = 0; + TOKENMAP *pMap = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + if ( IndexForType(tkFrom) == -1 ) + { + // It is a type that we are not tracking, such as mdtProperty or mdtEvent, + // just return S_OK. + goto ErrExit; + } + + _ASSERTE(IndexForType(tkFrom) < GetMaxMapSize()); + _ASSERTE(IndexForType(tkTo) != -1 && IndexForType(tkTo) < GetMaxMapSize()); + + // If there is another token mapper that the user wants called, go + // ahead and call it now. + if (m_pIMapToken) + m_pIMapToken->Map(tkFrom, tkTo); + + ridFrom = RidFromToken(tkFrom); + pMap = &m_rgMap[IndexForType(tkFrom)]; + + // If there isn't enough entries, fill out array up to the count + // and mark the token to nil so we know there is no valid data yet. + if ((ULONG) pMap->Count() <= ridFrom) + { + for (int i=ridFrom - pMap->Count() + 1; i; i--) + { + pToken = pMap->Append(); + if (!pToken) + break; + *pToken = mdTokenNil; + } + _ASSERTE(!pToken || pMap->Get(ridFrom) == pToken); + } + else + pToken = pMap->Get(ridFrom); + + IfNullGo(pToken); + + *pToken = tkTo; + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + + +//***************************************************************************** +// Check the given token to see if it has moved to a new location. If so, +// return true and give back the new token. +//***************************************************************************** +int CeeGenTokenMapper::HasTokenMoved( + mdToken tkFrom, + mdToken &tkTo) +{ + mdToken tk; + + int i = IndexForType(tkFrom); + if(i == -1) return false; + + _ASSERTE(i < GetMaxMapSize()); + TOKENMAP *pMap = &m_rgMap[i]; + + // Assume nothing moves. + tkTo = tkFrom; + + // If the array is smaller than the index, can't have moved. + if ((ULONG) pMap->Count() <= RidFromToken(tkFrom)) + return (false); + + // If the entry is set to 0, then nothing there. + tk = *pMap->Get(RidFromToken(tkFrom)); + if (tk == mdTokenNil) + return (false); + + // Had to move to a new location, return that new location. + tkTo = tk; + return (true); +} + + +//***************************************************************************** +// Hand out a copy of the meta data information. +//***************************************************************************** + +HRESULT CeeGenTokenMapper::GetMetaData( + IMetaDataImport **ppIImport) +{ + if (m_pIImport) + return (m_pIImport->QueryInterface(IID_IMetaDataImport, (PVOID *) ppIImport)); + *ppIImport = 0; + return E_FAIL; +} + + +HRESULT STDMETHODCALLTYPE CeeGenTokenMapper::QueryInterface(REFIID iid, PVOID *ppIUnk) +{ + if (iid == IID_IUnknown || iid == IID_IMapToken) + *ppIUnk = static_cast<IMapToken*>(this); + else + { + *ppIUnk = 0; + return (E_NOINTERFACE); + } + AddRef(); + return (S_OK); +} + + diff --git a/src/coreclr/md/ceefilegen/ceesectionstring.cpp b/src/coreclr/md/ceefilegen/ceesectionstring.cpp new file mode 100644 index 00000000000..2575d56d0c6 --- /dev/null +++ b/src/coreclr/md/ceefilegen/ceesectionstring.cpp @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// =========================================================================== +// File: CeeSectionString.cpp +// + +// +// =========================================================================== +#include "stdafx.h" + +struct StringTableEntry { + ULONG m_hashId; + int m_offset; + StringTableEntry *m_next; +}; + +CeeSectionString::CeeSectionString(CCeeGen &ceeFile, CeeSectionImpl &impl) + : CeeSection(ceeFile, impl) +{ + memset(stringTable, 0, sizeof(stringTable)); +} + +void CeeSectionString::deleteEntries(StringTableEntry *e) +{ + if (!e) + return; + deleteEntries(e->m_next); + delete e; +} + +#ifdef RDATA_STATS +int CeeSectionString::dumpEntries(StringTableEntry *e) +{ + if (!e) + return 0; + else { + printf(" HashId: %d, value: %S\n", e->m_hashId, computOffset(e->m_offset)); + return dumpEntries(e->m_next) + 1; + } +} + +void CeeSectionString::dumpTable() +{ + int sum = 0, count = 0; + for (int i=0; i < MaxRealEntries; i++) { + if (stringTable[i]) { + printf("Bucket %d\n", i); + printf("Total size: %d\n\n", + count = dumpEntries(stringTable[i])); + sum += count; + } + } + printf("Total number strings: %d\n\n", sum); +} +#endif + +CeeSectionString::~CeeSectionString() +{ +#ifdef RDATA_STATS + dumpTable(); +#endif + for (int i=0; i < MaxRealEntries; i++) + deleteEntries(stringTable[i]); +} + +StringTableEntry* CeeSectionString::createEntry(__in_z LPWSTR target, ULONG hashId) +{ + StringTableEntry *entry = new (nothrow) StringTableEntry; + if (!entry) + return NULL; + entry->m_next = NULL; + entry->m_hashId = hashId; + entry->m_offset = dataLen(); + size_t len = (wcslen(target)+1) * sizeof(WCHAR); + if (len > UINT32_MAX) { + delete entry; + return NULL; + } + void *buf = getBlock((ULONG)len); + if (!buf) { + delete entry; + return NULL; + } + memcpy(buf, target, len); + return entry; +} + +// Searches through the linked list looking for a match on hashID. If +// multiple elements hash to the same value, a strcmp must be done to +// check for match. The goal is to have very large hashId space so that +// string compares are minimized +StringTableEntry *CeeSectionString::findStringInsert( + StringTableEntry *&head, __in_z LPWSTR target, ULONG hashId) +{ + StringTableEntry *cur, *prev; + cur = prev = head; + while (cur && cur->m_hashId < hashId) { + prev = cur; + cur = cur->m_next; + } + while (cur && cur->m_hashId == hashId) { + if (wcscmp(target, (LPWSTR)(computePointer(cur->m_offset))) == 0) + return cur; + prev = cur; + cur = cur->m_next; + } + // didn't find in chain so insert at prev + StringTableEntry *entry = createEntry(target, hashId); + if (cur == head) { + head = entry; + entry->m_next = prev; + } else { + prev->m_next = entry; + entry->m_next = cur; + } + return entry; +} + +HRESULT CeeSectionString::getEmittedStringRef(__in_z LPWSTR target, StringRef *ref) +{ + TESTANDRETURN(ref!=NULL, E_POINTER); + ULONG hashId = HashString(target) % MaxVirtualEntries; + ULONG bucketIndex = hashId / MaxRealEntries; + + StringTableEntry *entry; + entry = findStringInsert(stringTable[bucketIndex], target, hashId); + + if (! entry) + return E_OUTOFMEMORY; + *ref = entry->m_offset; + return S_OK; +} diff --git a/src/coreclr/md/ceefilegen/pesectionman.cpp b/src/coreclr/md/ceefilegen/pesectionman.cpp new file mode 100644 index 00000000000..a75558be4d8 --- /dev/null +++ b/src/coreclr/md/ceefilegen/pesectionman.cpp @@ -0,0 +1,427 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// PESectionMan implementation +// + + +#include "stdafx.h" + +/*****************************************************************/ +HRESULT PESectionMan::Init() +{ + const int initNumSections = 16; + sectStart = new (nothrow) PESection*[initNumSections]; + if (!sectStart) + return E_OUTOFMEMORY; + sectCur = sectStart; + sectEnd = §Start[initNumSections]; + + return S_OK; +} + +/*****************************************************************/ +HRESULT PESectionMan::Cleanup() +{ + for (PESection** ptr = sectStart; ptr < sectCur; ptr++) + delete *ptr; + delete [] sectStart; + + return S_OK; +} + +/*****************************************************************/ +// <REVISIT_TODO>this class is located in it's own DLL (MsCorXvt.dll) +// Since DLL allocates, The DLL must delete; we can't simply delete from +// the client (This is a bug in VC, see knowledge base Q122675)</REVISIT_TODO> +void PESectionMan::sectionDestroy(PESection **section) +{ + // check if this section is referenced in other sections' relocs + for(PESection** ptr = sectStart; ptr < sectCur; ptr++) + { + if(ptr != section) + { + for(PESectionReloc* cur = (*ptr)->m_relocStart; cur < (*ptr)->m_relocCur; cur++) + { + if(cur->section == *section) // here it is! Delete the reference + { + for(PESectionReloc* tmp = cur; tmp < (*ptr)->m_relocCur; tmp++) + { + memcpy(tmp,(tmp+1),sizeof(PESectionReloc)); + } + (*ptr)->m_relocCur--; + cur--; // no position shift this time + } + } + } + } + delete *section; + *section = NULL; +} +/*****************************************************************/ + +/******************************************************************/ +// Apply the relocs for all the sections +// Called by: ClassConverter after loading up during an in-memory conversion, + +HRESULT PESectionMan::applyRelocs(CeeGenTokenMapper *pTokenMapper) +{ + HRESULT hr; + + // Cycle through each of the sections + for(PESection ** ppCurSection = sectStart; ppCurSection < sectCur; ppCurSection++) { + IfFailRet((*ppCurSection)->applyRelocs(pTokenMapper)); + } // End sections + return S_OK; +} + + +/*****************************************************************/ +PESection* PESectionMan::getSection(const char* name) +{ + int len = (int)strlen(name); + + // the section name can be at most 8 characters including the null. + if (len < 8) + len++; + else + len = 8; + + // dbPrintf(("looking for section %s\n", name)); + for(PESection** cur = sectStart; cur < sectCur; cur++) { + // dbPrintf(("searching section %s\n", (*cur)->m_ame)); + if (strncmp((*cur)->m_name, name, len) == 0) { + // dbPrintf(("found section %s\n", (*cur)->m_name)); + return(*cur); + } + } + return(0); +} + +/******************************************************************/ +HRESULT PESectionMan::getSectionCreate(const char* name, unsigned flags, + PESection **section) +{ + PESection* ret = getSection(name); + + // If there is an existing section with the given name, return that + if (ret != NULL) { + *section = ret; + return(S_OK); + } + + // Check if there is space for a new section + if (sectCur >= sectEnd) { + unsigned curLen = (unsigned)(sectCur-sectStart); + unsigned newLen = (curLen * 2) + 1; + PESection** sectNew = new (nothrow) PESection*[newLen]; + if (sectNew == NULL) + { + return E_OUTOFMEMORY; + } + memcpy(sectNew, sectStart, sizeof(PESection*)*curLen); + delete [] sectStart; + sectStart = sectNew; + sectCur = §Start[curLen]; + sectEnd = §Start[newLen]; + } + + HRESULT hr; + IfFailRet(newSection(name, &ret, flags)); + + // dbPrintf(("MAKING NEW %s SECTION data starts at 0x%x\n", name, ret->dataStart)); + *sectCur++ = ret; + _ASSERTE(sectCur <= sectEnd); + *section = ret; + return(S_OK); +} + +/******************************************************************/ +HRESULT PESectionMan::newSection(const char* name, PESection **section, + unsigned flags, unsigned estSize, unsigned estRelocs) +{ + PESection * ret = new (nothrow) PESection(name, flags, estSize, estRelocs); + if (ret == NULL) + { + return E_OUTOFMEMORY; + } + *section = ret; + return S_OK; +} + +//Clone each of our sections. This will cause a deep copy of the sections +HRESULT PESectionMan::cloneInstance(PESectionMan *destination) { + _ASSERTE(destination); + PESection *pSection; + PESection **destPtr; + HRESULT hr = NOERROR; + + //Copy each of the sections + for (PESection** ptr = sectStart; ptr < sectCur; ptr++) { + destPtr = destination->sectStart; + pSection = NULL; + + // try to find the matching section by name + for (; destPtr < destination->sectCur; destPtr++) + { + if (strcmp((*destPtr)->m_name, (*ptr)->m_name) == 0) + { + pSection = *destPtr; + break; + } + } + if (destPtr >= destination->sectCur) + { + // cannot find a section in the destination with matching name + // so create one! + IfFailRet( destination->getSectionCreate((*ptr)->m_name, + (*ptr)->flags(), + &pSection) ); + } + if (pSection) + IfFailRet( (*ptr)->cloneInstance(pSection) ); + } + + //destination->sectEnd=destination->sectStart + (sectEnd-sectStart); + return S_OK; +} + + +//***************************************************************************** +// Implementation for PESection +//***************************************************************************** +PESection::PESection(const char *name, unsigned flags, + unsigned estSize, unsigned estRelocs) +{ + dirEntry = -1; + + // No init needed for CBlobFectcher m_pIndex + + m_relocStart = new (nothrow) PESectionReloc[estRelocs]; + if (m_relocStart == NULL) + { + // Can't report an error out of here - just initialize + // as if estRelocs was 0 (all three m_reloc pointers will be NULL). + // We'll lazily grow as needed. + estRelocs = 0; + } + m_relocCur = m_relocStart; + m_relocEnd = &m_relocStart[estRelocs]; + m_header = NULL; + m_baseRVA = 0; + m_filePos = 0; + m_filePad = 0; + m_flags = flags; + + _ASSERTE(strlen(name)<sizeof(m_name)); + strncpy_s(m_name, sizeof(m_name), name, strlen(name)); +} + + +/******************************************************************/ +PESection::~PESection() { + delete [] m_relocStart; +} + + +/******************************************************************/ +void PESection::writeSectReloc(unsigned val, CeeSection& relativeTo, CeeSectionRelocType reloc, CeeSectionRelocExtra *extra) +{ + addSectReloc(dataLen(), relativeTo, reloc, extra); + unsigned* ptr = (unsigned*) getBlock(4); + *ptr = val; +} + +/******************************************************************/ +HRESULT PESection::addSectReloc(unsigned offset, CeeSection& relativeToIn, + CeeSectionRelocType reloc, CeeSectionRelocExtra *extra) +{ + return addSectReloc(offset, + (PESection *)&relativeToIn.getImpl(), reloc, extra); +} + +/******************************************************************/ +HRESULT PESection::addSectReloc(unsigned offset, PESection *relativeTo, + CeeSectionRelocType reloc, CeeSectionRelocExtra *extra) +{ + /* dbPrintf(("******** GOT a section reloc for section %s offset 0x%x to section %x offset 0x%x\n", + header->m_name, offset, relativeTo->m_name, *((unsigned*) dataStart + offset))); */ + _ASSERTE(offset < dataLen()); + + if (m_relocCur >= m_relocEnd) { + unsigned curLen = (unsigned)(m_relocCur-m_relocStart); + unsigned newLen = curLen * 2 + 1; + PESectionReloc* relocNew = new (nothrow) PESectionReloc[newLen]; + if (relocNew == NULL) + { + return E_OUTOFMEMORY; + } + + memcpy(relocNew, m_relocStart, sizeof(PESectionReloc)*curLen); + delete m_relocStart; + m_relocStart = relocNew; + m_relocCur = &m_relocStart[curLen]; + m_relocEnd = &m_relocStart[newLen]; + } + + m_relocCur->type = reloc; + m_relocCur->offset = offset; + m_relocCur->section = relativeTo; + if (extra) + m_relocCur->extra = *extra; + m_relocCur++; + assert(m_relocCur <= m_relocEnd); + return S_OK; +} + +/******************************************************************/ +// Compute a pointer (wrap blobfetcher) +char * PESection::computePointer(unsigned offset) const // virtual +{ + return m_blobFetcher.ComputePointer(offset); +} + +/******************************************************************/ +BOOL PESection::containsPointer(__in char *ptr) const // virtual +{ + return m_blobFetcher.ContainsPointer(ptr); +} + +/******************************************************************/ +// Compute an offset (wrap blobfetcher) +unsigned PESection::computeOffset(__in char *ptr) const // virtual +{ + return m_blobFetcher.ComputeOffset(ptr); +} + + +/******************************************************************/ +HRESULT PESection::addBaseReloc(unsigned offset, CeeSectionRelocType reloc, + CeeSectionRelocExtra *extra) +{ + HRESULT hr = E_FAIL; + + // Use for fixing up pointers pointing outside of the module. + // + // We only record base relocs for cross module pc-rel pointers + // + + switch (reloc) + { +#ifdef HOST_64BIT + case srRelocDir64Ptr: +#endif + case srRelocAbsolutePtr: + case srRelocHighLowPtr: + // For non pc-rel pointers we don't need to record a section reloc + hr = S_OK; + break; + +#if defined (TARGET_X86) || defined (TARGET_AMD64) + case srRelocRelativePtr: + case srRelocRelative: + hr = addSectReloc(offset, NULL, reloc, extra); + break; +#endif + + default: + _ASSERTE(!"unhandled reloc in PESection::addBaseReloc"); + break; + } + return hr; +} + +/******************************************************************/ +// Dynamic mem allocation, but we can't move old blocks (since others +// have pointers to them), so we need a fancy way to grow +char* PESection::getBlock(unsigned len, unsigned align) +{ + return m_blobFetcher.MakeNewBlock(len, align); +} + +unsigned PESection::dataLen() +{ + return m_blobFetcher.GetDataLen(); +} + +// Apply all the relocs for in memory conversion + +// <REVISIT_TODO>@FUTURE: Currently, our VM is rather inefficient in dealing with in-memory RVA. +// @FUTURE: VM is given an index to memory pool and a helper will return the memory pointer given the index. +// @FUTURE: We will consider having the coverter resolve RVAs into addresses.</REVISIT_TODO> + +HRESULT PESection::applyRelocs(CeeGenTokenMapper *pTokenMapper) +{ + // For each section, go through each of its relocs + for(PESectionReloc* pCurReloc = m_relocStart; pCurReloc < m_relocCur; pCurReloc++) { + if (pCurReloc->type == srRelocMapToken) { + unsigned * pos = (unsigned*) + m_blobFetcher.ComputePointer(pCurReloc->offset); + mdToken newToken; + PREFIX_ASSUME(pos != NULL); + if (pTokenMapper->HasTokenMoved(*pos, newToken)) { + // we have a mapped token + *pos = newToken; + } + } + +#if 0 + _ASSERTE(pCurReloc->offset + 4 <= CurSection.m_blobFetcher.GetDataLen()); + unsigned * pAddr = (unsigned *) + CurSection.m_blobFetcher.ComputePointer(pCurReloc->offset); + _ASSERTE(pCurReloc->type == srRelocAbsolute); + + // Current contents contain an offset into pCurReloc->section + // computePointer() is like pCurReloc-section + *pAddr, but for non-linear section + // This will resolve *pAddr to be a complete address + *pAddr = (unsigned) pCurReloc->section->computePointer(*pAddr); +#endif + + } // End relocs + return S_OK; +} + +HRESULT PESection::cloneInstance(PESection *destination) { + PESectionReloc *cur; + INT32 newSize; + HRESULT hr = NOERROR; + + _ASSERTE(destination); + + destination->dirEntry = dirEntry; + + //Merge the information currently in the BlobFetcher into + //out current blob fetcher + m_blobFetcher.Merge(&(destination->m_blobFetcher)); + + //Copy the name. + strncpy_s(destination->m_name, sizeof(destination->m_name), m_name, sizeof(m_name) - 1); + + //Clone the relocs + //If the arrays aren't the same size, reallocate as necessary. + //<REVISIT_TODO>@FUTURE: Make this a ref-counted structure and don't copy it.</REVISIT_TODO> + + newSize = (INT32)(m_relocCur-m_relocStart); + + if (newSize>(destination->m_relocEnd - destination->m_relocStart)) { + delete destination->m_relocStart; + + destination->m_relocStart = new (nothrow) PESectionReloc[newSize]; + if (destination->m_relocStart == NULL) + IfFailGo( E_OUTOFMEMORY ); + destination->m_relocEnd = destination->m_relocStart+(newSize); + } + + //copy the correct data over into our new array. + memcpy(destination->m_relocStart, m_relocStart, sizeof(PESectionReloc)*(newSize)); + destination->m_relocCur = destination->m_relocStart + (newSize); + for (cur=destination->m_relocStart; cur<destination->m_relocCur; cur++) { + cur->section=destination; + } +ErrExit: + return hr; +} + +void PESection::SetInitialGrowth(unsigned growth) +{ + m_blobFetcher.SetInitialGrowth(growth); +} diff --git a/src/coreclr/md/ceefilegen/stdafx.h b/src/coreclr/md/ceefilegen/stdafx.h new file mode 100644 index 00000000000..36f42f95aa5 --- /dev/null +++ b/src/coreclr/md/ceefilegen/stdafx.h @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// stdafx.h +// + +// +// Common include file for utility code. +//***************************************************************************** + +#define _CRT_DEPENDENCY_ //this code depends on the crt file functions +#include <crtwrap.h> +#include <string.h> +#include <limits.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> // for qsort +#include <windows.h> +#include <time.h> + +#include <corerror.h> +#include <utilcode.h> + +#include <corpriv.h> + +#include "pesectionman.h" + +#include "ceegen.h" +#include "ceesectionstring.h" diff --git a/src/coreclr/md/compiler/CMakeLists.txt b/src/coreclr/md/compiler/CMakeLists.txt new file mode 100644 index 00000000000..a2d3a5776c0 --- /dev/null +++ b/src/coreclr/md/compiler/CMakeLists.txt @@ -0,0 +1,85 @@ +set(MDCOMPILER_SOURCES + assemblymd.cpp + assemblymd_emit.cpp + classfactory.cpp + custattr_import.cpp + custattr_emit.cpp + disp.cpp + emit.cpp + filtermanager.cpp + helper.cpp + import.cpp + importhelper.cpp + mdutil.cpp + regmeta.cpp + regmeta_compilersupport.cpp + regmeta_emit.cpp + regmeta_import.cpp + regmeta_imetadatatables.cpp + regmeta_vm.cpp +) + +set(MDCOMPILER_HEADERS + ../../inc/corhdr.h + ../../inc/corpriv.h + ../../inc/mdcommon.h + ../../inc/metadata.h + ../../inc/posterror.h + ../../inc/sstring.h + ../../inc/switches.h + ../inc/cahlprinternal.h + ../inc/mdlog.h + ../inc/metamodelrw.h + ../inc/rwutil.h + ../inc/stgio.h + classfactory.h + custattr.h + disp.h + filtermanager.h + importhelper.h + mdperf.h + mdutil.h + regmeta.h +) + +set(MDCOMPILER_WKS_SOURCES + ${MDCOMPILER_SOURCES} + verifylayouts.cpp +) + +set(MDCOMPILER_WKS_HEADERS + ${MDCOMPILER_HEADERS} + ../inc/verifylayouts.h +) + +convert_to_absolute_path(MDCOMPILER_SOURCES ${MDCOMPILER_SOURCES}) +convert_to_absolute_path(MDCOMPILER_HEADERS ${MDCOMPILER_HEADERS}) +convert_to_absolute_path(MDCOMPILER_WKS_SOURCES ${MDCOMPILER_WKS_SOURCES}) +convert_to_absolute_path(MDCOMPILER_WKS_HEADERS ${MDCOMPILER_WKS_HEADERS}) + +if (CLR_CMAKE_TARGET_WIN32) + list(APPEND MDCOMPILER_SOURCES ${MDCOMPILER_HEADERS}) + list(APPEND MDCOMPILER_WKS_SOURCES ${MDCOMPILER_WKS_HEADERS}) +endif() + +add_library_clr(mdcompiler_dac ${MDCOMPILER_SOURCES}) +set_target_properties(mdcompiler_dac PROPERTIES DAC_COMPONENT TRUE) +target_precompile_headers(mdcompiler_dac PRIVATE stdafx.h) + +add_library_clr(mdcompiler_wks_obj OBJECT ${MDCOMPILER_WKS_SOURCES}) +target_compile_definitions(mdcompiler_wks_obj PRIVATE FEATURE_METADATA_EMIT_ALL) +target_precompile_headers(mdcompiler_wks_obj PRIVATE stdafx.h) +add_library(mdcompiler_wks INTERFACE) +target_sources(mdcompiler_wks INTERFACE $<TARGET_OBJECTS:mdcompiler_wks_obj>) + +add_library_clr(mdcompiler-dbi ${MDCOMPILER_SOURCES}) +set_target_properties(mdcompiler-dbi PROPERTIES DBI_COMPONENT TRUE) +target_precompile_headers(mdcompiler-dbi PRIVATE stdafx.h) + +add_library_clr(mdcompiler_crossgen ${MDCOMPILER_SOURCES}) +set_target_properties(mdcompiler_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) +target_precompile_headers(mdcompiler_crossgen PRIVATE stdafx.h) + +add_library_clr(mdcompiler_ppdb ${MDCOMPILER_SOURCES}) +target_compile_definitions(mdcompiler_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB) +target_precompile_headers(mdcompiler_ppdb PRIVATE stdafx.h) diff --git a/src/coreclr/md/compiler/assemblymd.cpp b/src/coreclr/md/compiler/assemblymd.cpp new file mode 100644 index 00000000000..d778c636722 --- /dev/null +++ b/src/coreclr/md/compiler/assemblymd.cpp @@ -0,0 +1,726 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// AssemblyMD.cpp +// + +// +// Implementation for the assembly meta data import code (code:IMetaDataAssemblyImport). +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +//******************************************************************************* +// Get the properties for the given Assembly token. +//******************************************************************************* +STDMETHODIMP RegMeta::GetAssemblyProps( // S_OK or error. + mdAssembly mda, // [IN] The Assembly for which to get the properties. + const void **ppbPublicKey, // [OUT] Pointer to the public key. + ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key. + ULONG *pulHashAlgId, // [OUT] Hash Algorithm. + __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData. + DWORD *pdwAssemblyFlags) // [OUT] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + AssemblyRec *pRecord; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "RegMeta::GetAssemblyProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mda, ppbPublicKey, pcbPublicKey, pulHashAlgId, szName, cchName, pchName, pMetaData, + pdwAssemblyFlags)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mda) == mdtAssembly && RidFromToken(mda)); + IfFailGo(pMiniMd->GetAssemblyRecord(RidFromToken(mda), &pRecord)); + + if (ppbPublicKey != NULL) + { + IfFailGo(pMiniMd->getPublicKeyOfAssembly(pRecord, (const BYTE **)ppbPublicKey, pcbPublicKey)); + } + if (pulHashAlgId) + *pulHashAlgId = pMiniMd->getHashAlgIdOfAssembly(pRecord); + if (pMetaData) + { + pMetaData->usMajorVersion = pMiniMd->getMajorVersionOfAssembly(pRecord); + pMetaData->usMinorVersion = pMiniMd->getMinorVersionOfAssembly(pRecord); + pMetaData->usBuildNumber = pMiniMd->getBuildNumberOfAssembly(pRecord); + pMetaData->usRevisionNumber = pMiniMd->getRevisionNumberOfAssembly(pRecord); + IfFailGo(pMiniMd->getLocaleOfAssembly(pRecord, pMetaData->szLocale, + pMetaData->cbLocale, &pMetaData->cbLocale)); + pMetaData->ulProcessor = 0; + pMetaData->ulOS = 0; + } + if (pdwAssemblyFlags) + { + *pdwAssemblyFlags = pMiniMd->getFlagsOfAssembly(pRecord); + + // Turn on the afPublicKey if PublicKey blob is not empty + DWORD cbPublicKey; + const BYTE *pbPublicKey; + IfFailGo(pMiniMd->getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey)); + if (cbPublicKey != 0) + *pdwAssemblyFlags |= afPublicKey; + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szName || pchName) + IfFailGo(pMiniMd->getNameOfAssembly(pRecord, szName, cchName, pchName)); +ErrExit: + + STOP_MD_PERF(GetAssemblyProps); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetAssemblyProps + +//******************************************************************************* +// Get the properties for the given AssemblyRef token. +//******************************************************************************* +STDMETHODIMP RegMeta::GetAssemblyRefProps( // S_OK or error. + mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties. + const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token. + ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token. + __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData. + const void **ppbHashValue, // [OUT] Hash blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob. + DWORD *pdwAssemblyRefFlags) // [OUT] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + AssemblyRefRec *pRecord; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "RegMeta::GetAssemblyRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mdar, ppbPublicKeyOrToken, pcbPublicKeyOrToken, szName, cchName, + pchName, pMetaData, ppbHashValue, pdwAssemblyRefFlags)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdar) == mdtAssemblyRef && RidFromToken(mdar)); + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(mdar), &pRecord)); + + if (ppbPublicKeyOrToken != NULL) + { + IfFailGo(pMiniMd->getPublicKeyOrTokenOfAssemblyRef(pRecord, (const BYTE **)ppbPublicKeyOrToken, pcbPublicKeyOrToken)); + } + if (pMetaData) + { + pMetaData->usMajorVersion = pMiniMd->getMajorVersionOfAssemblyRef(pRecord); + pMetaData->usMinorVersion = pMiniMd->getMinorVersionOfAssemblyRef(pRecord); + pMetaData->usBuildNumber = pMiniMd->getBuildNumberOfAssemblyRef(pRecord); + pMetaData->usRevisionNumber = pMiniMd->getRevisionNumberOfAssemblyRef(pRecord); + IfFailGo(pMiniMd->getLocaleOfAssemblyRef(pRecord, pMetaData->szLocale, + pMetaData->cbLocale, &pMetaData->cbLocale)); + pMetaData->ulProcessor = 0; + pMetaData->ulOS = 0; + } + if (ppbHashValue != NULL) + { + IfFailGo(pMiniMd->getHashValueOfAssemblyRef(pRecord, (const BYTE **)ppbHashValue, pcbHashValue)); + } + if (pdwAssemblyRefFlags) + *pdwAssemblyRefFlags = pMiniMd->getFlagsOfAssemblyRef(pRecord); + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szName || pchName) + IfFailGo(pMiniMd->getNameOfAssemblyRef(pRecord, szName, cchName, pchName)); +ErrExit: + + STOP_MD_PERF(GetAssemblyRefProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetAssemblyRefProps + +//******************************************************************************* +// Get the properties for the given File token. +//******************************************************************************* +STDMETHODIMP RegMeta::GetFileProps( // S_OK or error. + mdFile mdf, // [IN] The File for which to get the properties. + __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob. + DWORD *pdwFileFlags) // [OUT] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + FileRec *pRecord; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "RegMeta::GetFileProps(%#08x, %#08x, %#08x, %#08x, %#08x, %#08x, %#08x)\n", + mdf, szName, cchName, pchName, ppbHashValue, pcbHashValue, pdwFileFlags)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdf) == mdtFile && RidFromToken(mdf)); + IfFailGo(pMiniMd->GetFileRecord(RidFromToken(mdf), &pRecord)); + + if (ppbHashValue != NULL) + { + IfFailGo(pMiniMd->getHashValueOfFile(pRecord, (const BYTE **)ppbHashValue, pcbHashValue)); + } + if (pdwFileFlags != NULL) + *pdwFileFlags = pMiniMd->getFlagsOfFile(pRecord); + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if ((szName != NULL) || (pchName != NULL)) + { + IfFailGo(pMiniMd->getNameOfFile(pRecord, szName, cchName, pchName)); + } + +ErrExit: + STOP_MD_PERF(GetFileProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetFileProps + +//******************************************************************************* +// Get the properties for the given ExportedType token. +//******************************************************************************* +STDMETHODIMP RegMeta::GetExportedTypeProps( // S_OK or error. + mdExportedType mdct, // [IN] The ExportedType for which to get the properties. + __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file. + DWORD *pdwExportedTypeFlags) // [OUT] Flags. +{ + HRESULT hr = S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + ExportedTypeRec *pRecord; // The exported type. + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + int bTruncation=0; // Was there name truncation? + + LOG((LOGMD, "RegMeta::GetExportedTypeProps(%#08x, %#08x, %#08x, %#08x, %#08x, %#08x, %#08x)\n", + mdct, szName, cchName, pchName, + ptkImplementation, ptkTypeDef, pdwExportedTypeFlags)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdct) == mdtExportedType && RidFromToken(mdct)); + IfFailGo(pMiniMd->GetExportedTypeRecord(RidFromToken(mdct), &pRecord)); + + if (szName || pchName) + { + LPCSTR szTypeNamespace; + LPCSTR szTypeName; + + IfFailGo(pMiniMd->getTypeNamespaceOfExportedType(pRecord, &szTypeNamespace)); + PREFIX_ASSUME(szTypeNamespace != NULL); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzTypeNamespace, szTypeNamespace); + IfNullGo(wzTypeNamespace); + + IfFailGo(pMiniMd->getTypeNameOfExportedType(pRecord, &szTypeName)); + _ASSERTE(*szTypeName); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzTypeName, szTypeName); + IfNullGo(wzTypeName); + + if (szName) + bTruncation = ! (ns::MakePath(szName, cchName, wzTypeNamespace, wzTypeName)); + if (pchName) + { + if (bTruncation || !szName) + *pchName = ns::GetFullLength(wzTypeNamespace, wzTypeName); + else + *pchName = (ULONG)(wcslen(szName) + 1); + } + } + if (ptkImplementation) + *ptkImplementation = pMiniMd->getImplementationOfExportedType(pRecord); + if (ptkTypeDef) + *ptkTypeDef = pMiniMd->getTypeDefIdOfExportedType(pRecord); + if (pdwExportedTypeFlags) + *pdwExportedTypeFlags = pMiniMd->getFlagsOfExportedType(pRecord); + + if (bTruncation && hr == S_OK) + { + if ((szName != NULL) && (cchName > 0)) + { // null-terminate the truncated output string + szName[cchName - 1] = W('\0'); + } + hr = CLDB_S_TRUNCATION; + } + +ErrExit: + STOP_MD_PERF(GetExportedTypeProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetExportedTypeProps + +//******************************************************************************* +// Get the properties for the given Resource token. +//******************************************************************************* +STDMETHODIMP RegMeta::GetManifestResourceProps( // S_OK or error. + mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties. + __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file. + DWORD *pdwResourceFlags) // [OUT] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ManifestResourceRec *pRecord; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "RegMeta::GetManifestResourceProps(" + "%#08x, %#08x, %#08x, %#08x, %#08x, %#08x, %#08x)\n", + mdmr, szName, cchName, pchName, + ptkImplementation, pdwOffset, + pdwResourceFlags)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdmr) == mdtManifestResource && RidFromToken(mdmr)); + IfFailGo(pMiniMd->GetManifestResourceRecord(RidFromToken(mdmr), &pRecord)); + + if (ptkImplementation) + *ptkImplementation = pMiniMd->getImplementationOfManifestResource(pRecord); + if (pdwOffset) + *pdwOffset = pMiniMd->getOffsetOfManifestResource(pRecord); + if (pdwResourceFlags) + *pdwResourceFlags = pMiniMd->getFlagsOfManifestResource(pRecord); + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szName || pchName) + IfFailGo(pMiniMd->getNameOfManifestResource(pRecord, szName, cchName, pchName)); +ErrExit: + + STOP_MD_PERF(GetManifestResourceProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetManifestResourceProps + + +//******************************************************************************* +// Enumerating through all of the AssemblyRefs. +//******************************************************************************* +STDMETHODIMP RegMeta::EnumAssemblyRefs( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdAssemblyRef rAssemblyRefs[], // [OUT] Put AssemblyRefs here. + ULONG cMax, // [IN] Max AssemblyRefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "MD RegMeta::EnumAssemblyRefs(%#08x, %#08x, %#08x, %#08x)\n", + phEnum, rAssemblyRefs, cMax, pcTokens)); + START_MD_PERF(); + + LOCKREAD(); + + if (*ppmdEnum == 0) + { + // instantiate a new ENUM. + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator. + IfFailGo(HENUMInternal::CreateSimpleEnum( + mdtAssemblyRef, + 1, + pMiniMd->getCountAssemblyRefs() + 1, + &pEnum) ); + + // set the output parameter. + *ppmdEnum = pEnum; + } + else + pEnum = *ppmdEnum; + + // we can only fill the minimum of what the caller asked for or what we have left. + IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rAssemblyRefs, pcTokens)); +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumAssemblyRefs); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumAssemblyRefs + +//******************************************************************************* +// Enumerating through all of the Files. +//******************************************************************************* +STDMETHODIMP RegMeta::EnumFiles( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdFile rFiles[], // [OUT] Put Files here. + ULONG cMax, // [IN] Max Files to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "MD RegMeta::EnumFiles(%#08x, %#08x, %#08x, %#08x)\n", + phEnum, rFiles, cMax, pcTokens)); + START_MD_PERF(); + LOCKREAD(); + + if (*ppmdEnum == 0) + { + // instantiate a new ENUM. + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator. + IfFailGo(HENUMInternal::CreateSimpleEnum( + mdtFile, + 1, + pMiniMd->getCountFiles() + 1, + &pEnum) ); + + // set the output parameter. + *ppmdEnum = pEnum; + } + else + pEnum = *ppmdEnum; + + // we can only fill the minimum of what the caller asked for or what we have left. + IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rFiles, pcTokens)); +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumFiles); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumFiles + +//******************************************************************************* +// Enumerating through all of the ExportedTypes. +//******************************************************************************* +STDMETHODIMP RegMeta::EnumExportedTypes( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdExportedType rExportedTypes[], // [OUT] Put ExportedTypes here. + ULONG cMax, // [IN] Max ExportedTypes to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "MD RegMeta::EnumExportedTypes(%#08x, %#08x, %#08x, %#08x)\n", + phEnum, rExportedTypes, cMax, pcTokens)); + + START_MD_PERF(); + LOCKREAD(); + + if (*ppmdEnum == 0) + { + // instantiate a new ENUM. + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + if (pMiniMd->HasDelete() && + ((m_OptionValue.m_ImportOption & MDImportOptionAllExportedTypes) == 0)) + { + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtExportedType, &pEnum) ); + + // add all Types to the dynamic array if name is not _Delete + for (ULONG index = 1; index <= pMiniMd->getCountExportedTypes(); index ++ ) + { + ExportedTypeRec *pRec; + IfFailGo(pMiniMd->GetExportedTypeRecord(index, &pRec)); + LPCSTR szTypeName; + IfFailGo(pMiniMd->getTypeNameOfExportedType(pRec, &szTypeName)); + if (IsDeletedName(szTypeName)) + { + continue; + } + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtExportedType) ) ); + } + } + else + { + // create the enumerator. + IfFailGo(HENUMInternal::CreateSimpleEnum( + mdtExportedType, + 1, + pMiniMd->getCountExportedTypes() + 1, + &pEnum) ); + } + + // set the output parameter. + *ppmdEnum = pEnum; + } + else + pEnum = *ppmdEnum; + + // we can only fill the minimum of what the caller asked for or what we have left. + IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rExportedTypes, pcTokens)); +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumExportedTypes); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumExportedTypes + +//******************************************************************************* +// Enumerating through all of the Resources. +//******************************************************************************* +STDMETHODIMP RegMeta::EnumManifestResources( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdManifestResource rManifestResources[], // [OUT] Put ManifestResources here. + ULONG cMax, // [IN] Max Resources to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "MD RegMeta::EnumManifestResources(%#08x, %#08x, %#08x, %#08x)\n", + phEnum, rManifestResources, cMax, pcTokens)); + + START_MD_PERF(); + LOCKREAD(); + + if (*ppmdEnum == 0) + { + // instantiate a new ENUM. + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator. + IfFailGo(HENUMInternal::CreateSimpleEnum( + mdtManifestResource, + 1, + pMiniMd->getCountManifestResources() + 1, + &pEnum) ); + + // set the output parameter. + *ppmdEnum = pEnum; + } + else + pEnum = *ppmdEnum; + + // we can only fill the minimum of what the caller asked for or what we have left. + IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rManifestResources, pcTokens)); +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumManifestResources); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumManifestResources + +//******************************************************************************* +// Get the Assembly token for the given scope.. +//******************************************************************************* +STDMETHODIMP RegMeta::GetAssemblyFromScope( // S_OK or error + mdAssembly *ptkAssembly) // [OUT] Put token here. +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMd = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::GetAssemblyFromScope(%#08x)\n", ptkAssembly)); + START_MD_PERF(); + LOCKREAD(); + _ASSERTE(ptkAssembly); + + pMiniMd = &(m_pStgdb->m_MiniMd); + if (pMiniMd->getCountAssemblys()) + { + *ptkAssembly = TokenFromRid(1, mdtAssembly); + } + else + { + IfFailGo( CLDB_E_RECORD_NOTFOUND ); + } +ErrExit: + STOP_MD_PERF(GetAssemblyFromScope); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetAssemblyFromScope + +//******************************************************************************* +// Find the ExportedType given the name. +//******************************************************************************* +STDMETHODIMP RegMeta::FindExportedTypeByName( // S_OK or error + LPCWSTR szName, // [IN] Name of the ExportedType. + mdExportedType tkEnclosingType, // [IN] Enclosing ExportedType. + mdExportedType *ptkExportedType) // [OUT] Put the ExportedType token here. +{ + HRESULT hr = S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = NULL; + LPSTR szNameUTF8 = NULL; + + LOG((LOGMD, "MD RegMeta::FindExportedTypeByName(%S, %#08x, %#08x)\n", + MDSTR(szName), tkEnclosingType, ptkExportedType)); + + START_MD_PERF(); + LOCKREAD(); + + + // Validate name for prefix. + if (!szName) + IfFailGo(E_INVALIDARG); + + _ASSERTE(szName && ptkExportedType); + + pMiniMd = &(m_pStgdb->m_MiniMd); + UTF8STR(szName, szNameUTF8); + LPCSTR szTypeName; + LPCSTR szTypeNamespace; + + ns::SplitInline(szNameUTF8, szTypeNamespace, szTypeName); + + IfFailGo(ImportHelper::FindExportedType(pMiniMd, + szTypeNamespace, + szTypeName, + tkEnclosingType, + ptkExportedType)); +ErrExit: + STOP_MD_PERF(FindExportedTypeByName); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::FindExportedTypeByName + +//******************************************************************************* +// Find the ManifestResource given the name. +//******************************************************************************* +STDMETHODIMP RegMeta::FindManifestResourceByName( // S_OK or error + LPCWSTR szName, // [IN] Name of the ManifestResource. + mdManifestResource *ptkManifestResource) // [OUT] Put the ManifestResource token here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LPCUTF8 szNameTmp = NULL; + CMiniMdRW *pMiniMd = NULL; + + LOG((LOGMD, "MD RegMeta::FindManifestResourceByName(%S, %#08x)\n", + MDSTR(szName), ptkManifestResource)); + + START_MD_PERF(); + LOCKREAD(); + + + // Validate name for prefix. + if (!szName) + IfFailGo(E_INVALIDARG); + + _ASSERTE(szName && ptkManifestResource); + + ManifestResourceRec *pRecord; + ULONG cRecords; // Count of records. + LPUTF8 szUTF8Name; // UTF8 version of the name passed in. + ULONG i; + + pMiniMd = &(m_pStgdb->m_MiniMd); + *ptkManifestResource = mdManifestResourceNil; + cRecords = pMiniMd->getCountManifestResources(); + UTF8STR(szName, szUTF8Name); + + // Search for the TypeRef. + for (i = 1; i <= cRecords; i++) + { + IfFailGo(pMiniMd->GetManifestResourceRecord(i, &pRecord)); + IfFailGo(pMiniMd->getNameOfManifestResource(pRecord, &szNameTmp)); + if (! strcmp(szUTF8Name, szNameTmp)) + { + *ptkManifestResource = TokenFromRid(i, mdtManifestResource); + goto ErrExit; + } + } + IfFailGo( CLDB_E_RECORD_NOTFOUND ); +ErrExit: + + STOP_MD_PERF(FindManifestResourceByName); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::FindManifestResourceByName + +//******************************************************************************* +// Used to find assemblies either in Fusion cache or on disk at build time. +//******************************************************************************* +STDMETHODIMP RegMeta::FindAssembliesByName( // S_OK or error + LPCWSTR szAppBase, // [IN] optional - can be NULL + LPCWSTR szPrivateBin, // [IN] optional - can be NULL + LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting + IUnknown *ppIUnk[], // [OUT] put IMetaDataAssemblyImport pointers here + ULONG cMax, // [IN] The max number to put + ULONG *pcAssemblies) // [OUT] The number of assemblies returned. +{ +#ifdef FEATURE_METADATA_IN_VM + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::FindAssembliesByName(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + szAppBase, szPrivateBin, szAssemblyName, ppIUnk, cMax, pcAssemblies)); + START_MD_PERF(); + + // No need to lock this function. It is going through fusion to find the matching Assemblies by name + + IfFailGo(COR_E_NOTSUPPORTED); + +ErrExit: + STOP_MD_PERF(FindAssembliesByName); + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_IN_VM + // Calls to fusion are not suported outside VM + return E_NOTIMPL; +#endif //!FEATURE_METADATA_IN_VM +} // RegMeta::FindAssembliesByName diff --git a/src/coreclr/md/compiler/assemblymd_emit.cpp b/src/coreclr/md/compiler/assemblymd_emit.cpp new file mode 100644 index 00000000000..7a43a56259a --- /dev/null +++ b/src/coreclr/md/compiler/assemblymd_emit.cpp @@ -0,0 +1,808 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// AssemblyMD.cpp +// + +// +// Implementation for the assembly meta data emit code (code:IMetaDataAssemblyEmit). +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +#ifdef FEATURE_METADATA_EMIT + +//******************************************************************************* +// Define an Assembly and set the attributes. +//******************************************************************************* +STDMETHODIMP RegMeta::DefineAssembly( // S_OK or error. + const void *pbPublicKey, // [IN] Public key of the assembly. + ULONG cbPublicKey, // [IN] Count of bytes in the public key. + ULONG ulHashAlgId, // [IN] Hash Algorithm. + LPCWSTR szName, // [IN] Name of the assembly. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + DWORD dwAssemblyFlags, // [IN] Flags. + mdAssembly *pma) // [OUT] Returned Assembly token. +{ + HRESULT hr = S_OK; + + AssemblyRec *pRecord = NULL; // The assembly record. + ULONG iRecord; // RID of the assembly record. + + if (szName == NULL || pMetaData == NULL || pma == NULL) + return E_INVALIDARG; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineAssembly(0x%08x, 0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + pbPublicKey, cbPublicKey, ulHashAlgId, MDSTR(szName), pMetaData, + dwAssemblyFlags, pma)); + + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(szName && pMetaData && pma); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + // Assembly defs always contain a full public key (assuming they're strong + // named) rather than the tokenized version. Force the flag on to indicate + // this, and this way blindly copying public key & flags from a def to a ref + // will work (though the ref will be bulkier than strictly necessary). + if (cbPublicKey != 0) + dwAssemblyFlags |= afPublicKey; + + if (CheckDups(MDDupAssembly)) + { // Should be no more than one -- just check count of records. + if (m_pStgdb->m_MiniMd.getCountAssemblys() > 0) + { // S/b only one, so we know the rid. + iRecord = 1; + // If ENC, let them update the existing record. + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRecord(iRecord, &pRecord)); + else + { // Not ENC, so it is a duplicate. + *pma = TokenFromRid(iRecord, mdtAssembly); + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + } + else + { // Not ENC, not duplicate checking, so shouldn't already have one. + _ASSERTE(m_pStgdb->m_MiniMd.getCountAssemblys() == 0); + } + + // Create a new record, if needed. + if (pRecord == NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.AddAssemblyRecord(&pRecord, &iRecord)); + } + + // Set the output parameter. + *pma = TokenFromRid(iRecord, mdtAssembly); + + IfFailGo(_SetAssemblyProps(*pma, pbPublicKey, cbPublicKey, ulHashAlgId, szName, pMetaData, dwAssemblyFlags)); + +ErrExit: + + STOP_MD_PERF(DefineAssembly); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::DefineAssembly + +//******************************************************************************* +// Define an AssemblyRef and set the attributes. +//******************************************************************************* +STDMETHODIMP RegMeta::DefineAssemblyRef( // S_OK or error. + const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly. + ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token. + LPCWSTR szName, // [IN] Name of the assembly being referenced. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwAssemblyRefFlags, // [IN] Flags. + mdAssemblyRef *pmar) // [OUT] Returned AssemblyRef token. +{ + HRESULT hr = S_OK; + + AssemblyRefRec *pRecord = NULL; + ULONG iRecord; + + if (szName == NULL || pmar == NULL || pMetaData == NULL) + return E_INVALIDARG; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineAssemblyRef(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + pbPublicKeyOrToken, cbPublicKeyOrToken, MDSTR(szName), pMetaData, pbHashValue, + cbHashValue, dwAssemblyRefFlags, pmar)); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(szName && pmar); + + if (CheckDups(MDDupAssemblyRef)) + { + LPUTF8 szUTF8Name, szUTF8Locale; + UTF8STR(szName, szUTF8Name); + UTF8STR(pMetaData->szLocale, szUTF8Locale); + hr = ImportHelper::FindAssemblyRef(&m_pStgdb->m_MiniMd, + szUTF8Name, + szUTF8Locale, + pbPublicKeyOrToken, + cbPublicKeyOrToken, + pMetaData->usMajorVersion, + pMetaData->usMinorVersion, + pMetaData->usBuildNumber, + pMetaData->usRevisionNumber, + dwAssemblyRefFlags, + pmar); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRefRecord(RidFromToken(*pmar), &pRecord)); + } + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + { + IfFailGo(hr); + } + } + + // Create a new record if needed. + if (pRecord == NULL) + { + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddAssemblyRefRecord(&pRecord, &iRecord)); + + // Set the output parameter. + *pmar = TokenFromRid(iRecord, mdtAssemblyRef); + } + + // Set rest of the attributes. + SetCallerDefine(); + IfFailGo(_SetAssemblyRefProps(*pmar, pbPublicKeyOrToken, cbPublicKeyOrToken, szName, pMetaData, + pbHashValue, cbHashValue, + dwAssemblyRefFlags)); +ErrExit: + SetCallerExternal(); + + STOP_MD_PERF(DefineAssemblyRef); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::DefineAssemblyRef + +//******************************************************************************* +// Define a File and set the attributes. +//******************************************************************************* +STDMETHODIMP RegMeta::DefineFile( // S_OK or error. + LPCWSTR szName, // [IN] Name of the file. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwFileFlags, // [IN] Flags. + mdFile *pmf) // [OUT] Returned File token. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + FileRec *pRecord = NULL; + ULONG iRecord; + + LOG((LOGMD, "RegMeta::DefineFile(%S, %#08x, %#08x, %#08x, %#08x)\n", + MDSTR(szName), pbHashValue, cbHashValue, dwFileFlags, pmf)); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(szName && pmf); + + if (CheckDups(MDDupFile)) + { + LPUTF8 szUTF8Name; + UTF8STR(szName, szUTF8Name); + hr = ImportHelper::FindFile(&m_pStgdb->m_MiniMd, szUTF8Name, pmf); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + IfFailGo(m_pStgdb->m_MiniMd.GetFileRecord(RidFromToken(*pmf), &pRecord)); + } + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + { + IfFailGo(hr); + } + } + + // Create a new record if needed. + if (pRecord == NULL) + { + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddFileRecord(&pRecord, &iRecord)); + + // Set the output parameter. + *pmf = TokenFromRid(iRecord, mdtFile); + + // Set the name. + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_File, FileRec::COL_Name, pRecord, szName)); + } + + // Set rest of the attributes. + IfFailGo(_SetFileProps(*pmf, pbHashValue, cbHashValue, dwFileFlags)); +ErrExit: + + STOP_MD_PERF(DefineFile); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::DefineFile + +//******************************************************************************* +// Define a ExportedType and set the attributes. +//******************************************************************************* +STDMETHODIMP RegMeta::DefineExportedType( // S_OK or error. + LPCWSTR szName, // [IN] Name of the Com Type. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef tkTypeDef, // [IN] TypeDef token within the file. + DWORD dwExportedTypeFlags, // [IN] Flags. + mdExportedType *pmct) // [OUT] Returned ExportedType token. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ExportedTypeRec *pRecord = NULL; + ULONG iRecord; + LPSTR szNameUTF8; + LPCSTR szTypeNameUTF8; + LPCSTR szTypeNamespaceUTF8; + + LOG((LOGMD, "RegMeta::DefineExportedType(%S, %#08x, %08x, %#08x, %#08x)\n", + MDSTR(szName), tkImplementation, tkTypeDef, + dwExportedTypeFlags, pmct)); + + START_MD_PERF(); + LOCKWRITE(); + + // Validate name for prefix. + if (szName == NULL) + IfFailGo(E_INVALIDARG); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + //SLASHES2DOTS_NAMESPACE_BUFFER_UNICODE(szName, szName); + + UTF8STR(szName, szNameUTF8); + // Split the name into name/namespace pair. + ns::SplitInline(szNameUTF8, szTypeNamespaceUTF8, szTypeNameUTF8); + + _ASSERTE(szName && dwExportedTypeFlags != UINT32_MAX && pmct); + _ASSERTE(TypeFromToken(tkImplementation) == mdtFile || + TypeFromToken(tkImplementation) == mdtAssemblyRef || + TypeFromToken(tkImplementation) == mdtExportedType || + tkImplementation == mdTokenNil); + + if (CheckDups(MDDupExportedType)) + { + hr = ImportHelper::FindExportedType(&m_pStgdb->m_MiniMd, + szTypeNamespaceUTF8, + szTypeNameUTF8, + tkImplementation, + pmct); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(*pmct), &pRecord)); + } + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + { + IfFailGo(hr); + } + } + + // Create a new record if needed. + if (pRecord == NULL) + { + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddExportedTypeRecord(&pRecord, &iRecord)); + + // Set the output parameter. + *pmct = TokenFromRid(iRecord, mdtExportedType); + + // Set the TypeName and TypeNamespace. + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ExportedType, + ExportedTypeRec::COL_TypeName, pRecord, szTypeNameUTF8)); + if (szTypeNamespaceUTF8) + { + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ExportedType, + ExportedTypeRec::COL_TypeNamespace, pRecord, szTypeNamespaceUTF8)); + } + } + + // Set rest of the attributes. + IfFailGo(_SetExportedTypeProps(*pmct, tkImplementation, tkTypeDef, + dwExportedTypeFlags)); +ErrExit: + + STOP_MD_PERF(DefineExportedType); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::DefineExportedType + +//******************************************************************************* +// Define a Resource and set the attributes. +//******************************************************************************* +STDMETHODIMP RegMeta::DefineManifestResource( // S_OK or error. + LPCWSTR szName, // [IN] Name of the ManifestResource. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource. + DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file. + DWORD dwResourceFlags, // [IN] Flags. + mdManifestResource *pmmr) // [OUT] Returned ManifestResource token. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ManifestResourceRec *pRecord = NULL; + ULONG iRecord; + + LOG((LOGMD, "RegMeta::DefineManifestResource(%S, %#08x, %#08x, %#08x, %#08x)\n", + MDSTR(szName), tkImplementation, dwOffset, dwResourceFlags, pmmr)); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(szName && dwResourceFlags != UINT32_MAX && pmmr); + _ASSERTE(TypeFromToken(tkImplementation) == mdtFile || + TypeFromToken(tkImplementation) == mdtAssemblyRef || + tkImplementation == mdTokenNil); + + if (CheckDups(MDDupManifestResource)) + { + LPUTF8 szUTF8Name; + UTF8STR(szName, szUTF8Name); + hr = ImportHelper::FindManifestResource(&m_pStgdb->m_MiniMd, szUTF8Name, pmmr); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(*pmmr), &pRecord)); + } + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + { + IfFailGo(hr); + } + } + + // Create a new record if needed. + if (pRecord == NULL) + { + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddManifestResourceRecord(&pRecord, &iRecord)); + + // Set the output parameter. + *pmmr = TokenFromRid(iRecord, mdtManifestResource); + + // Set the name. + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_ManifestResource, + ManifestResourceRec::COL_Name, pRecord, szName)); + } + + // Set the rest of the attributes. + IfFailGo(_SetManifestResourceProps(*pmmr, tkImplementation, + dwOffset, dwResourceFlags)); + +ErrExit: + + STOP_MD_PERF(DefineManifestResource); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::DefineManifestResource + +//******************************************************************************* +// Set the specified attributes on the given Assembly token. +//******************************************************************************* +STDMETHODIMP RegMeta::SetAssemblyProps( // S_OK or error. + mdAssembly ma, // [IN] Assembly token. + const void *pbPublicKey, // [IN] Public key of the assembly. + ULONG cbPublicKey, // [IN] Count of bytes in the public key. + ULONG ulHashAlgId, // [IN] Hash Algorithm. + LPCWSTR szName, // [IN] Name of the assembly. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + DWORD dwAssemblyFlags) // [IN] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(TypeFromToken(ma) == mdtAssembly && RidFromToken(ma)); + + LOG((LOGMD, "RegMeta::SetAssemblyProps(%#08x, %#08x, %#08x, %#08x %S, %#08x, %#08x)\n", + ma, pbPublicKey, cbPublicKey, ulHashAlgId, MDSTR(szName), pMetaData, dwAssemblyFlags)); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + IfFailGo(_SetAssemblyProps(ma, pbPublicKey, cbPublicKey, ulHashAlgId, szName, pMetaData, dwAssemblyFlags)); + +ErrExit: + STOP_MD_PERF(SetAssemblyProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP SetAssemblyProps() + +//******************************************************************************* +// Set the specified attributes on the given AssemblyRef token. +//******************************************************************************* +STDMETHODIMP RegMeta::SetAssemblyRefProps( // S_OK or error. + mdAssemblyRef ar, // [IN] AssemblyRefToken. + const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly. + ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token. + LPCWSTR szName, // [IN] Name of the assembly being referenced. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwAssemblyRefFlags) // [IN] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(TypeFromToken(ar) == mdtAssemblyRef && RidFromToken(ar)); + + LOG((LOGMD, "RegMeta::SetAssemblyRefProps(0x%08x, 0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + ar, pbPublicKeyOrToken, cbPublicKeyOrToken, MDSTR(szName), pMetaData, pbHashValue, cbHashValue, + dwAssemblyRefFlags)); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + IfFailGo(_SetAssemblyRefProps( + ar, + pbPublicKeyOrToken, + cbPublicKeyOrToken, + szName, + pMetaData, + pbHashValue, + cbHashValue, + dwAssemblyRefFlags)); + +ErrExit: + STOP_MD_PERF(SetAssemblyRefProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::SetAssemblyRefProps + +//******************************************************************************* +// Set the specified attributes on the given File token. +//******************************************************************************* +STDMETHODIMP RegMeta::SetFileProps( // S_OK or error. + mdFile file, // [IN] File token. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwFileFlags) // [IN] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + _ASSERTE(TypeFromToken(file) == mdtFile && RidFromToken(file)); + + LOG((LOGMD, "RegMeta::SetFileProps(%#08x, %#08x, %#08x, %#08x)\n", + file, pbHashValue, cbHashValue, dwFileFlags)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + IfFailGo( _SetFileProps(file, pbHashValue, cbHashValue, dwFileFlags) ); + +ErrExit: + + STOP_MD_PERF(SetFileProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::SetFileProps + +//******************************************************************************* +// Set the specified attributes on the given ExportedType token. +//******************************************************************************* +STDMETHODIMP RegMeta::SetExportedTypeProps( // S_OK or error. + mdExportedType ct, // [IN] ExportedType token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef tkTypeDef, // [IN] TypeDef token within the file. + DWORD dwExportedTypeFlags) // [IN] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "RegMeta::SetExportedTypeProps(%#08x, %#08x, %#08x, %#08x)\n", + ct, tkImplementation, tkTypeDef, dwExportedTypeFlags)); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo( _SetExportedTypeProps( ct, tkImplementation, tkTypeDef, dwExportedTypeFlags) ); + +ErrExit: + + STOP_MD_PERF(SetExportedTypeProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::SetExportedTypeProps + +//******************************************************************************* +// Set the specified attributes on the given ManifestResource token. +//******************************************************************************* +STDMETHODIMP RegMeta::SetManifestResourceProps(// S_OK or error. + mdManifestResource mr, // [IN] ManifestResource token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource. + DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file. + DWORD dwResourceFlags) // [IN] Flags. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::SetManifestResourceProps(%#08x, %#08x, %#08x, %#08x)\n", + mr, tkImplementation, dwOffset, + dwResourceFlags)); + + _ASSERTE(TypeFromToken(tkImplementation) == mdtFile || + TypeFromToken(tkImplementation) == mdtAssemblyRef || + tkImplementation == mdTokenNil); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo( _SetManifestResourceProps( mr, tkImplementation, dwOffset, dwResourceFlags) ); + +ErrExit: + + STOP_MD_PERF(SetManifestResourceProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::SetManifestResourceProps() + +//******************************************************************************* +// Helper: Set the specified attributes on the given Assembly token. +//******************************************************************************* +HRESULT RegMeta::_SetAssemblyProps( // S_OK or error. + mdAssembly ma, // [IN] Assembly token. + const void *pbPublicKey, // [IN] Originator of the assembly. + ULONG cbPublicKey, // [IN] Count of bytes in the Originator blob. + ULONG ulHashAlgId, // [IN] Hash Algorithm. + LPCWSTR szName, // [IN] Name of the assembly. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + DWORD dwAssemblyFlags) // [IN] Flags. +{ + AssemblyRec *pRecord = NULL; // The assembly record. + HRESULT hr = S_OK; + + IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRecord(RidFromToken(ma), &pRecord)); + + // Set the data. + if (pbPublicKey) + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Assembly, AssemblyRec::COL_PublicKey, + pRecord, pbPublicKey, cbPublicKey)); + if (ulHashAlgId != UINT32_MAX) + pRecord->SetHashAlgId(ulHashAlgId); + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Assembly, AssemblyRec::COL_Name, pRecord, szName)); + if (pMetaData->usMajorVersion != USHRT_MAX) + pRecord->SetMajorVersion(pMetaData->usMajorVersion); + if (pMetaData->usMinorVersion != USHRT_MAX) + pRecord->SetMinorVersion(pMetaData->usMinorVersion); + if (pMetaData->usBuildNumber != USHRT_MAX) + pRecord->SetBuildNumber(pMetaData->usBuildNumber); + if (pMetaData->usRevisionNumber != USHRT_MAX) + pRecord->SetRevisionNumber(pMetaData->usRevisionNumber); + if (pMetaData->szLocale) + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Assembly, AssemblyRec::COL_Locale, + pRecord, pMetaData->szLocale)); + + dwAssemblyFlags = (dwAssemblyFlags & ~afPublicKey) | (cbPublicKey ? afPublicKey : 0); + pRecord->SetFlags(dwAssemblyFlags); + IfFailGo(UpdateENCLog(ma)); + +ErrExit: + + + return hr; +} // HRESULT RegMeta::_SetAssemblyProps() + +//******************************************************************************* +// Helper: Set the specified attributes on the given AssemblyRef token. +//******************************************************************************* +HRESULT RegMeta::_SetAssemblyRefProps( // S_OK or error. + mdAssemblyRef ar, // [IN] AssemblyRefToken. + const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly. + ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token. + LPCWSTR szName, // [IN] Name of the assembly being referenced. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwAssemblyRefFlags) // [IN] Flags. +{ + AssemblyRefRec *pRecord; + HRESULT hr = S_OK; + + IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRefRecord(RidFromToken(ar), &pRecord)); + + if (pbPublicKeyOrToken) + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken, + pRecord, pbPublicKeyOrToken, cbPublicKeyOrToken)); + if (szName) + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_AssemblyRef, AssemblyRefRec::COL_Name, + pRecord, szName)); + if (pMetaData) + { + if (pMetaData->usMajorVersion != USHRT_MAX) + pRecord->SetMajorVersion(pMetaData->usMajorVersion); + if (pMetaData->usMinorVersion != USHRT_MAX) + pRecord->SetMinorVersion(pMetaData->usMinorVersion); + if (pMetaData->usBuildNumber != USHRT_MAX) + pRecord->SetBuildNumber(pMetaData->usBuildNumber); + if (pMetaData->usRevisionNumber != USHRT_MAX) + pRecord->SetRevisionNumber(pMetaData->usRevisionNumber); + if (pMetaData->szLocale) + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_AssemblyRef, + AssemblyRefRec::COL_Locale, pRecord, pMetaData->szLocale)); + + } + if (pbHashValue) + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue, + pRecord, pbHashValue, cbHashValue)); + if (dwAssemblyRefFlags != UINT32_MAX) + pRecord->SetFlags(PrepareForSaving(dwAssemblyRefFlags)); + + IfFailGo(UpdateENCLog(ar)); + +ErrExit: + + + return hr; +} // RegMeta::_SetAssemblyRefProps + +//******************************************************************************* +// Helper: Set the specified attributes on the given File token. +//******************************************************************************* +HRESULT RegMeta::_SetFileProps( // S_OK or error. + mdFile file, // [IN] File token. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwFileFlags) // [IN] Flags. +{ + FileRec *pRecord; + HRESULT hr = S_OK; + + IfFailGo(m_pStgdb->m_MiniMd.GetFileRecord(RidFromToken(file), &pRecord)); + + if (pbHashValue) + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_File, FileRec::COL_HashValue, pRecord, + pbHashValue, cbHashValue)); + if (dwFileFlags != UINT32_MAX) + pRecord->SetFlags(dwFileFlags); + + IfFailGo(UpdateENCLog(file)); +ErrExit: + return hr; +} // RegMeta::_SetFileProps + +//******************************************************************************* +// Helper: Set the specified attributes on the given ExportedType token. +//******************************************************************************* +HRESULT RegMeta::_SetExportedTypeProps( // S_OK or error. + mdExportedType ct, // [IN] ExportedType token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef tkTypeDef, // [IN] TypeDef token within the file. + DWORD dwExportedTypeFlags) // [IN] Flags. +{ + ExportedTypeRec *pRecord; + HRESULT hr = S_OK; + + IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(ct), &pRecord)); + + if(! IsNilToken(tkImplementation)) + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ExportedType, ExportedTypeRec::COL_Implementation, + pRecord, tkImplementation)); + if (! IsNilToken(tkTypeDef)) + { + _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef); + pRecord->SetTypeDefId(tkTypeDef); + } + if (dwExportedTypeFlags != UINT32_MAX) + pRecord->SetFlags(dwExportedTypeFlags); + + IfFailGo(UpdateENCLog(ct)); +ErrExit: + return hr; +} // RegMeta::_SetExportedTypeProps + +//******************************************************************************* +// Helper: Set the specified attributes on the given ManifestResource token. +//******************************************************************************* +HRESULT RegMeta::_SetManifestResourceProps(// S_OK or error. + mdManifestResource mr, // [IN] ManifestResource token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource. + DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file. + DWORD dwResourceFlags) // [IN] Flags. +{ + ManifestResourceRec *pRecord = NULL; + HRESULT hr = S_OK; + + IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(mr), &pRecord)); + + // Set the attributes. + if (tkImplementation != mdTokenNil) + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ManifestResource, + ManifestResourceRec::COL_Implementation, pRecord, tkImplementation)); + if (dwOffset != UINT32_MAX) + pRecord->SetOffset(dwOffset); + if (dwResourceFlags != UINT32_MAX) + pRecord->SetFlags(dwResourceFlags); + + IfFailGo(UpdateENCLog(mr)); + +ErrExit: + return hr; +} // RegMeta::_SetManifestResourceProps + +#endif //FEATURE_METADATA_EMIT diff --git a/src/coreclr/md/compiler/classfactory.cpp b/src/coreclr/md/compiler/classfactory.cpp new file mode 100644 index 00000000000..46b9775e440 --- /dev/null +++ b/src/coreclr/md/compiler/classfactory.cpp @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// ClassFactory.cpp +// + +// +// Dll* routines for entry points, and support for COM framework. The class +// factory and other routines live in this module. +// +// This file is not included in the standalone metadata version, because standalone can't use COM, +// let alone COM-activation. So this file gets linked into mscorwks.dll, and then the mscorwks +// class factory delegates to this co-creation routine. +// +//***************************************************************************** +#include "stdafx.h" + +#ifdef FEATURE_METADATA_IN_VM + +#include "classfactory.h" +#include "disp.h" +#include "regmeta.h" +#include "mscoree.h" +#include "corhost.h" + +// @telesto - why does Telesto export any Co-classes at all? + +// This map contains the list of coclasses which are exported from this module. +// NOTE: CLSID_CorMetaDataDispenser must be the first entry in this table! +const COCLASS_REGISTER g_CoClasses[] = +{ +// pClsid szProgID pfnCreateObject + { &CLSID_CorMetaDataDispenser, W("CorMetaDataDispenser"), Disp::CreateObject }, + { NULL, NULL, NULL } +}; + + +//***************************************************************************** +// Called by COM to get a class factory for a given CLSID. If it is one we +// support, instantiate a class factory object and prepare for create instance. +// +// Notes: +// This gets invoked from mscorwks's DllGetClassObject. +//***************************************************************************** +STDAPI MetaDataDllGetClassObject( // Return code. + REFCLSID rclsid, // The class to desired. + REFIID riid, // Interface wanted on class factory. + LPVOID FAR *ppv) // Return interface pointer here. +{ + MDClassFactory *pClassFactory; // To create class factory object. + const COCLASS_REGISTER *pCoClass; // Loop control. + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + // Scan for the right one. + for (pCoClass=g_CoClasses; pCoClass->pClsid; pCoClass++) + { + if (*pCoClass->pClsid == rclsid) + { + // Allocate the new factory object. + pClassFactory = new (nothrow) MDClassFactory(pCoClass); + if (!pClassFactory) + return (E_OUTOFMEMORY); + + // Pick the v-table based on the caller's request. + hr = pClassFactory->QueryInterface(riid, ppv); + + // Always release the local reference, if QI failed it will be + // the only one and the object gets freed. + pClassFactory->Release(); + break; + } + } + return hr; +} + + +//***************************************************************************** +// +//********** Class factory code. +// +//***************************************************************************** + + +//***************************************************************************** +// QueryInterface is called to pick a v-table on the co-class. +//***************************************************************************** +HRESULT STDMETHODCALLTYPE MDClassFactory::QueryInterface( + REFIID riid, + void **ppvObject) +{ + HRESULT hr; + + // Avoid confusion. + *ppvObject = NULL; + + // Pick the right v-table based on the IID passed in. + if (riid == IID_IUnknown) + *ppvObject = (IUnknown *) this; + else if (riid == IID_IClassFactory) + *ppvObject = (IClassFactory *) this; + + // If successful, add a reference for out pointer and return. + if (*ppvObject) + { + hr = S_OK; + AddRef(); + } + else + hr = E_NOINTERFACE; + return hr; +} + + +//***************************************************************************** +// CreateInstance is called to create a new instance of the coclass for which +// this class was created in the first place. The returned pointer is the +// v-table matching the IID if there. +//***************************************************************************** +HRESULT STDMETHODCALLTYPE MDClassFactory::CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppvObject) +{ + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + + // Avoid confusion. + *ppvObject = NULL; + _ASSERTE(m_pCoClass); + + // Aggregation is not supported by these objects. + if (pUnkOuter) + IfFailGo(CLASS_E_NOAGGREGATION); + + // Ask the object to create an instance of itself, and check the iid. + hr = (*m_pCoClass->pfnCreateObject)(riid, ppvObject); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +HRESULT STDMETHODCALLTYPE +MDClassFactory::LockServer( + BOOL fLock) +{ + // @FUTURE: Should we return E_NOTIMPL instead of S_OK? + return S_OK; +} + +#endif //FEATURE_METADATA_IN_VM diff --git a/src/coreclr/md/compiler/classfactory.h b/src/coreclr/md/compiler/classfactory.h new file mode 100644 index 00000000000..86417708fcf --- /dev/null +++ b/src/coreclr/md/compiler/classfactory.h @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// ClassFactory.h +// + +// +// Class factories are used by the pluming in COM to activate new objects. +// This module contains the class factory code to instantiate the debugger +// objects described in <cordb.h>. +// +//***************************************************************************** +#ifndef __ClassFactory__h__ +#define __ClassFactory__h__ + +#include "disp.h" + + +// This typedef is for a function which will create a new instance of an object. +typedef HRESULT (* PFN_CREATE_OBJ)(REFIID riid, void **ppvObject); + +//***************************************************************************** +// This structure is used to declare a global list of coclasses. The class +// factory object is created with a pointer to the correct one of these, so +// that when create instance is called, it can be created. +//***************************************************************************** +struct COCLASS_REGISTER +{ + const GUID *pClsid; // Class ID of the coclass. + LPCWSTR szProgID; // Prog ID of the class. + PFN_CREATE_OBJ pfnCreateObject; // Creation function for an instance. +}; + + + +//***************************************************************************** +// One class factory object satifies all of our clsid's, to reduce overall +// code bloat. +//***************************************************************************** +class MDClassFactory : + public IClassFactory +{ + MDClassFactory() { } // Can't use without data. + +public: + MDClassFactory(const COCLASS_REGISTER *pCoClass) + : m_cRef(1), m_pCoClass(pCoClass) + { } + + virtual ~MDClassFactory() {} + + // + // IUnknown methods. + // + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void **ppvObject); + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + LONG cRef = InterlockedDecrement(&m_cRef); + if (cRef <= 0) + delete this; + return (cRef); + } + + + // + // IClassFactory methods. + // + + virtual HRESULT STDMETHODCALLTYPE CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppvObject); + + virtual HRESULT STDMETHODCALLTYPE LockServer( + BOOL fLock); + + +private: + LONG m_cRef; // Reference count. + const COCLASS_REGISTER *m_pCoClass; // The class we belong to. +}; + + + +#endif // __ClassFactory__h__ diff --git a/src/coreclr/md/compiler/custattr.h b/src/coreclr/md/compiler/custattr.h new file mode 100644 index 00000000000..13df242481c --- /dev/null +++ b/src/coreclr/md/compiler/custattr.h @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// + +#ifndef __CustAttr__h__ +#define __CustAttr__h__ + +//#include "stdafx.h" +#include "corhdr.h" +#include "cahlprinternal.h" +#include "sarray.h" +#include "factory.h" + +//***************************************************************************** +// Argument parsing. The custom attributes may have ctor arguments, and may +// have named arguments. The arguments are defined by the following tables. +// +// These tables also include a member to contain the value of the argument, +// which is used at runtime. When parsing a given custom attribute, a copy +// of the argument descriptors is filled in with the values for the instance +// of the custom attribute. +// +// For each ctor arg, there is a CaArg struct, with the type. At runtime, +// a value is filled in for each ctor argument. +// +// For each named arg, there is a CaNamedArg struct, with the name of the +// argument, the expected type of the argument, if the type is an enum, +// the name of the enum. Also, at runtime, a value is filled in for +// each named argument found. +// +// Note that arrays and variants are not supported. +// +// At runtime, after the args have been parsed, the tag field of CaValue +// can be used to determine if a particular arg was given. +//***************************************************************************** +struct CaArg +{ + void InitEnum(CorSerializationType _enumType, INT64 _val = 0) + { + CaTypeCtor caType(SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, _enumType, NULL, 0); + Init(caType, _val); + } + void Init(CorSerializationType _type, INT64 _val = 0) + { + _ASSERTE(_type != SERIALIZATION_TYPE_ENUM); + _ASSERTE(_type != SERIALIZATION_TYPE_SZARRAY); + CaTypeCtor caType(_type); + Init(caType, _val); + } + void Init(CaType _type, INT64 _val = 0) + { + type = _type; + memset(&val, 0, sizeof(CaValue)); + val.i8 = _val; + } + + CaType type; + CaValue val; +}; + +struct CaNamedArg +{ + void InitI4FieldEnum(LPCSTR _szName, LPCSTR _szEnumName, INT64 _val = 0) + { + CaTypeCtor caType(SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_I4, _szEnumName, (ULONG)strlen(_szEnumName)); + Init(_szName, SERIALIZATION_TYPE_FIELD, caType, _val); + } + + void InitBoolField(LPCSTR _szName, INT64 _val = 0) + { + CaTypeCtor caType(SERIALIZATION_TYPE_BOOLEAN); + Init(_szName, SERIALIZATION_TYPE_FIELD, caType, _val); + } + + void Init(LPCSTR _szName, CorSerializationType _propertyOrField, CaType _type, INT64 _val = 0) + { + szName = _szName; + cName = _szName ? (ULONG)strlen(_szName) : 0; + propertyOrField = _propertyOrField; + type = _type; + + memset(&val, 0, sizeof(CaValue)); + val.i8 = _val; + } + + LPCSTR szName; + ULONG cName; + CorSerializationType propertyOrField; + CaType type; + CaValue val; +}; + +struct CaNamedArgCtor : public CaNamedArg +{ + CaNamedArgCtor() + { + memset(this, 0, sizeof(CaNamedArg)); + } +}; + +HRESULT ParseEncodedType( + CustomAttributeParser &ca, + CaType* pCaType); + +HRESULT ParseKnownCaArgs( + CustomAttributeParser &ca, // The Custom Attribute blob. + CaArg *pArgs, // Array of argument descriptors. + ULONG cArgs); // Count of argument descriptors. + +HRESULT ParseKnownCaNamedArgs( + CustomAttributeParser &ca, // The Custom Attribute blob. + CaNamedArg *pNamedArgs, // Array of argument descriptors. + ULONG cNamedArgs); // Count of argument descriptors. + +#endif diff --git a/src/coreclr/md/compiler/custattr_emit.cpp b/src/coreclr/md/compiler/custattr_emit.cpp new file mode 100644 index 00000000000..47589f93a24 --- /dev/null +++ b/src/coreclr/md/compiler/custattr_emit.cpp @@ -0,0 +1,2003 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// CustAttr_Emit.cpp +// + +// +// Implementation for the meta data custom attribute emit code (code:IMetaDataEmit). +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" +#include "mdperf.h" +#include "posterror.h" +#include "cahlprinternal.h" +#include "custattr.h" +#include "corhdr.h" +#include <metamodelrw.h> + +#ifdef FEATURE_METADATA_EMIT + +//***************************************************************************** +//***************************************************************************** +// Support for "Pseudo Custom Attributes" + + +//***************************************************************************** +// Enumeration of known custom attributes. +//***************************************************************************** +#define KnownCaList() \ + KnownCa(UNKNOWN) \ + KnownCa(DllImportAttribute) \ + KnownCa(GuidAttribute) \ + KnownCa(ComImportAttribute) \ + KnownCa(InterfaceTypeAttribute) \ + KnownCa(ClassInterfaceAttribute) \ + KnownCa(SerializableAttribute) \ + KnownCa(NonSerializedAttribute) \ + KnownCa(MethodImplAttribute1) \ + KnownCa(MethodImplAttribute2) \ + KnownCa(MethodImplAttribute3) \ + KnownCa(MarshalAsAttribute1) \ + KnownCa(MarshalAsAttribute2) \ + KnownCa(PreserveSigAttribute) \ + KnownCa(InAttribute) \ + KnownCa(OutAttribute) \ + KnownCa(OptionalAttribute) \ + KnownCa(StructLayoutAttribute1) \ + KnownCa(StructLayoutAttribute2) \ + KnownCa(FieldOffsetAttribute) \ + KnownCa(TypeLibVersionAttribute) \ + KnownCa(ComCompatibleVersionAttribute) \ + KnownCa(SpecialNameAttribute) \ + KnownCa(AllowPartiallyTrustedCallersAttribute) \ + KnownCa(WindowsRuntimeImportAttribute) \ + +// Ids for the CA's. CA_DllImportAttribute, etc. +#define KnownCa(x) CA_##x, +enum { + KnownCaList() + CA_COUNT +}; + + +//***************************************************************************** +// Properties of the known custom attributes. +// +// These tables describe the known custom attributes. For each custom +// attribute, we know the namespace and name of the custom attribute, +// the types to which the CA applies, the ctor args, and possible named +// args. There is a flag which specifies whether the custom attribute +// should be kept, in addition to any processing done with the data. +//***************************************************************************** +const BOOL bKEEPCA = TRUE; +const BOOL bDONTKEEPCA = FALSE; +const BOOL bMATCHBYSIG = TRUE; +const BOOL bMATCHBYNAME = FALSE; + +struct KnownCaProp +{ + LPCUTF8 szNamespace; // Namespace of the custom attribute. + LPCUTF8 szName; // Name of the custom attribute. + const mdToken * rTypes; // Types that the CA applies to. + BOOL bKeepCa; // Keep the CA after processing? + const CaArg * pArgs; // List of ctor argument descriptors. + ULONG cArgs; // Count of ctor argument descriptors. + const CaNamedArg * pNamedArgs; // List of named arg descriptors. + ULONG cNamedArgs; // Count of named arg descriptors. + BOOL bMatchBySig; // For overloads; match by sig, not just name. + // WARNING: All overloads need the flag! +}; + +// Recognized targets for known custom attributes. +// If Target includes mdtAssembly, then make sure to include mdtTypeRef as well, +// aLink uses mdtTypeRef target temporarily for assembly target attributes +const mdToken DllImportTargets[] = {mdtMethodDef, (ULONG32) -1}; +const mdToken GuidTargets[] = {mdtTypeDef, mdtTypeRef, mdtModule, mdtAssembly, (ULONG32) -1}; +const mdToken ComImportTargets[] = {mdtTypeDef, (ULONG32) -1}; +const mdToken InterfaceTypeTargets[] = {mdtTypeDef, (ULONG32) -1}; +const mdToken ClassInterfaceTargets[] = {mdtTypeDef, mdtAssembly, mdtTypeRef, (ULONG32) -1}; +const mdToken SerializableTargets[] = {mdtTypeDef, (ULONG32) -1}; +const mdToken NotInGCHeapTargets[] = {mdtTypeDef, (ULONG32) -1}; +const mdToken NonSerializedTargets[] = {mdtFieldDef, (ULONG32) -1}; +const mdToken MethodImplTargets[] = {mdtMethodDef, (ULONG32) -1}; +const mdToken MarshalTargets[] = {mdtFieldDef, mdtParamDef, mdtProperty, (ULONG32) -1}; +const mdToken PreserveSigTargets[] = {mdtMethodDef, (ULONG32) -1}; +const mdToken InOutTargets[] = {mdtParamDef, (ULONG32) -1}; +const mdToken StructLayoutTargets[] = {mdtTypeDef, (ULONG32) -1}; +const mdToken FieldOffsetTargets[] = {mdtFieldDef, (ULONG32) -1}; +const mdToken TypeLibVersionTargets[] = {mdtAssembly, mdtTypeRef,(ULONG32) -1}; +const mdToken ComCompatibleVersionTargets[] = {mdtAssembly, mdtTypeRef, (ULONG32) -1}; +const mdToken SpecialNameTargets[] = {mdtTypeDef, mdtMethodDef, mdtFieldDef, mdtProperty, mdtEvent, (ULONG32) -1}; +const mdToken AllowPartiallyTrustedCallersTargets[] = {mdtAssembly, mdtTypeRef, (ULONG32) -1}; +const mdToken WindowsRuntimeImportTargets[] = {mdtTypeDef, (ULONG32) -1}; + + +//#ifndef CEE_CALLCONV +// # define CEE_CALLCONV (IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS) +//#endif + +#define DEFINE_CA_CTOR_ARGS(NAME) \ + const CaArg r##NAME##Args[] = \ + { +#define DEFINE_CA_CTOR_ARG(TYPE) {{TYPE}}, +#define DEFINE_CA_CTOR_ARGS_END() \ + }; + + + +#define DEFINE_CA_NAMED_ARGS(NAME) \ + const CaNamedArg r##NAME##NamedArgs[] = \ + { + +#define DEFINE_CA_NAMED_ARG(NAME, PROPORFIELD, TYPE, ARRAYTYPE, ENUMTYPE, ENUMNAME) \ + { NAME, sizeof(NAME) - 1, PROPORFIELD, { TYPE, ARRAYTYPE, ENUMTYPE, ENUMNAME, sizeof(ENUMNAME) - 1 } }, + +#define DEFINE_CA_NAMED_PROP_I4ENUM(NAME, ENUM) \ + DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_PROPERTY, SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_I4, ENUM) +#define DEFINE_CA_NAMED_FIELD_I4ENUM(NAME, ENUM) \ + DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_FIELD, SERIALIZATION_TYPE_ENUM, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_I4, ENUM) +#define DEFINE_CA_NAMED_PROP(NAME, TYPE) \ + DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_PROPERTY, TYPE, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_UNDEFINED, "") +#define DEFINE_CA_NAMED_FIELD(NAME, TYPE) \ + DEFINE_CA_NAMED_ARG(NAME, SERIALIZATION_TYPE_FIELD, TYPE, SERIALIZATION_TYPE_UNDEFINED, SERIALIZATION_TYPE_UNDEFINED, "") +#define DEFINE_CA_NAMED_PROP_BOOL(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_BOOLEAN) +#define DEFINE_CA_NAMED_FIELD_BOOL(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_BOOLEAN) +#define DEFINE_CA_NAMED_PROP_STRING(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_STRING) +#define DEFINE_CA_NAMED_FIELD_STRING(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_STRING) +#define DEFINE_CA_NAMED_PROP_TYPE(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_TYPE) +#define DEFINE_CA_NAMED_FIELD_TYPE(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_TYPE) +#define DEFINE_CA_NAMED_PROP_I2(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_I2) +#define DEFINE_CA_NAMED_FIELD_I2(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_I2) +#define DEFINE_CA_NAMED_PROP_I4(NAME) DEFINE_CA_NAMED_PROP(NAME, SERIALIZATION_TYPE_I4) +#define DEFINE_CA_NAMED_FIELD_I4(NAME) DEFINE_CA_NAMED_FIELD(NAME, SERIALIZATION_TYPE_I4) + +#define DEFINE_CA_NAMED_ARGS_END() \ + }; + +//----------------------------------------------------------------------------- +// index 0 is used as a placeholder. +const KnownCaProp UNKNOWNProps = {0}; + +//----------------------------------------------------------------------------- +// DllImport args, named args, and known attribute properties. +DEFINE_CA_CTOR_ARGS(DllImportAttribute) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_STRING) +DEFINE_CA_CTOR_ARGS_END() + +// NOTE: Keep this enum in sync with the array of named arguments. +enum DllImportNamedArgs +{ + DI_CallingConvention, + DI_CharSet, + DI_EntryPoint, + DI_ExactSpelling, + DI_SetLastError, + DI_PreserveSig, + DI_BestFitMapping, + DI_ThrowOnUnmappableChar, + DI_COUNT +}; + +DEFINE_CA_NAMED_ARGS(DllImportAttribute) + DEFINE_CA_NAMED_FIELD_I4ENUM("CallingConvention", "System.Runtime.InteropServices.CallingConvention") + DEFINE_CA_NAMED_FIELD_I4ENUM("CharSet", "System.Runtime.InteropServices.CharSet") + DEFINE_CA_NAMED_FIELD_STRING("EntryPoint") + DEFINE_CA_NAMED_FIELD_BOOL("ExactSpelling") + DEFINE_CA_NAMED_FIELD_BOOL("SetLastError") + DEFINE_CA_NAMED_FIELD_BOOL("PreserveSig") + DEFINE_CA_NAMED_FIELD_BOOL("BestFitMapping") + DEFINE_CA_NAMED_FIELD_BOOL("ThrowOnUnmappableChar") +DEFINE_CA_NAMED_ARGS_END() + +const KnownCaProp DllImportAttributeProps = {"System.Runtime.InteropServices", "DllImportAttribute", DllImportTargets, bDONTKEEPCA, + rDllImportAttributeArgs, lengthof(rDllImportAttributeArgs), + rDllImportAttributeNamedArgs, lengthof(rDllImportAttributeNamedArgs)}; + +//----------------------------------------------------------------------------- +// GUID args, named args (none), and known attribute properties. +DEFINE_CA_CTOR_ARGS(GuidAttribute) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_STRING) +DEFINE_CA_CTOR_ARGS_END() + +const KnownCaProp GuidAttributeProps = {"System.Runtime.InteropServices", "GuidAttribute", GuidTargets, bKEEPCA, + rGuidAttributeArgs, lengthof(rGuidAttributeArgs)}; + +//----------------------------------------------------------------------------- +// ComImport args (none), named args (none), and known attribute properties. +const KnownCaProp ComImportAttributeProps = {"System.Runtime.InteropServices", "ComImportAttribute", ComImportTargets}; + +//----------------------------------------------------------------------------- +// Interface type args, named args (none), and known attribute properties. +DEFINE_CA_CTOR_ARGS(InterfaceTypeAttribute) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U2) +DEFINE_CA_CTOR_ARGS_END() + +const KnownCaProp InterfaceTypeAttributeProps = {"System.Runtime.InteropServices", "InterfaceTypeAttribute", InterfaceTypeTargets, bKEEPCA, + rInterfaceTypeAttributeArgs, lengthof(rInterfaceTypeAttributeArgs)}; + +//----------------------------------------------------------------------------- +// Class interface type args, named args (none), and known attribute properties. +DEFINE_CA_CTOR_ARGS(ClassInterfaceAttribute) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U2) +DEFINE_CA_CTOR_ARGS_END() + +const KnownCaProp ClassInterfaceAttributeProps = {"System.Runtime.InteropServices", "ClassInterfaceAttribute", ClassInterfaceTargets, bKEEPCA, + rClassInterfaceAttributeArgs, lengthof(rClassInterfaceAttributeArgs)}; + +//----------------------------------------------------------------------------- +// Serializable args (none), named args (none), and known attribute properties. +const KnownCaProp SerializableAttributeProps = {"System", "SerializableAttribute", SerializableTargets}; + +//----------------------------------------------------------------------------- +// NonSerialized args (none), named args (none), and known attribute properties. +const KnownCaProp NonSerializedAttributeProps = {"System", "NonSerializedAttribute", NonSerializedTargets}; + +//----------------------------------------------------------------------------- +// SpecialName args (none), named args (none), and known attribute properties. +const KnownCaProp SpecialNameAttributeProps = {"System.Runtime.CompilerServices", "SpecialNameAttribute", SpecialNameTargets, bDONTKEEPCA}; + +//----------------------------------------------------------------------------- +// WindowsRuntimeImport args (none), named args (none), and known attribute properties. +const KnownCaProp WindowsRuntimeImportAttributeProps = {"System.Runtime.InteropServices.WindowsRuntime", "WindowsRuntimeImportAttribute", WindowsRuntimeImportTargets}; + + +//----------------------------------------------------------------------------- +// MethodImpl #1 args (none), named args, and known attribute properties. +// MethodImpl #2 args (short), named args, and known attribute properties. +// MethodImpl #3 args (enum), named args, and known attribute properties. +// Note: first two match by signature; third by name only, because signature matching code is not +// strong enough for enums. +DEFINE_CA_CTOR_ARGS(MethodImplAttribute2) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I2) +DEFINE_CA_CTOR_ARGS_END() + +DEFINE_CA_CTOR_ARGS(MethodImplAttribute3) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U4) +DEFINE_CA_CTOR_ARGS_END() + +enum MethodImplAttributeNamedArgs +{ + MI_CodeType, + MI_COUNT +}; + +DEFINE_CA_NAMED_ARGS(MethodImplAttribute) + DEFINE_CA_NAMED_FIELD_I4ENUM("MethodCodeType", "System.Runtime.CompilerServices.MethodCodeType") +DEFINE_CA_NAMED_ARGS_END() + +const KnownCaProp MethodImplAttribute1Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA, + 0, 0, + rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs), + bMATCHBYSIG}; +const KnownCaProp MethodImplAttribute2Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA, + rMethodImplAttribute2Args, lengthof(rMethodImplAttribute2Args), + rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs), + bMATCHBYSIG}; +const KnownCaProp MethodImplAttribute3Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA, + rMethodImplAttribute3Args, lengthof(rMethodImplAttribute3Args), + rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs), + bMATCHBYNAME}; + +//----------------------------------------------------------------------------- +// Marshal args, named args, and known attribute properties. +DEFINE_CA_CTOR_ARGS(MarshalAsAttribute2) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U4) +DEFINE_CA_CTOR_ARGS_END() + +DEFINE_CA_CTOR_ARGS(MarshalAsAttribute1) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I2) +DEFINE_CA_CTOR_ARGS_END() + +// NOTE: Keep this enum in sync with the array of named arguments. +enum MarshalNamedArgs +{ + M_ArraySubType, + M_SafeArraySubType, + M_SafeArrayUserDefinedSubType, + M_SizeParamIndex, + M_SizeConst, + M_MarshalType, + M_MarshalTypeRef, + M_MarshalCookie, + M_IidParameterIndex, + M_COUNT +}; + +DEFINE_CA_NAMED_ARGS(MarshalAsAttribute) + DEFINE_CA_NAMED_FIELD_I4ENUM("ArraySubType", "System.Runtime.InteropServices.UnmanagedType") + DEFINE_CA_NAMED_FIELD_I4ENUM("SafeArraySubType", "System.Runtime.InteropServices.VarEnum") + DEFINE_CA_NAMED_FIELD_TYPE("SafeArrayUserDefinedSubType") + DEFINE_CA_NAMED_FIELD_I2("SizeParamIndex") + DEFINE_CA_NAMED_FIELD_I4("SizeConst") + DEFINE_CA_NAMED_FIELD_STRING("MarshalType") + DEFINE_CA_NAMED_FIELD_TYPE("MarshalTypeRef") + DEFINE_CA_NAMED_FIELD_STRING("MarshalCookie") + DEFINE_CA_NAMED_FIELD_I4("IidParameterIndex") +DEFINE_CA_NAMED_ARGS_END() + +const KnownCaProp MarshalAsAttribute1Props = {"System.Runtime.InteropServices", "MarshalAsAttribute", MarshalTargets, bDONTKEEPCA, + rMarshalAsAttribute1Args, lengthof(rMarshalAsAttribute1Args), + rMarshalAsAttributeNamedArgs, lengthof(rMarshalAsAttributeNamedArgs), + bMATCHBYSIG}; + +const KnownCaProp MarshalAsAttribute2Props = {"System.Runtime.InteropServices", "MarshalAsAttribute", MarshalTargets, bDONTKEEPCA, + rMarshalAsAttribute2Args, lengthof(rMarshalAsAttribute2Args), + rMarshalAsAttributeNamedArgs, lengthof(rMarshalAsAttributeNamedArgs), + bMATCHBYNAME}; + +//----------------------------------------------------------------------------- +// PreserveSignature args, named args (none), and known attribute properties. +const KnownCaProp PreserveSigAttributeProps = {"System.Runtime.InteropServices", "PreserveSigAttribute", PreserveSigTargets, bDONTKEEPCA}; + +//----------------------------------------------------------------------------- +// In args (none), named args (none), and known attribute properties. +const KnownCaProp InAttributeProps = {"System.Runtime.InteropServices", "InAttribute", InOutTargets}; + +//----------------------------------------------------------------------------- +// Out args (none), named args (none), and known attribute properties. +const KnownCaProp OutAttributeProps = {"System.Runtime.InteropServices", "OutAttribute", InOutTargets}; + +//----------------------------------------------------------------------------- +// Optional args (none), named args (none), and known attribute properties. +const KnownCaProp OptionalAttributeProps = {"System.Runtime.InteropServices", "OptionalAttribute", InOutTargets}; + +//----------------------------------------------------------------------------- +// StructLayout args, named args, and known attribute properties. +DEFINE_CA_CTOR_ARGS(StructLayoutAttribute2) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4) +DEFINE_CA_CTOR_ARGS_END() + +DEFINE_CA_CTOR_ARGS(StructLayoutAttribute1) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I2) +DEFINE_CA_CTOR_ARGS_END() + +// NOTE: Keep this enum in sync with the array of named arguments. +enum StructLayoutNamedArgs +{ + SL_Pack, + SL_Size, + SL_CharSet, + SL_COUNT +}; + +DEFINE_CA_NAMED_ARGS(StructLayoutAttribute) + DEFINE_CA_NAMED_FIELD_I4("Pack") + DEFINE_CA_NAMED_FIELD_I4("Size") + DEFINE_CA_NAMED_FIELD_I4ENUM("CharSet", "System.Runtime.InteropServices.CharSet") +DEFINE_CA_NAMED_ARGS_END() + +const KnownCaProp StructLayoutAttribute1Props = {"System.Runtime.InteropServices", "StructLayoutAttribute", StructLayoutTargets, bDONTKEEPCA, + rStructLayoutAttribute1Args, lengthof(rStructLayoutAttribute1Args), + rStructLayoutAttributeNamedArgs, lengthof(rStructLayoutAttributeNamedArgs), + bMATCHBYSIG}; +const KnownCaProp StructLayoutAttribute2Props = {"System.Runtime.InteropServices", "StructLayoutAttribute", StructLayoutTargets, bDONTKEEPCA, + rStructLayoutAttribute2Args, lengthof(rStructLayoutAttribute2Args), + rStructLayoutAttributeNamedArgs, lengthof(rStructLayoutAttributeNamedArgs), + bMATCHBYNAME}; + +//----------------------------------------------------------------------------- +// FieldOffset args, named args (none), and known attribute properties. +DEFINE_CA_CTOR_ARGS(FieldOffsetAttribute) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_U4) +DEFINE_CA_CTOR_ARGS_END() + +const KnownCaProp FieldOffsetAttributeProps = {"System.Runtime.InteropServices", "FieldOffsetAttribute", FieldOffsetTargets, bDONTKEEPCA, + rFieldOffsetAttributeArgs, lengthof(rFieldOffsetAttributeArgs)}; + +DEFINE_CA_CTOR_ARGS(TypeLibVersionAttribute) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4) +DEFINE_CA_CTOR_ARGS_END() + +const KnownCaProp TypeLibVersionAttributeProps = {"System.Runtime.InteropServices", "TypeLibVersionAttribute", TypeLibVersionTargets, bKEEPCA, + rTypeLibVersionAttributeArgs, lengthof(rTypeLibVersionAttributeArgs)}; + + +DEFINE_CA_CTOR_ARGS(ComCompatibleVersionAttribute) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4) + DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4) +DEFINE_CA_CTOR_ARGS_END() + +const KnownCaProp ComCompatibleVersionAttributeProps = {"System.Runtime.InteropServices", "ComCompatibleVersionAttribute", ComCompatibleVersionTargets, bKEEPCA, + rComCompatibleVersionAttributeArgs, lengthof(rComCompatibleVersionAttributeArgs)}; + + +//----------------------------------------------------------------------------- +// APTCA args (none), named args (none), and known attribute properties. +const KnownCaProp AllowPartiallyTrustedCallersAttributeProps = {"System.Security", "AllowPartiallyTrustedCallersAttribute", AllowPartiallyTrustedCallersTargets, bKEEPCA}; + + + +//----------------------------------------------------------------------------- +// Array of known custom attribute properties +#undef KnownCa +#define KnownCa(x) &x##Props, +const KnownCaProp * const rKnownCaProps[CA_COUNT] = +{ + KnownCaList() +}; + +//***************************************************************************** +// Helper to turn on or off a single bit in a bitmask. +//***************************************************************************** +template<class T> FORCEINLINE void SetBitValue(T &bitmask, T bit, int bVal) +{ + if (bVal) + bitmask |= bit; + else + bitmask &= ~bit; +} // template<class T> FORCEINLINE void SetBitValue() + +HRESULT ParseEncodedType( + CustomAttributeParser &ca, + CaType* pCaType) +{ + CONTRACTL + { + PRECONDITION(CheckPointer(pCaType)); + NOTHROW; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + CorSerializationType* pType = &pCaType->tag; + + IfFailGo(ca.GetTag(pType)); + + if (*pType == SERIALIZATION_TYPE_SZARRAY) + { + IfFailGo(ca.GetTag(&pCaType->arrayType)); + pType = &pCaType->arrayType; + } + + if (*pType == SERIALIZATION_TYPE_ENUM) + { + // We cannot determine the underlying type without loading the Enum. + pCaType->enumType = SERIALIZATION_TYPE_UNDEFINED; + IfFailGo(ca.GetNonNullString(&pCaType->szEnumName, &pCaType->cEnumName)); + } + +ErrExit: + return hr; +} + +//--------------------------------------------------------------------------------------- +// +// Helper to parse the values for the ctor argument list and the named argument list. +// + +HRESULT ParseKnownCaValue( + CustomAttributeParser &ca, + CaValue* pCaArg, + CaType* pCaParam) +{ + CONTRACTL + { + PRECONDITION(CheckPointer(pCaArg)); + PRECONDITION(CheckPointer(pCaParam)); + PRECONDITION(pCaParam->tag != SERIALIZATION_TYPE_TAGGED_OBJECT && pCaParam->tag != SERIALIZATION_TYPE_SZARRAY); + NOTHROW; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + CorSerializationType underlyingType; + + pCaArg->type = *pCaParam; + + underlyingType = pCaArg->type.tag == SERIALIZATION_TYPE_ENUM ? pCaArg->type.enumType : pCaArg->type.tag; + + // Grab the value. + switch (underlyingType) + { + case SERIALIZATION_TYPE_BOOLEAN: + case SERIALIZATION_TYPE_I1: + case SERIALIZATION_TYPE_U1: + IfFailGo(ca.GetU1(&pCaArg->u1)); + break; + + case SERIALIZATION_TYPE_CHAR: + case SERIALIZATION_TYPE_I2: + case SERIALIZATION_TYPE_U2: + IfFailGo(ca.GetU2(&pCaArg->u2)); + break; + + case SERIALIZATION_TYPE_I4: + case SERIALIZATION_TYPE_U4: + IfFailGo(ca.GetU4(&pCaArg->u4)); + break; + + case SERIALIZATION_TYPE_I8: + case SERIALIZATION_TYPE_U8: + IfFailGo(ca.GetU8(&pCaArg->u8)); + break; + + case SERIALIZATION_TYPE_R4: + IfFailGo(ca.GetR4(&pCaArg->r4)); + break; + + case SERIALIZATION_TYPE_R8: + IfFailGo(ca.GetR8(&pCaArg->r8)); + break; + + case SERIALIZATION_TYPE_STRING: + case SERIALIZATION_TYPE_TYPE: + IfFailGo(ca.GetString(&pCaArg->str.pStr, &pCaArg->str.cbStr)); + break; + + default: + // The arguments of all known custom attributes are Type, String, Enum, or primitive types. + _ASSERTE(!"Unexpected internal error"); + hr = E_FAIL; + break; + } // End switch + +ErrExit: + return hr; +} + +//--------------------------------------------------------------------------------------- +// +// Helper to parse the nanmed argument list. +// This function is used by code:RegMeta::DefineCustomAttribute for IMetaDataEmit2 and +// should not have any VM dependency. +// + +HRESULT ParseKnownCaNamedArgs( + CustomAttributeParser &ca, // The Custom Attribute blob. + CaNamedArg *pNamedParams, // Array of argument descriptors. + ULONG cNamedParams) +{ + WRAPPER_NO_CONTRACT; + + HRESULT hr = S_OK; + ULONG ixParam; + INT32 ixArg; + INT16 cActualArgs; + CaNamedArgCtor namedArg; + CaNamedArg* pNamedParam; + + // Get actual count of named arguments. + if (FAILED(ca.GetI2(&cActualArgs))) + cActualArgs = 0; // Everett behavior + + for (ixParam = 0; ixParam < cNamedParams; ixParam++) + pNamedParams[ixParam].val.type.tag = SERIALIZATION_TYPE_UNDEFINED; + + // For each named argument... + for (ixArg = 0; ixArg < cActualArgs; ixArg++) + { + // Field or property? + IfFailGo(ca.GetTag(&namedArg.propertyOrField)); + if (namedArg.propertyOrField != SERIALIZATION_TYPE_FIELD && namedArg.propertyOrField != SERIALIZATION_TYPE_PROPERTY) + IfFailGo(PostError(META_E_CA_INVALID_ARGTYPE)); + + // Get argument type information + IfFailGo(ParseEncodedType(ca, &namedArg.type)); + + // Get name of Arg. + if (FAILED(ca.GetNonEmptyString(&namedArg.szName, &namedArg.cName))) + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + + // Match arg by name and type + for (ixParam = 0; ixParam < cNamedParams; ixParam++) + { + pNamedParam = &pNamedParams[ixParam]; + + // Match type + if (pNamedParam->type.tag != SERIALIZATION_TYPE_TAGGED_OBJECT) + { + if (namedArg.type.tag != pNamedParam->type.tag) + continue; + + // Match array type + if (namedArg.type.tag == SERIALIZATION_TYPE_SZARRAY && + pNamedParam->type.arrayType != SERIALIZATION_TYPE_TAGGED_OBJECT && + namedArg.type.arrayType != pNamedParam->type.arrayType) + continue; + } + + // Match name (and its length to avoid substring matching) + if ((pNamedParam->cName != namedArg.cName) || + (strncmp(pNamedParam->szName, namedArg.szName, namedArg.cName) != 0)) + { + continue; + } + + // If enum, match enum name. + if (pNamedParam->type.tag == SERIALIZATION_TYPE_ENUM || + (pNamedParam->type.tag == SERIALIZATION_TYPE_SZARRAY && pNamedParam->type.arrayType == SERIALIZATION_TYPE_ENUM )) + { + if (pNamedParam->type.cEnumName > namedArg.type.cEnumName) + continue; // name cannot possibly match + + if (strncmp(pNamedParam->type.szEnumName, namedArg.type.szEnumName, pNamedParam->type.cEnumName) != 0 || + (pNamedParam->type.cEnumName < namedArg.type.cEnumName && + namedArg.type.szEnumName[pNamedParam->type.cEnumName] != ',')) + continue; + + // TODO: For now assume the property\field array size is correct - later we should verify this + namedArg.type.enumType = pNamedParam->type.enumType; + } + + // Found a match. + break; + } + + // Better have found an argument. + if (ixParam == cNamedParams) + { + MAKE_WIDEPTR_FROMUTF8N(pWideStr, namedArg.szName, namedArg.cName) + IfFailGo(PostError(META_E_CA_UNKNOWN_ARGUMENT, wcslen(pWideStr), pWideStr)); + } + + // Argument had better not have been seen already. + if (pNamedParams[ixParam].val.type.tag != SERIALIZATION_TYPE_UNDEFINED) + { + MAKE_WIDEPTR_FROMUTF8N(pWideStr, namedArg.szName, namedArg.cName) + IfFailGo(PostError(META_E_CA_REPEATED_ARG, wcslen(pWideStr), pWideStr)); + } + + IfFailGo(ParseKnownCaValue(ca, &pNamedParams[ixParam].val, &namedArg.type)); + } + +ErrExit: + return hr; +} + +//--------------------------------------------------------------------------------------- +// +// Helper to parse the ctor argument list. +// This function is used by code:RegMeta::DefineCustomAttribute for IMetaDataEmit2 and +// should not have any VM dependency. +// + +HRESULT ParseKnownCaArgs( + CustomAttributeParser &ca, // The Custom Attribute blob. + CaArg* pArgs, // Array of argument descriptors. + ULONG cArgs) // Count of argument descriptors. +{ + WRAPPER_NO_CONTRACT; + + HRESULT hr = S_OK; // A result. + ULONG ix; // Loop control. + + // If there is a blob, check the prolog. + if (FAILED(ca.ValidateProlog())) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + + // For each expected arg... + for (ix=0; ix<cArgs; ++ix) + { + CaArg* pArg = &pArgs[ix]; + IfFailGo(ParseKnownCaValue(ca, &pArg->val, &pArg->type)); + } + +ErrExit: + return hr; +} // ParseKnownCaArgs + +//***************************************************************************** +// Create a CustomAttribute record from a blob with the specified parent. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineCustomAttribute( + mdToken tkOwner, // [IN] The object to put the value on. + mdToken tkCtor, // [IN] Constructor of the CustomAttribute type (MemberRef/MethodDef). + void const *pCustomAttribute, // [IN] Custom Attribute data. + ULONG cbCustomAttribute, // [IN] Size of custom Attribute data. + mdCustomAttribute *pcv) // [OUT, OPTIONAL] Put custom Attribute token here. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CustomAttributeRec *pRecord = NULL; // New custom Attribute record. + RID iRecord; // New custom Attribute RID. + CMiniMdRW *pMiniMd = &m_pStgdb->m_MiniMd; + int ixKnown; // Index of known custom attribute. + + LOG((LOGMD, "RegMeta::DefineCustomAttribute(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", tkOwner, tkCtor, + pCustomAttribute, cbCustomAttribute, pcv)); + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(TypeFromToken(tkCtor) == mdtMethodDef || TypeFromToken(tkCtor) == mdtMemberRef); + + if (TypeFromToken(tkOwner) == mdtCustomAttribute) + IfFailGo(E_INVALIDARG); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + if (IsNilToken(tkOwner) || + IsNilToken(tkCtor) || + (TypeFromToken(tkCtor) != mdtMethodDef && + TypeFromToken(tkCtor) != mdtMemberRef) ) + { + IfFailGo(E_INVALIDARG); + } + + // See if this is a known custom attribute. + IfFailGo(_IsKnownCustomAttribute(tkCtor, &ixKnown)); + if (ixKnown != 0) + { + int bKeep = false; + hr = _HandleKnownCustomAttribute(tkOwner, pCustomAttribute, cbCustomAttribute, ixKnown, &bKeep); + if (pcv) + *pcv = mdCustomAttributeNil; + IfFailGo(hr); + if (!bKeep) + goto ErrExit; + } + + if (((TypeFromToken(tkOwner) == mdtTypeDef) || (TypeFromToken(tkOwner) == mdtMethodDef)) && + (TypeFromToken(tkCtor) == mdtMethodDef || TypeFromToken(tkCtor) == mdtMemberRef)) + { + CHAR szBuffer[MAX_CLASS_NAME + 1]; + LPSTR szName = szBuffer; + LPCSTR szNamespace; + LPCSTR szClass; + TypeRefRec *pTypeRefRec = NULL; + TypeDefRec *pTypeDefRec = NULL; + mdToken tkParent; + + if (TypeFromToken(tkCtor) == mdtMemberRef) + { + MemberRefRec *pMemberRefRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCtor), &pMemberRefRec)); + tkParent = pMiniMd->getClassOfMemberRef(pMemberRefRec); + if (TypeFromToken(tkParent) == mdtTypeRef) + { + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkParent), &pTypeRefRec)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szClass)); + ns::MakePath(szName, sizeof(szBuffer) - 1, szNamespace, szClass); + } + else if (TypeFromToken(tkParent) == mdtTypeDef) + { + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec)); + } + } + else + { + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkCtor, &tkParent)); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec)); + } + + if (pTypeDefRec != NULL) + { + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szClass)); + ns::MakePath(szName, sizeof(szBuffer) - 1, szNamespace, szClass); + } + + if ((TypeFromToken(tkOwner) == mdtMethodDef) && strcmp(szName, COR_REQUIRES_SECOBJ_ATTRIBUTE_ANSI) == 0) + { + // Turn REQ_SO attribute into flag bit on the methoddef. + MethodRec *pMethod; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkOwner), &pMethod)); + pMethod->AddFlags(mdRequireSecObject); + IfFailGo(UpdateENCLog(tkOwner)); + goto ErrExit; + } + else if (strcmp(szName, COR_SUPPRESS_UNMANAGED_CODE_CHECK_ATTRIBUTE_ANSI) == 0) + { + // If we spot an unmanged code check suppression attribute, turn on + // the bit that says there's declarative security on the + // class/method, but still write the attribute itself. + if (TypeFromToken(tkOwner) == mdtTypeDef) + { + IfFailGo(_TurnInternalFlagsOn(tkOwner, tdHasSecurity)); + } + else if (TypeFromToken(tkOwner) == mdtMethodDef) + { + IfFailGo(_TurnInternalFlagsOn(tkOwner, mdHasSecurity)); + } + IfFailGo(UpdateENCLog(tkOwner)); + } + } + + IfFailGo(m_pStgdb->m_MiniMd.AddCustomAttributeRecord(&pRecord, &iRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Type, pRecord, tkCtor)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, pRecord, tkOwner)); + + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_CustomAttribute, CustomAttributeRec::COL_Value, pRecord, pCustomAttribute, cbCustomAttribute)); + + // Give token back to caller. + if (pcv != NULL) + *pcv = TokenFromRid(iRecord, mdtCustomAttribute); + + IfFailGo(m_pStgdb->m_MiniMd.AddCustomAttributesToHash(TokenFromRid(iRecord, mdtCustomAttribute)) ); + + IfFailGo(UpdateENCLog(TokenFromRid(iRecord, mdtCustomAttribute))); + +ErrExit: + STOP_MD_PERF(DefineCustomAttribute); + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineCustomAttribute + +//***************************************************************************** +// Replace the blob of an existing custom attribute. +//***************************************************************************** +STDMETHODIMP RegMeta::SetCustomAttributeValue( // Return code. + mdCustomAttribute tkAttr, // [IN] The object to be Attributed. + void const *pCustomAttribute, // [IN] Custom Attribute data. + ULONG cbCustomAttribute) // [IN] Size of custom Attribute data. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + CustomAttributeRec *pRecord = NULL;// Existing custom Attribute record. + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tkAttr) == mdtCustomAttribute && !InvalidRid(tkAttr)); + + // Retrieve and update the custom value. + IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkAttr), &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_CustomAttribute, CustomAttributeRec::COL_Value, pRecord, pCustomAttribute, cbCustomAttribute)); + + IfFailGo(UpdateENCLog(tkAttr)); +ErrExit: + + STOP_MD_PERF(SetCustomAttributeValue); + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetCustomAttributeValue + +//***************************************************************************** +//***************************************************************************** +HRESULT RegMeta::_IsKnownCustomAttribute( // S_OK, S_FALSE, or error. + mdToken tkCtor, // [IN] Token of custom attribute's constructor. + int *pca) // [OUT] Put value from KnownCustAttr enum here. +{ + HRESULT hr = S_OK; // A result. + CCustAttrHashKey sLookup; // For looking up a custom attribute. + CCustAttrHashKey *pFound; // Result of a lookup. + LPCSTR szNamespace = ""; // Namespace of custom attribute type. + LPCSTR szName = ""; // Name of custom attribute type. + TypeDefRec *pTypeDefRec = NULL; // Parent record, when a TypeDef. + TypeRefRec *pTypeRefRec = NULL; // Parent record, when a TypeRef. + CMiniMdRW *pMiniMd = &m_pStgdb->m_MiniMd; + int ixCa; // Index of Known CustomAttribute, or 0. + int i; // Loop control. + mdToken tkParent; + + *pca = 0; + + // Only for Custom Attributes. + _ASSERTE(TypeFromToken(tkCtor) != mdtTypeRef && TypeFromToken(tkCtor) != mdtTypeDef); + + sLookup.tkType = tkCtor; + + // See if this custom attribute type has been seen before. + if ((pFound = m_caHash.Find(&sLookup))) + { // Yes, already seen. + *pca = pFound->ca; + hr = (pFound->ca == CA_UNKNOWN) ? S_FALSE : S_OK; + goto ErrExit; + } + + // Hasn't been seen before. See if it is well known. + + // Get the CA name. + if (TypeFromToken(tkCtor) == mdtMemberRef) + { + MemberRefRec *pMember; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCtor), &pMember)); + tkParent = pMiniMd->getClassOfMemberRef(pMember); + if (TypeFromToken(tkParent) == mdtTypeRef) + { + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkParent), &pTypeRefRec)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName)); + } + else if (TypeFromToken(tkParent) == mdtTypeDef) + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec)); + } + else + { + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkCtor, &tkParent)); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec)); + } + + if (pTypeDefRec) + { + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName)); + } + + // Search in list of Known CAs. + for (ixCa=0, i=1; i<CA_COUNT; ++i) + { + if (strcmp(szName, rKnownCaProps[i]->szName) != 0) + continue; + if (strcmp(szNamespace, rKnownCaProps[i]->szNamespace) == 0) + { + // Some custom attributes have overloaded ctors. For those, + // see if this is the matching overload. + if (rKnownCaProps[i]->bMatchBySig) + { + // Name matches. Does the signature? + PCCOR_SIGNATURE pSig = NULL; // Signature of a method. + ULONG cbSig = 0; // Size of the signature. + ULONG cParams; // Count of signature parameters. + ULONG cb; // Size of an element + ULONG elem; // Signature element. + ULONG j; // Loop control. + + // Get the signature. + if (TypeFromToken(tkCtor) == mdtMemberRef) + { + MemberRefRec *pMember; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCtor), &pMember)); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pMember, &pSig, &cbSig)); + } + else + { + MethodRec *pMethod; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkCtor), &pMethod)); + IfFailGo(pMiniMd->getSignatureOfMethod(pMethod, &pSig, &cbSig)); + } + + // Skip calling convention. + cb = CorSigUncompressData(pSig, &elem); + pSig += cb; + cbSig -= cb; + // Count of params. + cb = CorSigUncompressData(pSig, &cParams); + pSig += cb; + cbSig -= cb; + + // If param count mismatch, not the right CA. + if (cParams != rKnownCaProps[i]->cArgs) + continue; + + // Count is fine, check each param. Skip return type (better be void). + cb = CorSigUncompressData(pSig, &elem); + _ASSERTE(elem == ELEMENT_TYPE_VOID); + pSig += cb; + cbSig -= cb; + for (j=0; j<cParams; ++j) + { + // Get next element from method signature. + cb = CorSigUncompressData(pSig, &elem); + pSig += cb; + cbSig -= cb; + if (rKnownCaProps[i]->pArgs[j].type.tag != (CorSerializationType) elem) + break; + } + + // All matched? + if (j != cParams) + continue; + } + // All matched. + ixCa = i; + break; + } + } + + // Add to hash. + sLookup.ca = ixCa; + pFound = m_caHash.Add(&sLookup); + IfNullGo(pFound); + *pFound = sLookup; + *pca = ixCa; + +ErrExit: + return hr; +} // RegMeta::_IsKnownCustomAttribute + +//***************************************************************************** +//***************************************************************************** +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::_HandleKnownCustomAttribute( // S_OK or error. + mdToken tkObj, // [IN] Object being attributed. + const void *pData, // [IN] Custom Attribute data blob. + ULONG cbData, // [IN] Count of bytes in the data. + int ixCa, // [IN] Value from KnownCustAttr enum. + int *bKeep) // [OUT] If true, keep the CA after processing. +{ + HRESULT hr = S_OK; // A result. + ULONG ixTbl; // Index of table with object. + void *pRow; // Whatever sort of record it is. + CMiniMdRW *pMiniMd = &m_pStgdb->m_MiniMd; + mdToken tkObjType; // Type of the object. + ULONG ix; // Loop control. + KnownCaProp const *props=rKnownCaProps[ixCa]; // For convenience. + CustomAttributeParser ca(pData, cbData); + CQuickArray<CaArg> qArgs; // Un-named arguments. + CQuickArray<CaNamedArg> qNamedArgs; // Named arguments. + CQuickArray<BYTE> qNativeType;// Native type string. + + _ASSERTE(ixCa > 0 && ixCa < CA_COUNT); + *bKeep = props->bKeepCa || m_bKeepKnownCa; + + // Validate that target is valid for attribute. + tkObjType = TypeFromToken(tkObj); + for (ix=0; props->rTypes[ix] != (mdToken) -1; ++ix) + { + if (props->rTypes[ix] == tkObjType) + break; + } + // Was the type found in list of valid targets? + if (props->rTypes[ix] == (mdToken) -1) + { // No, error. + IfFailGo(PostError(META_E_CA_INVALID_TARGET)); + } + // Get the row. + ixTbl = pMiniMd->GetTblForToken(tkObj); + _ASSERTE(ixTbl >= 0 && ixTbl <= pMiniMd->GetCountTables()); + IfFailGo(pMiniMd->getRow(ixTbl, RidFromToken(tkObj), &pRow)); + + // If this custom attribute expects any args... + if (props->cArgs || props->cNamedArgs) + { // Initialize array ctor arg descriptors. + IfFailGo(qArgs.ReSizeNoThrow(props->cArgs)); + for (ix=0; ix<props->cArgs; ++ix) + qArgs[ix] = props->pArgs[ix]; + // Parse any ctor args (unnamed, fixed args). + IfFailGo(ParseKnownCaArgs(ca, qArgs.Ptr(), props->cArgs)); + + // If this custom attribute accepts named args, parse them, or if there + // are unused bytes, parse them. + if (props->cNamedArgs || ca.BytesLeft() > 0) + { // Initialize array of named arg descriptors. + IfFailGo(qNamedArgs.ReSizeNoThrow(props->cNamedArgs)); + for (ix=0; ix<props->cNamedArgs; ++ix) + qNamedArgs[ix] = props->pNamedArgs[ix]; + // Parse named args. + IfFailGo(ParseKnownCaNamedArgs(ca, qNamedArgs.Ptr(), props->cNamedArgs)); + } + } + + switch (ixCa) + { + case CA_DllImportAttribute: + { + // Validate parameters. + if (qArgs[0].val.str.cbStr == 0 || qArgs[0].val.str.pStr == NULL) + { + // no name for DllImport. + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + } + + // Retrieve / create a ModuleRef on the dll name. + mdModuleRef mrModule; + CQuickArray<char> qDllName; + IfFailGo(qDllName.ReSizeNoThrow(qArgs[0].val.str.cbStr+1)); + memcpy(qDllName.Ptr(), qArgs[0].val.str.pStr, qArgs[0].val.str.cbStr); + qDllName[qArgs[0].val.str.cbStr] = '\0'; + hr = ImportHelper::FindModuleRef(pMiniMd, qDllName.Ptr(), &mrModule); + if (hr != S_OK) + { + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzDllName, qDllName.Ptr()); + if (wzDllName == NULL) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + IfFailGo(_DefineModuleRef(wzDllName, &mrModule)); + } + + // Create a p/invoke map entry. + ULONG dwFlags; dwFlags=0; + // Was a calling convention set? + if (qNamedArgs[DI_CallingConvention].val.type.tag) + { // Calling convention makes no sense on a field. + if (TypeFromToken(tkObj) == mdtFieldDef) + IfFailGo(PostError(META_E_CA_INVALID_ARG_FOR_TYPE, qNamedArgs[DI_CallingConvention].szName)); + // Turn off all callconv bits, then turn on specified value. + dwFlags &= ~pmCallConvMask; + switch (qNamedArgs[DI_CallingConvention].val.u4) + { //<TODO>@future: sigh. keep in sync with System.Runtime.InteropServices.CallingConvention</TODO> + case 0: break; // 0 means "do nothing" + case 1: dwFlags |= pmCallConvWinapi; break; + case 2: dwFlags |= pmCallConvCdecl; break; + case 3: dwFlags |= pmCallConvStdcall; break; + case 4: dwFlags |= pmCallConvThiscall; break; + case 5: dwFlags |= pmCallConvFastcall; break; + default: + _ASSERTE(!"Flags are out of sync! "); + break; + } + } + else + if (TypeFromToken(tkObj) == mdtMethodDef) + { // No calling convention specified for a method. Default to pmCallConvWinApi. + dwFlags = (dwFlags & ~pmCallConvMask) | pmCallConvWinapi; + } + + // Charset + if (qNamedArgs[DI_CharSet].val.type.tag) + { // Turn of all charset bits, then turn on specified bits. + dwFlags &= ~pmCharSetMask; + switch (qNamedArgs[DI_CharSet].val.u4) + { //<TODO>@future: keep in sync with System.Runtime.InteropServices.CharSet</TODO> + case 0: break; // 0 means "do nothing" + case 1: dwFlags |= pmCharSetNotSpec; break; + case 2: dwFlags |= pmCharSetAnsi; break; + case 3: dwFlags |= pmCharSetUnicode; break; + case 4: dwFlags |= pmCharSetAuto; break; + default: + _ASSERTE(!"Flags are out of sync! "); + break; + } + } + if (qNamedArgs[DI_ExactSpelling].val.u1) + dwFlags |= pmNoMangle; + if (qNamedArgs[DI_SetLastError].val.type.tag) + { // SetLastError makes no sense on a field. + if (TypeFromToken(tkObj) == mdtFieldDef) + IfFailGo(PostError(META_E_CA_INVALID_ARG_FOR_TYPE, qNamedArgs[DI_SetLastError].szName)); + if (qNamedArgs[DI_SetLastError].val.u1) + dwFlags |= pmSupportsLastError; + } + + // If an entrypoint name was specified, use it, otherwise grab the name from the member. + LPCWSTR wzEntry; + if (qNamedArgs[DI_EntryPoint].val.type.tag) + { + if (qNamedArgs[DI_EntryPoint].val.str.cbStr > 0) + { + MAKE_WIDEPTR_FROMUTF8N_NOTHROW(wzEntryName, qNamedArgs[DI_EntryPoint].val.str.pStr, qNamedArgs[DI_EntryPoint].val.str.cbStr); + if (wzEntryName == NULL) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + wzEntry = wzEntryName; + } + else + wzEntry = W(""); + } + else + { + LPCUTF8 szMember = NULL; + if (TypeFromToken(tkObj) == mdtMethodDef) + { + IfFailGo(pMiniMd->getNameOfMethod(reinterpret_cast<MethodRec*>(pRow), &szMember)); + } + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMemberName, szMember); + if (wzMemberName == NULL) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + wzEntry = wzMemberName; + } + + // Set the miPreserveSig bit based on the value of the preserve sig flag. + if (qNamedArgs[DI_PreserveSig].val.type.tag && !qNamedArgs[DI_PreserveSig].val.u1) + reinterpret_cast<MethodRec*>(pRow)->RemoveImplFlags(miPreserveSig); + else + reinterpret_cast<MethodRec*>(pRow)->AddImplFlags(miPreserveSig); + + if (qNamedArgs[DI_BestFitMapping].val.type.tag) + { + if (qNamedArgs[DI_BestFitMapping].val.u1) + dwFlags |= pmBestFitEnabled; + else + dwFlags |= pmBestFitDisabled; + } + + if (qNamedArgs[DI_ThrowOnUnmappableChar].val.type.tag) + { + if (qNamedArgs[DI_ThrowOnUnmappableChar].val.u1) + dwFlags |= pmThrowOnUnmappableCharEnabled; + else + dwFlags |= pmThrowOnUnmappableCharDisabled; + } + + // Finally, create the PInvokeMap entry., + IfFailGo(_DefinePinvokeMap(tkObj, dwFlags, wzEntry, mrModule)); + goto ErrExit; + } + break; + + case CA_GuidAttribute: + { // Just verify the attribute. It still gets stored as a real custom attribute. + // format is "{01234567-0123-0123-0123-001122334455}" + GUID guid; + WCHAR wzGuid[40]; + int cch = qArgs[0].val.str.cbStr; + + // Guid should be 36 characters; need to add curlies. + if (cch == 36) + { + WszMultiByteToWideChar(CP_UTF8, 0, qArgs[0].val.str.pStr,cch, wzGuid+1,39); + wzGuid[0] = '{'; + wzGuid[37] = '}'; + wzGuid[38] = 0; + hr = IIDFromString(wzGuid, &guid); + } + else + hr = META_E_CA_INVALID_UUID; + if (hr != S_OK) + IfFailGo(PostError(META_E_CA_INVALID_UUID)); + goto ErrExit; + } + break; + + case CA_ComImportAttribute: + reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdImport); + break; + + case CA_InterfaceTypeAttribute: + { + // Verify the attribute. + if (qArgs[0].val.u2 >= ifLast) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + } + break; + + case CA_ClassInterfaceAttribute: + { + // Verify the attribute. + if (qArgs[0].val.u2 >= clsIfLast) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + } + break; + + case CA_SpecialNameAttribute: + + switch (TypeFromToken(tkObj)) + { + case mdtTypeDef: + reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdSpecialName); + break; + + case mdtMethodDef: + reinterpret_cast<MethodRec*>(pRow)->AddFlags(mdSpecialName); + break; + + case mdtFieldDef: + reinterpret_cast<FieldRec*>(pRow)->AddFlags(fdSpecialName); + break; + + case mdtProperty: + reinterpret_cast<PropertyRec*>(pRow)->AddPropFlags(prSpecialName); + break; + + case mdtEvent: + reinterpret_cast<EventRec*>(pRow)->AddEventFlags(evSpecialName); + break; + + default: + _ASSERTE(!"Unfamilar type for SpecialName custom attribute"); + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + } + + break; + case CA_SerializableAttribute: + reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdSerializable); + break; + + case CA_NonSerializedAttribute: + reinterpret_cast<FieldRec*>(pRow)->AddFlags(fdNotSerialized); + break; + + case CA_InAttribute: + reinterpret_cast<ParamRec*>(pRow)->AddFlags(pdIn); + break; + + case CA_OutAttribute: + reinterpret_cast<ParamRec*>(pRow)->AddFlags(pdOut); + break; + + case CA_OptionalAttribute: + reinterpret_cast<ParamRec*>(pRow)->AddFlags(pdOptional); + break; + + case CA_MethodImplAttribute2: + // Force to wider value. + qArgs[0].val.u4 = (unsigned)qArgs[0].val.i2; + // Fall through to validation. + FALLTHROUGH; + case CA_MethodImplAttribute3: + // Validate bits. + if (qArgs[0].val.u4 & ~(miUserMask)) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + reinterpret_cast<MethodRec*>(pRow)->AddImplFlags(qArgs[0].val.u4); + if (!qNamedArgs[MI_CodeType].val.type.tag) + break; + // fall through to set the code type. + FALLTHROUGH; + case CA_MethodImplAttribute1: + { + USHORT usFlags = reinterpret_cast<MethodRec*>(pRow)->GetImplFlags(); + if (qNamedArgs[MI_CodeType].val.i4 & ~(miCodeTypeMask)) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + // Mask out old value, put in new one. + usFlags = (usFlags & ~miCodeTypeMask) | qNamedArgs[MI_CodeType].val.i4; + reinterpret_cast<MethodRec*>(pRow)->SetImplFlags(usFlags); + } + break; + + case CA_MarshalAsAttribute1: + // Force the U2 to a wider U4 value explicitly. + qArgs[0].val.u4 = qArgs[0].val.u2; + // Fall through to handle the CA. + FALLTHROUGH; + case CA_MarshalAsAttribute2: + IfFailGo(_HandleNativeTypeCustomAttribute(tkObj, qArgs.Ptr(), qNamedArgs.Ptr(), qNativeType)); + break; + + case CA_PreserveSigAttribute: + reinterpret_cast<MethodRec*>(pRow)->AddImplFlags(miPreserveSig); + break; + + case CA_StructLayoutAttribute1: + { + // Convert the I2 to a U2, then wide to an I4, then fall through. + qArgs[0].val.i4 = static_cast<int>(static_cast<USHORT>(qArgs[0].val.i2)); + } + FALLTHROUGH; + case CA_StructLayoutAttribute2: + { + // Get a copy of the flags to work with. + ULONG dwFlags; + dwFlags = reinterpret_cast<TypeDefRec*>(pRow)->GetFlags(); + // Class layout. Keep in sync with LayoutKind. + switch (qArgs[0].val.i4) + { + case 0: // tdSequentialLayout: + dwFlags = (dwFlags & ~tdLayoutMask) | tdSequentialLayout; + break; + case 2: // tdExplicitLayout: + dwFlags = (dwFlags & ~tdLayoutMask) | tdExplicitLayout; + break; + case 3: // tdAutoLayout: + dwFlags = (dwFlags & ~tdLayoutMask) | tdAutoLayout; + break; + default: + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + break; + } + + // Class packing and size. + ULONG ulSize, ulPack; + ulPack = ulSize = UINT32_MAX; + if (qNamedArgs[SL_Pack].val.type.tag) + { // Only 1,2,4,8,16,32,64,128 are legal values. + ulPack = qNamedArgs[SL_Pack].val.u4; + if ((ulPack > 128) || + (ulPack & (ulPack-1))) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + } + if (qNamedArgs[SL_Size].val.type.tag) + { + if (qNamedArgs[SL_Size].val.u4 > INT_MAX) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + ulSize = qNamedArgs[SL_Size].val.u4; + } + if (ulPack!=UINT32_MAX || ulSize!=UINT32_MAX) + IfFailGo(_SetClassLayout(tkObj, ulPack, ulSize)); + + // Class character set. + if (qNamedArgs[SL_CharSet].val.type.tag) + { + switch (qNamedArgs[SL_CharSet].val.u4) + { + //case 1: // Not specified. + // IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + // break; + case 2: // ANSI + dwFlags = (dwFlags & ~tdStringFormatMask) | tdAnsiClass; + break; + case 3: // Unicode + dwFlags = (dwFlags & ~tdStringFormatMask) | tdUnicodeClass; + break; + case 4: // Auto + dwFlags = (dwFlags & ~tdStringFormatMask) | tdAutoClass; + break; + default: + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + break; + } + } + + // Persist possibly-changed value of flags. + reinterpret_cast<TypeDefRec*>(pRow)->SetFlags(dwFlags); + } + break; + + case CA_FieldOffsetAttribute: + if (qArgs[0].val.u4 > INT_MAX) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + IfFailGo(_SetFieldOffset(tkObj, qArgs[0].val.u4)); + break; + + case CA_TypeLibVersionAttribute: + if ((qArgs[0].val.i4 < 0) || (qArgs[1].val.i4 < 0)) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + break; + + case CA_ComCompatibleVersionAttribute: + if ( (qArgs[0].val.i4 < 0) || (qArgs[1].val.i4 < 0) || (qArgs[2].val.i4 < 0) || (qArgs[3].val.i4 < 0) ) + IfFailGo(PostError(META_E_CA_INVALID_VALUE)); + break; + + case CA_AllowPartiallyTrustedCallersAttribute: + break; + + case CA_WindowsRuntimeImportAttribute: + reinterpret_cast<TypeDefRec*>(pRow)->AddFlags(tdWindowsRuntime); + break; + + default: + _ASSERTE(!"Unexpected custom attribute type"); + // Turn into ordinary custom attribute. + *bKeep = true; + hr = S_OK; + goto ErrExit; + break; + } + + IfFailGo(UpdateENCLog(tkObj)); + +ErrExit: + return hr; +} // RegMeta::_HandleKnownCustomAttribute +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +//***************************************************************************** +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::_HandleNativeTypeCustomAttribute(// S_OK or error. + mdToken tkObj, // The token this CA is applied on. + CaArg *pArgs, // Pointer to args. + CaNamedArg *pNamedArgs, // Pointer to named args. + CQuickArray<BYTE> &qNativeType) // Native type is built here. +{ + HRESULT hr = S_OK; // A result. + int cch = 0; // Size of a string argument. + ULONG cb; // Count of some character operation. + ULONG cbNative; // Size of native type string. + ULONG cbMax; // Max size of native type string. + BYTE *pbNative; // Pointer into native type buffer. + mdToken tkObjType; // The type of the token. + mdToken tkSetter; // Token for Property setter. + mdToken tkGetter; // Token for property getter. + mdParamDef tkParam; // Parameter of getter/setter. + ULONG cParams; // Count of params for getter/setter. + HCORENUM phEnum = 0; // Enumerator for params. + ULONG ulSeq; // Sequence of a param. + + // Retrieve the type of the token. + tkObjType = TypeFromToken(tkObj); + + // Compute maximum size of the native type. + if (pArgs[0].val.i4 == NATIVE_TYPE_CUSTOMMARSHALER) + { // N_T_* + 3 string lengths + cbMax = sizeof(ULONG) * 4; + // Marshal type - name of the type + cbMax += pNamedArgs[M_MarshalType].val.str.cbStr; + // Marshal type - type of the custom marshaler + cbMax += pNamedArgs[M_MarshalTypeRef].val.str.cbStr; + // String cookie. + cbMax += pNamedArgs[M_MarshalCookie].val.str.cbStr; + } + else if (pArgs[0].val.i4 == NATIVE_TYPE_SAFEARRAY) + { // N_T_* + safe array sub-type + string length. + cbMax = sizeof(ULONG) * 3; + // Safe array record sub type. + cbMax += pNamedArgs[M_SafeArrayUserDefinedSubType].val.str.cbStr; + } + else + { // N_T_* + sub-type + size + additive + NativeTypeArrayFlags. + cbMax = sizeof(ULONG) * 4 + sizeof(UINT16); + } + + // IidParameterIndex. + cbMax += sizeof(DWORD); + + // Extra space to prevent buffer overrun. + cbMax += 8; + + // Size the array. + IfFailGo(qNativeType.ReSizeNoThrow(cbMax)); + pbNative = qNativeType.Ptr(); + cbNative = 0; + + //<TODO>@FUTURE: check for valid combinations of args.</TODO> + + // Put in the NativeType. + cb = CorSigCompressData(pArgs[0].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Put in additional information, depending on native type. + switch (pArgs[0].val.i4) + { + case NATIVE_TYPE_INTF: + case NATIVE_TYPE_IUNKNOWN: + case NATIVE_TYPE_IDISPATCH: + // Validate that the IidParameterIndex field is valid if set. + if (pNamedArgs[M_IidParameterIndex].val.type.tag) + { + int iidparam = pNamedArgs[M_IidParameterIndex].val.i4; + if (iidparam < 0) + IfFailGo(PostError(META_E_CA_NEGATIVE_PARAMINDEX)); + + cb = CorSigCompressData(pNamedArgs[M_IidParameterIndex].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + } + break; + + + case NATIVE_TYPE_FIXEDARRAY: + // Validate that only fields valid for NATIVE_TYPE_FIXEDARRAY are set. + if (pNamedArgs[M_SafeArraySubType].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + if (pNamedArgs[M_SizeParamIndex].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // This native type is only applicable on fields. + if (tkObjType != mdtFieldDef) + IfFailGo(PostError(META_E_CA_NT_FIELDONLY)); + + if (pNamedArgs[M_SizeConst].val.type.tag) + { + // Make sure the size is not negative. + if (pNamedArgs[M_SizeConst].val.i4 < 0) + IfFailGo(PostError(META_E_CA_NEGATIVE_CONSTSIZE)); + + cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_NEGATIVE_CONSTSIZE)); + } + + } + else + { + cb = CorSigCompressData(1, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Is there a sub type? + if (pNamedArgs[M_ArraySubType].val.type.tag) + { + // Put in the sub type. + cb = CorSigCompressData(pNamedArgs[M_ArraySubType].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + } + break; + + case NATIVE_TYPE_FIXEDSYSSTRING: + // Validate that the required fields are set. + if (!pNamedArgs[M_SizeConst].val.type.tag) + IfFailGo(PostError(META_E_CA_FIXEDSTR_SIZE_REQUIRED)); + + // Validate that other array fields are not set. + if (pNamedArgs[M_ArraySubType].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + if (pNamedArgs[M_SizeParamIndex].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + if (pNamedArgs[M_SafeArraySubType].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // This native type is only applicable on fields. + if (tkObjType != mdtFieldDef) + IfFailGo(PostError(META_E_CA_NT_FIELDONLY)); + + // Put in the constant value. + cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + break; + + case NATIVE_TYPE_BYVALSTR: + // This native type is only applicable on parameters. + if (tkObjType != mdtParamDef) + IfFailGo(PostError(META_E_CA_INVALID_TARGET)); + break; + + case NATIVE_TYPE_SAFEARRAY: + // Validate that other array fields are not set. + if (pNamedArgs[M_ArraySubType].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + if (pNamedArgs[M_SizeParamIndex].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + if (pNamedArgs[M_SizeConst].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Is there a safe array sub type? + if (pNamedArgs[M_SafeArraySubType].val.type.tag) + { + // Put in the safe array sub type. + cb = CorSigCompressData(pNamedArgs[M_SafeArraySubType].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // When the SAFEARRAY contains user defined types, the type of the + // UDT can be specified in the SafeArrayUserDefinedSubType field. + if (pNamedArgs[M_SafeArrayUserDefinedSubType].val.type.tag) + { + // Validate that this is only set for valid VT's. + if (pNamedArgs[M_SafeArraySubType].val.i4 != VT_RECORD && + pNamedArgs[M_SafeArraySubType].val.i4 != VT_DISPATCH && + pNamedArgs[M_SafeArraySubType].val.i4 != VT_UNKNOWN) + { + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + } + + // Encode the size of the string. + cch = pNamedArgs[M_SafeArrayUserDefinedSubType].val.str.cbStr; + cb = CorSigCompressData(cch, pbNative); + if (cb == ((ULONG)(-1))) + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + cbNative += cb; + pbNative += cb; + + // Check that memcpy will fit and then encode the type name itself. + if (ovadd_gt(cbNative, cch, cbMax)) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + memcpy(pbNative, pNamedArgs[M_SafeArrayUserDefinedSubType].val.str.pStr, cch); + cbNative += cch; + pbNative += cch; + _ASSERTE(cbNative <= cbMax); + } + } + break; + + case NATIVE_TYPE_ARRAY: + // Validate that the array sub type is not set. + if (pNamedArgs[M_SafeArraySubType].val.type.tag) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Is there a sub type? + if (pNamedArgs[M_ArraySubType].val.type.tag) + { + // Do some validation on the array sub type. + if (pNamedArgs[M_ArraySubType].val.i4 == NATIVE_TYPE_CUSTOMMARSHALER) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Put in the sub type. + cb = CorSigCompressData(pNamedArgs[M_ArraySubType].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + } + else + { + // Put in the sub type. + cb = CorSigCompressData(NATIVE_TYPE_MAX, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + } + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Is there a parameter index? + if (pNamedArgs[M_SizeParamIndex].val.type.tag) + { + // Make sure the parameter index is not negative. + if (pNamedArgs[M_SizeParamIndex].val.i4 < 0) + IfFailGo(PostError(META_E_CA_NEGATIVE_PARAMINDEX)); + + // Yes, put it in. + cb = CorSigCompressData(pNamedArgs[M_SizeParamIndex].val.i2, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Is there a const? + if (pNamedArgs[M_SizeConst].val.type.tag) + { + // Make sure the size is not negative. + if (pNamedArgs[M_SizeConst].val.i4 < 0) + IfFailGo(PostError(META_E_CA_NEGATIVE_CONSTSIZE)); + + // Yes, put it in. + cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Put in the flag indicating the size param index was specified. + cb = CorSigCompressData((UINT16)ntaSizeParamIndexSpecified, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + } + } + else + { + // Is there a const? + if (pNamedArgs[M_SizeConst].val.type.tag) + { + // Put in a param index of 0. + cb = CorSigCompressData(0, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Put in the constant value. + cb = CorSigCompressData(pNamedArgs[M_SizeConst].val.i4, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Set the flags field to 0 to indicate the size param index was not specified. + cb = CorSigCompressData((UINT16)0, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + } + } + break; + + case NATIVE_TYPE_CUSTOMMARSHALER: + // Validate that the marshaler type field is set. + if (!pNamedArgs[M_MarshalType].val.type.tag && !pNamedArgs[M_MarshalTypeRef].val.type.tag) + IfFailGo(PostError(META_E_CA_CUSTMARSH_TYPE_REQUIRED)); + + // Put in the place holder for the unmanaged typelib guid. + cb = CorSigCompressData(0, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Put in the place holder for the unmanaged type name. + cb = CorSigCompressData(0, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + if (cbNative > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + + // Put in the marshaler type name. + if (pNamedArgs[M_MarshalType].val.type.tag) + { + cch = pNamedArgs[M_MarshalType].val.str.cbStr; + cb = CorSigCompressData(cch, pbNative); + if (cb == ((ULONG)(-1))) + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + cbNative += cb; + pbNative += cb; + // Check that memcpy will fit. + if ((cbNative+cch) > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + memcpy(pbNative, pNamedArgs[M_MarshalType].val.str.pStr, cch); + cbNative += cch; + pbNative += cch; + _ASSERTE(cbNative <= cbMax); + } + else + { + cch = pNamedArgs[M_MarshalTypeRef].val.str.cbStr; + cb = CorSigCompressData(cch, pbNative); + if (cb == ((ULONG)(-1))) + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + cbNative += cb; + pbNative += cb; + // Check that memcpy will fit. + if ((cbNative+cch) > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + memcpy(pbNative, pNamedArgs[M_MarshalTypeRef].val.str.pStr, cch); + cbNative += cch; + pbNative += cch; + _ASSERTE(cbNative <= cbMax); + } + + // Put in the cookie. + cch = pNamedArgs[M_MarshalCookie].val.str.cbStr; + cb = CorSigCompressData(cch, pbNative); + if (cb == ((ULONG)(-1))) + { + IfFailGo(PostError(META_E_CA_INVALID_BLOB)); + } + cbNative += cb; + pbNative += cb; + // Check that memcpy will fit. + if ((cbNative+cch) > cbMax) + IfFailGo(PostError(META_E_CA_INVALID_MARSHALAS_FIELDS)); + memcpy(pbNative, pNamedArgs[M_MarshalCookie].val.str.pStr, cch); + cbNative += cch; + pbNative += cch; + break; + } + _ASSERTE(cbNative <= cbMax); + + // Resize to actual size. + IfFailGo(qNativeType.ReSizeNoThrow(cbNative)); + + // Now apply the native type to actual token. If it is a property token, + // apply to the methods. + switch (TypeFromToken(tkObj)) + { + case mdtParamDef: + case mdtFieldDef: + IfFailGo(_SetFieldMarshal(tkObj, (PCCOR_SIGNATURE)qNativeType.Ptr(), (DWORD)qNativeType.Size())); + break; + + case mdtProperty: + // Get any setter/getter methods. + IfFailGo(GetPropertyProps(tkObj, 0,0,0,0,0,0,0,0,0,0, &tkSetter, &tkGetter, 0,0,0)); + // For getter, put the field marshal on the return value. + if (!IsNilToken(tkGetter)) + { + // Search for first param. + mdToken tk; + tkParam = mdParamDefNil; + do { + IfFailGo(EnumParams(&phEnum, tkGetter, &tk, 1, &cParams)); + if (cParams > 0) + { + IfFailGo(GetParamProps(tk, 0, &ulSeq, 0,0,0,0,0,0,0)); + if (ulSeq == 0) + { + tkParam = tk; + break; + } + } + + } while (hr == S_OK); + if (!IsNilToken(tkParam)) + IfFailGo(_SetFieldMarshal(tkParam, (PCCOR_SIGNATURE)qNativeType.Ptr(), (DWORD)qNativeType.Size())); + CloseEnum(phEnum); + phEnum = 0; + } + if (!IsNilToken(tkSetter)) + { + // Determine the last param. + PCCOR_SIGNATURE pSig; + ULONG cbSig; + mdToken tk; + ULONG iSeq; + IfFailGo(GetMethodProps(tkSetter, 0,0,0,0,0, &pSig,&cbSig, 0,0)); + tkParam = mdParamDefNil; + CorSigUncompressData(pSig+1, &iSeq); + // Search for last param. + if (iSeq != 0) + { + do { + IfFailGo(EnumParams(&phEnum, tkSetter, &tk, 1, &cParams)); + if (cParams > 0) + { + IfFailGo(GetParamProps(tk, 0, &ulSeq, 0,0,0,0,0,0,0)); + if (ulSeq == iSeq) + { + tkParam = tk; + break; + } + } + } while (hr == S_OK); + } + // If found one that is not return value + if (!IsNilToken(tkParam)) + IfFailGo(_SetFieldMarshal(tkParam, (PCCOR_SIGNATURE)qNativeType.Ptr(), (DWORD)qNativeType.Size())); + CloseEnum(phEnum); + phEnum = 0; + } + break; + + default: + _ASSERTE(!"Should not have this token type in _HandleNativeTypeCustomAttribute()"); + break; + } + +ErrExit: + if (phEnum) + CloseEnum(phEnum); + return hr; +} // RegMeta::_HandleNativeTypeCustomAttribute +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +#endif //FEATURE_METADATA_EMIT diff --git a/src/coreclr/md/compiler/custattr_import.cpp b/src/coreclr/md/compiler/custattr_import.cpp new file mode 100644 index 00000000000..56930bb5d3b --- /dev/null +++ b/src/coreclr/md/compiler/custattr_import.cpp @@ -0,0 +1,281 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** + +// +// CustAttr_Import.cpp +// +// Implementation for the meta data custom attribute import code (code:IMetaDataImport). +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" +#include "mdperf.h" +#include "posterror.h" +#include "cahlprinternal.h" +#include "custattr.h" +#include "corhdr.h" +#include <metamodelrw.h> + +//***************************************************************************** +// Implementation of hash for custom attribute types. +//***************************************************************************** +unsigned int CCustAttrHash::Hash(const CCustAttrHashKey *pData) +{ + return static_cast<unsigned int>(pData->tkType); +} // unsigned long CCustAttrHash::Hash() +unsigned int CCustAttrHash::Compare(const CCustAttrHashKey *p1, CCustAttrHashKey *p2) +{ + if (p1->tkType == p2->tkType) + return 0; + return 1; +} // unsigned long CCustAttrHash::Compare() +CCustAttrHash::ELEMENTSTATUS CCustAttrHash::Status(CCustAttrHashKey *p) +{ + if (p->tkType == FREE) + return (FREE); + if (p->tkType == DELETED) + return (DELETED); + return (USED); +} // CCustAttrHash::ELEMENTSTATUS CCustAttrHash::Status() +void CCustAttrHash::SetStatus(CCustAttrHashKey *p, CCustAttrHash::ELEMENTSTATUS s) +{ + p->tkType = s; +} // void CCustAttrHash::SetStatus() +void* CCustAttrHash::GetKey(CCustAttrHashKey *p) +{ + return &p->tkType; +} // void* CCustAttrHash::GetKey() + + +//***************************************************************************** +// Get the value of a CustomAttribute, using only TypeName for lookup. +//***************************************************************************** +STDMETHODIMP RegMeta::GetCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCWSTR wzName, // [IN] Name of desired Custom Attribute. + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData) // [OUT] Put size of data here. +{ + HRESULT hr; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + LPUTF8 szName; // Name in UFT8. + int iLen; // A length. + CMiniMdRW *pMiniMd = NULL; + + START_MD_PERF(); + LOCKREAD(); + pMiniMd = &(m_pStgdb->m_MiniMd); + + iLen = WszWideCharToMultiByte(CP_UTF8,0, wzName,-1, NULL,0, 0,0); + szName = (LPUTF8)_alloca(iLen); + VERIFY(WszWideCharToMultiByte(CP_UTF8,0, wzName,-1, szName,iLen, 0,0)); + + hr = ImportHelper::GetCustomAttributeByName(pMiniMd, tkObj, szName, ppData, pcbData); + +ErrExit: + + STOP_MD_PERF(GetCustomAttributeByName); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetCustomAttributeByName() + + +//***************************************************************************** +// Enumerate the CustomAttributes for a given token. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumCustomAttributes( + HCORENUM *phEnum, // Pointer to the enum. + mdToken tk, // Token to scope the enumeration. + mdToken tkType, // Type to limit the enumeration. + mdCustomAttribute rCustomAttributes[], // Put CustomAttributes here. + ULONG cMax, // Max CustomAttributes to put. + ULONG *pcCustomAttributes) // Put # tokens returned here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + HENUMInternal *pEnum = *ppmdEnum; + CustomAttributeRec *pRec; + ULONG index; + + LOG((LOGMD, "RegMeta::EnumCustomAttributes(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, tk, tkType, rCustomAttributes, cMax, pcCustomAttributes)); + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + CLookUpHash *pHashTable = pMiniMd->m_pLookUpHashs[TBL_CustomAttribute]; + + // Does caller want all custom Values? + if (IsNilToken(tk)) + { + IfFailGo( HENUMInternal::CreateSimpleEnum(mdtCustomAttribute, 1, pMiniMd->getCountCustomAttributes()+1, &pEnum) ); + } + else + { // Scope by some object. + if ( pMiniMd->IsSorted( TBL_CustomAttribute ) ) + { + // Get CustomAttributes for the object. + IfFailGo(pMiniMd->getCustomAttributeForToken(tk, &ridEnd, &ridStart)); + + if (IsNilToken(tkType)) + { + // Simple enumerator for object's entire list. + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtCustomAttribute, ridStart, ridEnd, &pEnum) ); + } + else + { + // Dynamic enumerator for subsetted list. + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) ); + + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->GetCustomAttributeRecord(index, &pRec)); + if (tkType == pMiniMd->getTypeOfCustomAttribute(pRec)) + { + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtCustomAttribute) ) ); + } + } + } + } + else + { + + if (pHashTable) + { + // table is not sorted but hash is built + // We want to create dynmaic array to hold the dynamic enumerator. + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + mdToken tkParentTmp; + mdToken tkTypeTmp; + + // Hash the data. + iHash = pMiniMd->HashCustomAttribute(tk); + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) ); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + + CustomAttributeRec *pCustomAttribute; + IfFailGo(pMiniMd->GetCustomAttributeRecord(RidFromToken(p->tok), &pCustomAttribute)); + tkParentTmp = pMiniMd->getParentOfCustomAttribute(pCustomAttribute); + tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pCustomAttribute); + if (tkParentTmp == tk) + { + if (IsNilToken(tkType) || tkType == tkTypeTmp) + { + // compare the blob value + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(p->tok, mdtCustomAttribute )) ); + } + } + } + } + else + { + + // table is not sorted and hash is not built so we have to create dynmaic array + // create the dynamic enumerator and loop through CA table linearly + // + ridStart = 1; + ridEnd = pMiniMd->getCountCustomAttributes() + 1; + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) ); + + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->GetCustomAttributeRecord(index, &pRec)); + if ( tk == pMiniMd->getParentOfCustomAttribute(pRec) && + (tkType == pMiniMd->getTypeOfCustomAttribute(pRec) || IsNilToken(tkType))) + { + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtCustomAttribute) ) ); + } + } + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rCustomAttributes, pcCustomAttributes); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumCustomAttributes); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumCustomAttributes() + + +//***************************************************************************** +// Get information about a CustomAttribute. +//***************************************************************************** +STDMETHODIMP RegMeta::GetCustomAttributeProps( + mdCustomAttribute cv, // The attribute token + mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here. + mdToken *ptkType, // [OUT, OPTIONAL] Put TypeDef/TypeRef token here. + void const **ppBlob, // [OUT, OPTIONAL] Put pointer to data here. + ULONG *pcbSize) // [OUT, OPTIONAL] Put size of data here. +{ + HRESULT hr = S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd; + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(cv) == mdtCustomAttribute); + + pMiniMd = &(m_pStgdb->m_MiniMd); + CustomAttributeRec *pCustomAttributeRec; // The custom value record. + + IfFailGo(pMiniMd->GetCustomAttributeRecord(RidFromToken(cv), &pCustomAttributeRec)); + + if (ptkObj) + *ptkObj = pMiniMd->getParentOfCustomAttribute(pCustomAttributeRec); + + if (ptkType) + *ptkType = pMiniMd->getTypeOfCustomAttribute(pCustomAttributeRec); + + if (ppBlob != NULL) + { + IfFailGo(pMiniMd->getValueOfCustomAttribute(pCustomAttributeRec, (const BYTE **)ppBlob, pcbSize)); + } + +ErrExit: + + STOP_MD_PERF(GetCustomAttributeProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetCustomAttributeProps diff --git a/src/coreclr/md/compiler/disp.cpp b/src/coreclr/md/compiler/disp.cpp new file mode 100644 index 00000000000..95c35063c71 --- /dev/null +++ b/src/coreclr/md/compiler/disp.cpp @@ -0,0 +1,969 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// Disp.cpp +// + +// +// Implementation for the meta data dispenser code. +// +//***************************************************************************** +#include "stdafx.h" +#include "disp.h" +#include "regmeta.h" +#include "mdutil.h" +#include <corerror.h> +#include <mdlog.h> +#include <mdcommon.h> + +#ifdef EnC_SUPPORTED +#define ENC_DELTA_HACK +#endif + +//***************************************************************************** +// Ctor. +//***************************************************************************** +Disp::Disp() : m_cRef(0) +{ +#if defined(LOGGING) + // InitializeLogging() calls scattered around the code. + // <TODO>@future: make this make some sense.</TODO> + InitializeLogging(); +#endif + + m_OptionValue.m_DupCheck = MDDupDefault; + m_OptionValue.m_RefToDefCheck = MDRefToDefDefault; + m_OptionValue.m_NotifyRemap = MDNotifyDefault; + m_OptionValue.m_UpdateMode = MDUpdateFull; + m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault; + m_OptionValue.m_ThreadSafetyOptions = MDThreadSafetyDefault; + m_OptionValue.m_GenerateTCEAdapters = FALSE; + m_OptionValue.m_ImportOption = MDImportOptionDefault; + m_OptionValue.m_LinkerOption = MDAssembly; + m_OptionValue.m_RuntimeVersion = NULL; + m_OptionValue.m_MetadataVersion = MDDefaultVersion; + m_OptionValue.m_MergeOptions = MergeFlagsNone; + m_OptionValue.m_InitialSize = MDInitialSizeDefault; + m_OptionValue.m_LocalRefPreservation = MDPreserveLocalRefsNone; +} // Disp::Disp + +Disp::~Disp() +{ + if (m_OptionValue.m_RuntimeVersion != NULL) + delete [] m_OptionValue.m_RuntimeVersion; +} // Disp::~Disp + +//***************************************************************************** +// Create a brand new scope. This is based on the CLSID that was used to get +// the dispenser. +//***************************************************************************** +__checkReturn +HRESULT +Disp::DefineScope( + REFCLSID rclsid, // [in] What version to create. + DWORD dwCreateFlags, // [in] Flags on the create. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ +#ifdef FEATURE_METADATA_EMIT + HRESULT hr = S_OK; + PathString szFileName(PathString::Literal, W("file:")); + PathString szFileNameSuffix; + BEGIN_ENTRYPOINT_NOTHROW; + + RegMeta *pMeta = 0; + OptionValue optionForNewScope = m_OptionValue; + + + LOG((LF_METADATA, LL_INFO10, "Disp::DefineScope(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", rclsid, dwCreateFlags, riid, ppIUnk)); + + if (dwCreateFlags) + IfFailGo(E_INVALIDARG); + + // Figure out what version of the metadata to emit + if (rclsid == CLSID_CLR_v1_MetaData) + { + optionForNewScope.m_MetadataVersion = MDVersion1; + } + else if (rclsid == CLSID_CLR_v2_MetaData) + { + optionForNewScope.m_MetadataVersion = MDVersion2; + } + else + { + // If it is a version we don't understand, then we cannot continue. + IfFailGo(CLDB_E_FILE_OLDVER); + } + +#ifdef ENC_DELTA_HACK +// Testers need this flag for their tests. + + DWORD len; + EX_TRY{ + len = WszGetEnvironmentVariable(W("COMP_ENC_OPENSCOPE"), szFileNameSuffix); + szFileName.Append(szFileNameSuffix); + } + EX_CATCH_HRESULT(hr); + + if (len > 0) + { + // _ASSERTE(!"ENC override on DefineScope"); +// m_OptionValue.m_UpdateMode = MDUpdateENC; +// m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault; +// hr = OpenScope(szFileName, ofWrite, riid, ppIUnk); + + IMetaDataEmit *pMetaEmit; + hr = OpenScope(szFileName, ofWrite, IID_IMetaDataEmit, (IUnknown **)&pMetaEmit); + DWORD cb; + CQuickBytes pbMetadata; + hr = pMetaEmit->GetSaveSize(cssAccurate,&cb); + _ASSERTE(SUCCEEDED(hr)); + + IfFailGo(pbMetadata.ReSizeNoThrow(cb)); + + hr = pMetaEmit->SaveToMemory(pbMetadata.Ptr(),cb); + _ASSERTE(SUCCEEDED(hr)); +// hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite|MDUpdateENC|MDUpdateDelta, riid, ppIUnk); + + + VARIANT encOption; + V_VT(&encOption) = VT_UI4; + V_UI4(&encOption) = MDUpdateENC; + SetOption(MetaDataSetENC, &encOption); + V_UI4(&encOption) = MDErrorOutOfOrderDefault; + SetOption(MetaDataErrorIfEmitOutOfOrder, &encOption); + hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite, riid, ppIUnk); + _ASSERTE(SUCCEEDED(hr)); + BOOL fResult = SUCCEEDED(hr); + // print out a message so people know what's happening + printf("Defining scope for EnC using %S %s\n", + static_cast<LPCWSTR>(szFileNameSuffix), fResult ? "succeeded" : "failed"); + + goto ErrExit; + } +#endif // ENC_DELTA_HACK + + // Create a new coclass for this. + pMeta = new (nothrow) RegMeta(); + IfNullGo(pMeta); + + IfFailGo(pMeta->SetOption(&optionForNewScope)); + + // Create the MiniMd-style scope. + IfFailGo(pMeta->CreateNewMD()); + + // Get the requested interface. + IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk)); + + // Add the new RegMeta to the cache. + IfFailGo(pMeta->AddToCache()); + + LOG((LOGMD, "{%08x} Created new emit scope\n", pMeta)); + +ErrExit: + if (FAILED(hr)) + { + if (pMeta != NULL) + delete pMeta; + *ppIUnk = NULL; + } + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT +} // Disp::DefineScope + + +//***************************************************************************** +// Deliver scope to caller of OpenScope or OpenScopeOnMemory +//***************************************************************************** +static HRESULT DeliverScope(IMDCommon *pMDCommon, REFIID riid, DWORD dwOpenFlags, IUnknown **ppIUnk) +{ + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(pMDCommon->QueryInterface(riid, (void**)ppIUnk)); + + ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} + +//***************************************************************************** +// Open an existing scope. +//***************************************************************************** +HRESULT Disp::OpenScope( // Return code. + LPCWSTR szFileName, // [in] The scope to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope(%S, 0x%08x, 0x%08x, 0x%08x)\n", MDSTR(szFileName), dwOpenFlags, riid, ppIUnk)); + + IMDCommon *pMDCommon = NULL; + + // Validate that there is some sort of file name. + if ((szFileName == NULL) || (szFileName[0] == 0) || (ppIUnk == NULL)) + IfFailGo(E_INVALIDARG); + + *ppIUnk = NULL; + IfFailGo(OpenRawScope(szFileName, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon)); + IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk)); + ErrExit: + if (pMDCommon) + pMDCommon->Release(); + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +//***************************************************************************** +// Open a raw view of existing scope. +//***************************************************************************** +__checkReturn +HRESULT +Disp::OpenRawScope( + LPCWSTR szFileName, // [in] The scope to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(szFileName != NULL); + _ASSERTE(ppIUnk != NULL); + RegMeta *pMeta = NULL; + +#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES + // Don't assert for code:ofTrustedImage (reserved) flag if the feature is supported + _ASSERTE(!IsOfReserved(dwOpenFlags & ~ofTrustedImage)); +#else + _ASSERTE(!IsOfReserved(dwOpenFlags)); +#endif //!FEATURE_METADATA_LOAD_TRUSTED_IMAGES + + { + } + + if (IsOfReadOnly(dwOpenFlags) && IsOfReadWrite(dwOpenFlags)) + { // Invalid combination of flags - ofReadOnly & ofWrite + IfFailGo(E_INVALIDARG); + } + // If open-for-read, and there is already an open-for-read copy, return it. + if (IsOfReadOnly(dwOpenFlags)) + { + RegMeta::FindCachedReadOnlyEntry(szFileName, dwOpenFlags, &pMeta); + if (pMeta != NULL) + { + // Return the requested interface. + hr = pMeta->QueryInterface(riid, (void **) ppIUnk); + if (FAILED(hr)) + { + pMeta = NULL; // Don't delete cached RegMeta! + } + else + { + pMeta->Release(); // Give back refcount from QI + LOG((LOGMD, "{%08x} Found in cache '%S'\n", pMeta, MDSTR(szFileName))); + } + + goto ErrExit; + } + } + // Create a new coclass for this guy. + pMeta = new (nothrow) RegMeta(); + IfNullGo(pMeta); + + IfFailGo(pMeta->SetOption(&m_OptionValue)); + + // Always initialize the RegMeta's stgdb. + // <TODO>@FUTURE: there are some cleanup for the open code!!</TODO> + if (memcmp(szFileName, W("file:"), 10) == 0) + { + szFileName = &szFileName[5]; + } + + // Try to open the MiniMd-style scope. + IfFailGo(pMeta->OpenExistingMD(szFileName, 0 /* pbData */,0 /* cbData */, dwOpenFlags)); + + // Obtain the requested interface. + IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk) ); + + // Add the new RegMeta to the cache. If this is read-only, any future opens will + // find this entry. If, due to another thread concurrently opening the same file, + // there is already another copy in the cache, well, then there will be two + // read-only copies in the cache. This is considered to be somewhat of a corner + // case, and the only harm is temporary memory usage. All requests will be + // satisfied by one or the other (depending on search algorithm), and eventually, + // the "other" copy will be released. + IfFailGo(pMeta->AddToCache()); + + LOG((LOGMD, "{%08x} Successfully opened '%S'\n", pMeta, MDSTR(szFileName))); + +#if defined(_DEBUG) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump)) + { + int DumpMD_impl(RegMeta *pMD); + DumpMD_impl(pMeta); + } +#endif // _DEBUG + + +ErrExit: + if (FAILED(hr)) + { + if (pMeta != NULL) + delete pMeta; + *ppIUnk = NULL; + } + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // Disp::OpenScope + + +//***************************************************************************** +// Open an existing scope. +//***************************************************************************** +HRESULT Disp::OpenScopeOnMemory( // Return code. + LPCVOID pData, // [in] Location of scope data. + ULONG cbData, // [in] Size of the data pointed to by pData. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnMemory(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pData, cbData, dwOpenFlags, riid, ppIUnk)); + + IMDCommon *pMDCommon = NULL; + + _ASSERTE(!IsOfReserved(dwOpenFlags)); + if (ppIUnk == NULL) + IfFailGo(E_INVALIDARG); + *ppIUnk = NULL; + IfFailGo(OpenRawScopeOnMemory(pData, cbData, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon)); + IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk)); + ErrExit: + if (pMDCommon) + pMDCommon->Release(); + END_ENTRYPOINT_NOTHROW; + return hr; +} + +//***************************************************************************** +// Open a raw voew of existing scope. +//***************************************************************************** +HRESULT Disp::OpenRawScopeOnMemory( // Return code. + LPCVOID pData, // [in] Location of scope data. + ULONG cbData, // [in] Size of the data pointed to by pData. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + RegMeta *pMeta = 0; + + _ASSERTE(!IsOfReserved(dwOpenFlags)); + + // Create a new coclass for this guy. + pMeta = new (nothrow) RegMeta(); + IfNullGo(pMeta); + IfFailGo(pMeta->SetOption(&m_OptionValue)); + + + PREFIX_ASSUME(pMeta != NULL); + // Always initialize the RegMeta's stgdb. + IfFailGo(pMeta->OpenExistingMD(0 /* szFileName */, const_cast<void*>(pData), cbData, dwOpenFlags)); + + LOG((LOGMD, "{%08x} Opened new scope on memory, pData: %08x cbData: %08x\n", pMeta, pData, cbData)); + + // Return the requested interface. + IfFailGo( pMeta->QueryInterface(riid, (void **) ppIUnk) ); + + // Add the new RegMeta to the cache. + IfFailGo(pMeta->AddToCache()); + +#if defined(_DEBUG) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump)) + { + int DumpMD_impl(RegMeta *pMD); + DumpMD_impl(pMeta); + } +#endif // _DEBUG + +ErrExit: + if (FAILED(hr)) + { + if (pMeta) delete pMeta; + *ppIUnk = 0; + } + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // Disp::OpenScopeOnMemory + + +//***************************************************************************** +// Get the directory where the CLR system resides. +// +// Implements public API code:IMetaDataDispenserEx::GetCORSystemDirectory. +//***************************************************************************** +HRESULT +Disp::GetCORSystemDirectory( + __out_ecount (cchBuffer) LPWSTR szBuffer, // [out] Buffer for the directory name + DWORD cchBuffer, // [in] Size of the buffer + DWORD *pcchBuffer) // [out] Number of characters returned +{ + + UNREACHABLE_MSG("Calling IMetaDataDispenser::GetCORSystemDirectory! This code should not be " + "reachable or needs to be reimplemented for CoreCLR!"); + + return E_NOTIMPL; +} // Disp::GetCORSystemDirectory + +HRESULT Disp::FindAssembly( // S_OK or error + LPCWSTR szAppBase, // [IN] optional - can be NULL + LPCWSTR szPrivateBin, // [IN] optional - can be NULL + LPCWSTR szGlobalBin, // [IN] optional - can be NULL + LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting + LPCWSTR szName, // [OUT] buffer - to hold name + ULONG cchName, // [IN] the name buffer's size + ULONG *pcName) // [OUT] the number of characters returend in the buffer +{ + BEGIN_ENTRYPOINT_NOTHROW; + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} // Disp::FindAssembly + +HRESULT Disp::FindAssemblyModule( // S_OK or error + LPCWSTR szAppBase, // [IN] optional - can be NULL + LPCWSTR szPrivateBin, // [IN] optional - can be NULL + LPCWSTR szGlobalBin, // [IN] optional - can be NULL + LPCWSTR szAssemblyName, // [IN] The assembly name or code base of the assembly + LPCWSTR szModuleName, // [IN] required - the name of the module + __out_ecount (cchName) LPWSTR szName, // [OUT] buffer - to hold name + ULONG cchName, // [IN] the name buffer's size + ULONG *pcName) // [OUT] the number of characters returend in the buffer +{ + BEGIN_ENTRYPOINT_NOTHROW; + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} // Disp::FindAssemblyModule + +//***************************************************************************** +// Open a scope on an ITypeInfo +//***************************************************************************** +HRESULT Disp::OpenScopeOnITypeInfo( // Return code. + ITypeInfo *pITI, // [in] ITypeInfo to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ + BEGIN_ENTRYPOINT_NOTHROW; + END_ENTRYPOINT_NOTHROW; + + return E_NOTIMPL; +} // Disp::OpenScopeOnITypeInfo + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +//***************************************************************************** +// Create a brand new scope which will be used for portable PDB metadata. +// This is based on the CLSID that was used to get the dispenser. +// +// The existing DefineScope method cannot be used for the purpose of PDB +// metadata generation, since it internally creates module and type def table +// entries. +//***************************************************************************** +__checkReturn +HRESULT +Disp::DefinePortablePdbScope( + REFCLSID rclsid, // [in] What version to create. + DWORD dwCreateFlags, // [in] Flags on the create. + REFIID riid, // [in] The interface desired. + IUnknown** ppIUnk) // [out] Return interface on success. +{ +#ifdef FEATURE_METADATA_EMIT + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + RegMeta* pMeta = 0; + OptionValue optionForNewScope = m_OptionValue; + + LOG((LF_METADATA, LL_INFO10, "Disp::DefinePortablePdbScope(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", rclsid, dwCreateFlags, riid, ppIUnk)); + + if (dwCreateFlags) + IfFailGo(E_INVALIDARG); + + // Currently the portable PDB tables are treated as an extension to the MDVersion2 + // TODO: this extension might deserve its own version number e.g. 'MDVersion3' + if (rclsid == CLSID_CLR_v2_MetaData) + { + optionForNewScope.m_MetadataVersion = MDVersion2; + } + else + { + // If it is a version we don't understand, then we cannot continue. + IfFailGo(CLDB_E_FILE_OLDVER); + } + + // Create a new coclass for this. + pMeta = new (nothrow) RegMeta(); + IfNullGo(pMeta); + + IfFailGo(pMeta->SetOption(&optionForNewScope)); + + // Create the MiniMd-style scope for portable pdb + IfFailGo(pMeta->CreateNewPortablePdbMD()); + + // Get the requested interface. + IfFailGo(pMeta->QueryInterface(riid, (void**)ppIUnk)); + + // Add the new RegMeta to the cache. + IfFailGo(pMeta->AddToCache()); + + LOG((LOGMD, "{%08x} Created new emit scope\n", pMeta)); + +ErrExit: + if (FAILED(hr)) + { + if (pMeta != NULL) + delete pMeta; + *ppIUnk = NULL; + } + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT +} // Disp::DefineScope +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + +//***************************************************************************** +// IMetaDataDispenserCustom +//***************************************************************************** + +HRESULT Disp::OpenScopeOnCustomDataSource( // S_OK or error + IMDCustomDataSource *pCustomSource, // [in] The scope to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnCustomDataSource(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pCustomSource, dwOpenFlags, riid, ppIUnk)); + + IMDCommon *pMDCommon = NULL; + + _ASSERTE(!IsOfReserved(dwOpenFlags)); + if (ppIUnk == NULL) + IfFailGo(E_INVALIDARG); + *ppIUnk = NULL; + IfFailGo(OpenRawScopeOnCustomDataSource(pCustomSource, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon)); + IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk)); +ErrExit: + if (pMDCommon) + pMDCommon->Release(); + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +//***************************************************************************** +// Open a raw view of existing scope. +//***************************************************************************** +HRESULT Disp::OpenRawScopeOnCustomDataSource( // Return code. + IMDCustomDataSource* pDataSource, // [in] scope data. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk) // [out] Return interface on success. +{ + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + RegMeta *pMeta = 0; + + _ASSERTE(!IsOfReserved(dwOpenFlags)); + + // Create a new coclass for this guy. + pMeta = new (nothrow)RegMeta(); + IfNullGo(pMeta); + IfFailGo(pMeta->SetOption(&m_OptionValue)); + + + PREFIX_ASSUME(pMeta != NULL); + // Always initialize the RegMeta's stgdb. + // TODO + IfFailGo(pMeta->OpenExistingMD(pDataSource, dwOpenFlags)); + + LOG((LOGMD, "{%08x} Opened new scope on custom data source, pDataSource: %08x\n", pMeta, pDataSource)); + + // Return the requested interface. + IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk)); + + // Add the new RegMeta to the cache. + IfFailGo(pMeta->AddToCache()); + +#if defined(_DEBUG) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump)) + { + int DumpMD_impl(RegMeta *pMD); + DumpMD_impl(pMeta); + } +#endif // _DEBUG + +ErrExit: + if (FAILED(hr)) + { + if (pMeta) delete pMeta; + *ppIUnk = 0; + } + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // Disp::OpenRawScopeOnCustomDataSource + +#endif + +//***************************************************************************** +// IUnknown +//***************************************************************************** + +ULONG Disp::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} // Disp::AddRef + +ULONG Disp::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + delete this; + return cRef; +} // Disp::Release + +HRESULT Disp::QueryInterface(REFIID riid, void **ppUnk) +{ + *ppUnk = 0; + + if (riid == IID_IUnknown) + *ppUnk = (IUnknown *) (IMetaDataDispenser *) this; + else if (riid == IID_IMetaDataDispenser) + *ppUnk = (IMetaDataDispenser *) this; + else if (riid == IID_IMetaDataDispenserEx) + *ppUnk = (IMetaDataDispenserEx *) this; +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + else if (riid == IID_IMetaDataDispenserEx2) + *ppUnk = (IMetaDataDispenserEx2 *) this; +#endif +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + else if (riid == IID_IMetaDataDispenserCustom) + *ppUnk = static_cast<IMetaDataDispenserCustom*>(this); +#endif + else + return E_NOINTERFACE; + AddRef(); + return S_OK; +} // Disp::QueryInterface + + +//***************************************************************************** +// Called by the class factory template to create a new instance of this object. +//***************************************************************************** +HRESULT Disp::CreateObject(REFIID riid, void **ppUnk) +{ + HRESULT hr; + Disp *pDisp = new (nothrow) Disp(); + + if (pDisp == 0) + return (E_OUTOFMEMORY); + + hr = pDisp->QueryInterface(riid, ppUnk); + if (FAILED(hr)) + delete pDisp; + return hr; +} // Disp::CreateObject + +//***************************************************************************** +// This routine provides the user a way to set certain properties on the +// Dispenser. +// +// Implements public API code:IMetaDataDispenserEx::SetOption. +//***************************************************************************** +__checkReturn +HRESULT +Disp::SetOption( + REFGUID optionid, // [in] GUID for the option to be set. + const VARIANT *pvalue) // [in] Value to which the option is to be set. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LF_METADATA, LL_INFO10, "Disp::SetOption(0x%08x, 0x%08x)\n", optionid, pvalue)); + + if (optionid == MetaDataCheckDuplicatesFor) + { + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_DupCheck = (CorCheckDuplicatesFor) V_UI4(pvalue); + } + else if (optionid == MetaDataRefToDefCheck) + { + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_RefToDefCheck = (CorRefToDefCheck) V_UI4(pvalue); + } + else if (optionid == MetaDataErrorIfEmitOutOfOrder) + { + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_ErrorIfEmitOutOfOrder = (CorErrorIfEmitOutOfOrder) V_UI4(pvalue); + } + else if (optionid == MetaDataThreadSafetyOptions) + { + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_ThreadSafetyOptions = (CorThreadSafetyOptions) V_UI4(pvalue); + } +// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat. +#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) + else if (optionid == MetaDataNotificationForTokenMovement) + { // Note: This is not used in CLR sources anymore, but we store the value and return it back in + // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it here for backward-compat. + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_NotifyRemap = (CorNotificationForTokenMovement)V_UI4(pvalue); + } + else if (optionid == MetaDataSetENC) + { // EnC update mode (also aliased as code:MetaDataSetUpdate) + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_UpdateMode = V_UI4(pvalue); + } + else if (optionid == MetaDataImportOption) + { // Allows enumeration of EnC deleted items by Import API + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_ImportOption = (CorImportOptions) V_UI4(pvalue); + } + else if (optionid == MetaDataLinkerOptions) + { // Used only by code:RegMeta::UnmarkAll (code:IMetaDataFilter::UnmarkAll) + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_LinkerOption = (CorLinkerOptions) V_UI4(pvalue); + } + else if (optionid == MetaDataMergerOptions) + { + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_MergeOptions = (MergeFlags) V_UI4(pvalue); + } + else if (optionid == MetaDataGenerateTCEAdapters) + { // Note: This is not used in CLR sources anymore, but we store the value and return it back in + // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it for backward-compat. + if (V_VT(pvalue) != VT_BOOL) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_GenerateTCEAdapters = V_BOOL(pvalue); + } + else if (optionid == MetaDataTypeLibImportNamespace) + { // Note: This is not used in CLR sources anymore, keep it here for backward-compat + if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL) + { + _ASSERTE(!"Invalid Variant Type value for namespace."); + IfFailGo(E_INVALIDARG); + } + } +#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER + else if (optionid == MetaDataRuntimeVersion) + { + if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL) + { + _ASSERTE(!"Invalid Variant Type value for version."); + IfFailGo(E_INVALIDARG); + } + if (m_OptionValue.m_RuntimeVersion) + delete [] m_OptionValue.m_RuntimeVersion; + + if ((V_VT(pvalue) == VT_EMPTY) || (V_VT(pvalue) == VT_NULL) || (*V_BSTR(pvalue) == 0)) + { + m_OptionValue.m_RuntimeVersion = NULL; + } + else + { + INT32 len = WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, NULL, 0, NULL, NULL); + m_OptionValue.m_RuntimeVersion = new (nothrow) char[len]; + if (m_OptionValue.m_RuntimeVersion == NULL) + IfFailGo(E_INVALIDARG); + WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, m_OptionValue.m_RuntimeVersion, len, NULL, NULL); + } + } + else if (optionid == MetaDataInitialSize) + { + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + m_OptionValue.m_InitialSize = V_UI4(pvalue); + } + else if (optionid == MetaDataPreserveLocalRefs) + { + if (V_VT(pvalue) != VT_UI4) + { + _ASSERTE(!"Invalid Variant Type value!"); + IfFailGo(E_INVALIDARG); + } + + m_OptionValue.m_LocalRefPreservation = (CorLocalRefPreservation) V_UI4(pvalue); + } + else + { + _ASSERTE(!"Invalid GUID"); + IfFailGo(E_INVALIDARG); + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // Disp::SetOption + +//***************************************************************************** +// This routine provides the user a way to set certain properties on the +// Dispenser. +// +// Implements public API code:IMetaDataDispenserEx::GetOption. +//***************************************************************************** +HRESULT Disp::GetOption( // Return code. + REFGUID optionid, // [in] GUID for the option to be set. + VARIANT *pvalue) // [out] Value to which the option is currently set. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LF_METADATA, LL_INFO10, "Disp::GetOption(0x%08x, 0x%08x)\n", optionid, pvalue)); + + _ASSERTE(pvalue); + if (optionid == MetaDataCheckDuplicatesFor) + { + V_VT(pvalue) = VT_UI4; + V_UI4(pvalue) = m_OptionValue.m_DupCheck; + } + else if (optionid == MetaDataRefToDefCheck) + { + V_VT(pvalue) = VT_UI4; + V_UI4(pvalue) = m_OptionValue.m_RefToDefCheck; + } + else if (optionid == MetaDataErrorIfEmitOutOfOrder) + { + V_VT(pvalue) = VT_UI4; + V_UI4(pvalue) = m_OptionValue.m_ErrorIfEmitOutOfOrder; + } +// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat. +#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) + else if (optionid == MetaDataNotificationForTokenMovement) + { // Note: This is not used in CLR sources anymore, but we store the value and return it here, + // so we keep it for backward-compat. + V_VT(pvalue) = VT_UI4; + V_UI4(pvalue) = m_OptionValue.m_NotifyRemap; + } + else if (optionid == MetaDataSetENC) + { // EnC update mode (also aliased as code:MetaDataSetUpdate) + V_VT(pvalue) = VT_UI4; + V_UI4(pvalue) = m_OptionValue.m_UpdateMode; + } + else if (optionid == MetaDataLinkerOptions) + { // Allows enumeration of EnC deleted items by Import API + V_VT(pvalue) = VT_BOOL; + V_UI4(pvalue) = m_OptionValue.m_LinkerOption; + } + else if (optionid == MetaDataGenerateTCEAdapters) + { // Note: This is not used in CLR sources anymore, but we store the value and return it here, + // so we keep it for backward-compat. + V_VT(pvalue) = VT_BOOL; + V_BOOL(pvalue) = m_OptionValue.m_GenerateTCEAdapters; + } +#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER + else + { + _ASSERTE(!"Invalid GUID"); + IfFailGo(E_INVALIDARG); + } +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} // Disp::GetOption + +#if defined(FEATURE_METADATA_IN_VM) + +//--------------------------------------------------------------------------------------- +// +// Process detach destruction. +// Called from DllMain of clr.dll/RoMetadata.dll/MidlrtMd.dll. +// +void DeleteMetaData() +{ + LOADEDMODULES::DeleteStatics(); +} + +#endif //FEATURE_METADATA_IN_VM + +// +// This is the entrypoint for usages of MetaData that need to start with the dispenser (e.g. +// mscordbi.dll and profiling API). +// +// Notes: +// This could be merged with the class factory support. +HRESULT InternalCreateMetaDataDispenser(REFIID riid, void ** pMetaDataDispenserOut) +{ + _ASSERTE(pMetaDataDispenserOut != NULL); + return Disp::CreateObject(riid, pMetaDataDispenserOut); +} diff --git a/src/coreclr/md/compiler/disp.h b/src/coreclr/md/compiler/disp.h new file mode 100644 index 00000000000..fbca3f069c6 --- /dev/null +++ b/src/coreclr/md/compiler/disp.h @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// Disp.h +// + +// +// Class factories are used by the pluming in COM to activate new objects. +// This module contains the class factory code to instantiate the debugger +// objects described in <cordb.h>. +// +//***************************************************************************** +#ifndef __Disp__h__ +#define __Disp__h__ + + +class Disp : +#ifndef FEATURE_METADATA_EMIT_PORTABLE_PDB + public IMetaDataDispenserEx +#else + public IMetaDataDispenserEx2 +#endif +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + , IMetaDataDispenserCustom +#endif +{ +public: + Disp(); + virtual ~Disp(); + + // *** IUnknown methods *** + STDMETHODIMP QueryInterface(REFIID riid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // *** IMetaDataDispenser methods *** + STDMETHODIMP DefineScope( // Return code. + REFCLSID rclsid, // [in] What version to create. + DWORD dwCreateFlags, // [in] Flags on the create. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. + + STDMETHODIMP OpenScope( // Return code. + LPCWSTR szScope, // [in] The scope to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. + + STDMETHODIMP OpenScopeOnMemory( // Return code. + LPCVOID pData, // [in] Location of scope data. + ULONG cbData, // [in] Size of the data pointed to by pData. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. + + // *** IMetaDataDispenserEx methods *** + STDMETHODIMP SetOption( // Return code. + REFGUID optionid, // [in] GUID for the option to be set. + const VARIANT *pvalue); // [in] Value to which the option is to be set. + + STDMETHODIMP GetOption( // Return code. + REFGUID optionid, // [in] GUID for the option to be set. + VARIANT *pvalue); // [out] Value to which the option is currently set. + + STDMETHODIMP OpenScopeOnITypeInfo( // Return code. + ITypeInfo *pITI, // [in] ITypeInfo to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. + + STDMETHODIMP GetCORSystemDirectory( // Return code. + __out_ecount (cchBuffer) LPWSTR szBuffer, // [out] Buffer for the directory name + DWORD cchBuffer, // [in] Size of the buffer + DWORD* pchBuffer); // [OUT] Number of characters returned + + STDMETHODIMP FindAssembly( // S_OK or error + LPCWSTR szAppBase, // [IN] optional - can be NULL + LPCWSTR szPrivateBin, // [IN] optional - can be NULL + LPCWSTR szGlobalBin, // [IN] optional - can be NULL + LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting + LPCWSTR szName, // [OUT] buffer - to hold name + ULONG cchName, // [IN] the name buffer's size + ULONG *pcName); // [OUT] the number of characters returend in the buffer + + STDMETHODIMP FindAssemblyModule( // S_OK or error + LPCWSTR szAppBase, // [IN] optional - can be NULL + LPCWSTR szPrivateBin, // [IN] optional - can be NULL + LPCWSTR szGlobalBin, // [IN] optional - can be NULL + LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting + LPCWSTR szModuleName, // [IN] required - the name of the module + __out_ecount (cchName)LPWSTR szName,// [OUT] buffer - to hold name + ULONG cchName, // [IN] the name buffer's size + ULONG *pcName); // [OUT] the number of characters returend in the buffer + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + STDMETHODIMP DefinePortablePdbScope( // Return code. + REFCLSID rclsid, // [in] What version to create. + DWORD dwCreateFlags, // [in] Flags on the create. + REFIID riid, // [in] The interface desired. + IUnknown** ppIUnk); // [out] Return interface on success. +#endif + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + // *** IMetaDataDispenserCustom methods *** + STDMETHODIMP OpenScopeOnCustomDataSource( // S_OK or error + IMDCustomDataSource *pCustomSource, // [in] The scope to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. +#endif + + // Class factory hook-up. + static HRESULT CreateObject(REFIID riid, void **ppUnk); + +private: + HRESULT OpenRawScope( // Return code. + LPCWSTR szScope, // [in] The scope to open. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. + + HRESULT OpenRawScopeOnMemory( // Return code. + LPCVOID pData, // [in] Location of scope data. + ULONG cbData, // [in] Size of the data pointed to by pData. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + HRESULT OpenRawScopeOnCustomDataSource( // Return code. + IMDCustomDataSource* pDataSource, // [in] scope data. + DWORD dwOpenFlags, // [in] Open mode flags. + REFIID riid, // [in] The interface desired. + IUnknown **ppIUnk); // [out] Return interface on success. +#endif + + +private: + LONG m_cRef; // Ref count + OptionValue m_OptionValue; // values can be set by using SetOption +}; + +#endif // __Disp__h__ diff --git a/src/coreclr/md/compiler/emit.cpp b/src/coreclr/md/compiler/emit.cpp new file mode 100644 index 00000000000..4a7cf802022 --- /dev/null +++ b/src/coreclr/md/compiler/emit.cpp @@ -0,0 +1,3306 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// Emit.cpp +// + +// +// Implementation for the meta data emit code. +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" + +#ifdef FEATURE_METADATA_EMIT + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +//***************************************************************************** +// Create and set a new MethodDef record. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineMethod( // S_OK or error. + mdTypeDef td, // Parent TypeDef + LPCWSTR szName, // Name of member + DWORD dwMethodFlags, // Member attributes + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + ULONG ulCodeRVA, + DWORD dwImplFlags, + mdMethodDef *pmd) // Put member token here +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + MethodRec *pRecord = NULL; // The new record. + RID iRecord; // The new record's RID. + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + + LOG((LOGMD, "MD: RegMeta::DefineMethod(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + td, MDSTR(szName), dwMethodFlags, pvSigBlob, cbSigBlob, ulCodeRVA, dwImplFlags, pmd)); + START_MD_PERF(); + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(pmd); + + // Make sure no one sets the reserved bits on the way in. + dwMethodFlags &= (~mdReservedMask); + + IsGlobalMethodParent(&td); + + // See if this method has already been defined. + if (CheckDups(MDDupMethodDef)) + { + hr = ImportHelper::FindMethod( + &(m_pStgdb->m_MiniMd), + td, + szNameUtf8, + pvSigBlob, + cbSigBlob, + pmd); + + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(*pmd), &pRecord)); + } + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + { + IfFailGo(hr); + } + } + + // Create the new record. + if (pRecord == NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.AddMethodRecord(&pRecord, &iRecord)); + + // Give token back to caller. + *pmd = TokenFromRid(iRecord, mdtMethodDef); + + // Add to parent's list of child records. + IfFailGo(m_pStgdb->m_MiniMd.AddMethodToTypeDef(RidFromToken(td), iRecord)); + + IfFailGo(UpdateENCLog(td, CMiniMdRW::eDeltaMethodCreate)); + + // record the more defs are introduced. + SetMemberDefDirty(true); + } + + // Set the method properties. + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Method, MethodRec::COL_Name, pRecord, szNameUtf8)); + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Method, MethodRec::COL_Signature, pRecord, pvSigBlob, cbSigBlob)); + + // <TODO>@FUTURE: possible performance improvement here to check _ first of all.</TODO> + // .ctor and .cctor below are defined in corhdr.h. However, corhdr.h does not have the + // the W() macro we need (since it's distributed to windows). We substitute the values of each + // macro in the code below to work around this issue. + // #define COR_CTOR_METHOD_NAME_W L".ctor" + // #define COR_CCTOR_METHOD_NAME_W L".cctor" + + if (!wcscmp(szName, W(".ctor")) || // COR_CTOR_METHOD_NAME_W + !wcscmp(szName, W(".cctor")) || // COR_CCTOR_METHOD_NAME_W + !wcsncmp(szName, W("_VtblGap"), 8) ) + { + dwMethodFlags |= mdRTSpecialName | mdSpecialName; + } + SetCallerDefine(); + IfFailGo(_SetMethodProps(*pmd, dwMethodFlags, ulCodeRVA, dwImplFlags)); + + IfFailGo(m_pStgdb->m_MiniMd.AddMemberDefToHash(*pmd, td) ); + +ErrExit: + SetCallerExternal(); + + STOP_MD_PERF(DefineMethod); + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineMethod + +//***************************************************************************** +// Create and set a MethodImpl Record. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineMethodImpl( // S_OK or error. + mdTypeDef td, // [IN] The class implementing the method + mdToken tkBody, // [IN] Method body, MethodDef or MethodRef + mdToken tkDecl) // [IN] Method declaration, MethodDef or MethodRef +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + MethodImplRec *pMethodImplRec = NULL; + RID iMethodImplRec; + + LOG((LOGMD, "MD RegMeta::DefineMethodImpl(0x%08x, 0x%08x, 0x%08x)\n", + td, tkBody, tkDecl)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + _ASSERTE(TypeFromToken(tkBody) == mdtMemberRef || TypeFromToken(tkBody) == mdtMethodDef); + _ASSERTE(TypeFromToken(tkDecl) == mdtMemberRef || TypeFromToken(tkDecl) == mdtMethodDef); + _ASSERTE(!IsNilToken(td) && !IsNilToken(tkBody) && !IsNilToken(tkDecl)); + + // Check for duplicates. + if (CheckDups(MDDupMethodDef)) + { + hr = ImportHelper::FindMethodImpl(&m_pStgdb->m_MiniMd, td, tkBody, tkDecl, NULL); + if (SUCCEEDED(hr)) + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + // Create the MethodImpl record. + IfFailGo(m_pStgdb->m_MiniMd.AddMethodImplRecord(&pMethodImplRec, &iMethodImplRec)); + + // Set the values. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, MethodImplRec::COL_Class, + pMethodImplRec, td)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodBody, + pMethodImplRec, tkBody)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodDeclaration, + pMethodImplRec, tkDecl)); + + IfFailGo( m_pStgdb->m_MiniMd.AddMethodImplToHash(iMethodImplRec) ); + + IfFailGo(UpdateENCLog2(TBL_MethodImpl, iMethodImplRec)); +ErrExit: + + STOP_MD_PERF(DefineMethodImpl); + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineMethodImpl + + +//***************************************************************************** +// Set or update RVA and ImplFlags for the given MethodDef or FieldDef record. +//***************************************************************************** +STDMETHODIMP RegMeta::SetMethodImplFlags( // [IN] S_OK or error. + mdMethodDef md, // [IN] Method for which to set impl flags + DWORD dwImplFlags) +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + MethodRec *pMethodRec; + + LOG((LOGMD, "MD RegMeta::SetMethodImplFlags(0x%08x, 0x%08x)\n", + md, dwImplFlags)); + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(TypeFromToken(md) == mdtMethodDef && dwImplFlags != UINT32_MAX); + + // Get the record. + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + pMethodRec->SetImplFlags(static_cast<USHORT>(dwImplFlags)); + + IfFailGo(UpdateENCLog(md)); + +ErrExit: + STOP_MD_PERF(SetMethodImplFlags); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetMethodImplFlags + + +//***************************************************************************** +// Set or update RVA and ImplFlags for the given MethodDef or FieldDef record. +//***************************************************************************** +STDMETHODIMP RegMeta::SetFieldRVA( // [IN] S_OK or error. + mdFieldDef fd, // [IN] Field for which to set offset + ULONG ulRVA) // [IN] The offset +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + FieldRVARec *pFieldRVARec; + RID iFieldRVA; + FieldRec *pFieldRec; + + LOG((LOGMD, "MD RegMeta::SetFieldRVA(0x%08x, 0x%08x)\n", + fd, ulRVA)); + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(TypeFromToken(fd) == mdtFieldDef); + + + IfFailGo(m_pStgdb->m_MiniMd.FindFieldRVAHelper(fd, &iFieldRVA)); + + if (InvalidRid(iFieldRVA)) + { + // turn on the has field RVA bit + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec)); + pFieldRec->AddFlags(fdHasFieldRVA); + + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddFieldRVARecord(&pFieldRVARec, &iFieldRVA)); + + // Set the data. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldRVA, FieldRVARec::COL_Field, + pFieldRVARec, fd)); + IfFailGo( m_pStgdb->m_MiniMd.AddFieldRVAToHash(iFieldRVA) ); + } + else + { + // Get the record. + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRVARecord(iFieldRVA, &pFieldRVARec)); + } + + // Set the data. + pFieldRVARec->SetRVA(ulRVA); + + IfFailGo(UpdateENCLog2(TBL_FieldRVA, iFieldRVA)); + +ErrExit: + STOP_MD_PERF(SetFieldRVA); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetFieldRVA + + +//***************************************************************************** +// Helper: Set or update RVA and ImplFlags for the given MethodDef or MethodImpl record. +//***************************************************************************** +HRESULT RegMeta::_SetRVA( // [IN] S_OK or error. + mdToken tk, // [IN] Member for which to set offset + ULONG ulCodeRVA, // [IN] The offset + DWORD dwImplFlags) +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + _ASSERTE(TypeFromToken(tk) == mdtMethodDef || TypeFromToken(tk) == mdtFieldDef); + _ASSERTE(!IsNilToken(tk)); + + if (TypeFromToken(tk) == mdtMethodDef) + { + MethodRec *pMethodRec; + + // Get the record. + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec)); + + // Set the data. + pMethodRec->SetRVA(ulCodeRVA); + + // Do not set the flag value unless its valid. + if (dwImplFlags != UINT32_MAX) + pMethodRec->SetImplFlags(static_cast<USHORT>(dwImplFlags)); + + IfFailGo(UpdateENCLog(tk)); + } + else // TypeFromToken(tk) == mdtFieldDef + { + _ASSERTE(dwImplFlags==0 || dwImplFlags==UINT32_MAX); + + FieldRVARec *pFieldRVARec; + RID iFieldRVA; + FieldRec *pFieldRec; + + IfFailGo(m_pStgdb->m_MiniMd.FindFieldRVAHelper(tk, &iFieldRVA)); + + if (InvalidRid(iFieldRVA)) + { + // turn on the has field RVA bit + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec)); + pFieldRec->AddFlags(fdHasFieldRVA); + + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddFieldRVARecord(&pFieldRVARec, &iFieldRVA)); + + // Set the data. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldRVA, FieldRVARec::COL_Field, + pFieldRVARec, tk)); + + IfFailGo( m_pStgdb->m_MiniMd.AddFieldRVAToHash(iFieldRVA) ); + + } + else + { + // Get the record. + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRVARecord(iFieldRVA, &pFieldRVARec)); + } + + // Set the data. + pFieldRVARec->SetRVA(ulCodeRVA); + + IfFailGo(UpdateENCLog2(TBL_FieldRVA, iFieldRVA)); + } + +ErrExit: + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::_SetRVA + +//***************************************************************************** +// Given a name, create a TypeRef. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineTypeRefByName( // S_OK or error. + mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef. + LPCWSTR szName, // [IN] Name of the TypeRef. + mdTypeRef *ptr) // [OUT] Put TypeRef token here. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD RegMeta::DefineTypeRefByName(0x%08x, %S, 0x%08x)\n", + tkResolutionScope, MDSTR(szName), ptr)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + // Common helper function does all of the work. + IfFailGo(_DefineTypeRef(tkResolutionScope, szName, TRUE, ptr)); + +ErrExit: + STOP_MD_PERF(DefineTypeRefByName); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineTypeRefByName + +//***************************************************************************** +// Create a reference, in an emit scope, to a TypeDef in another scope. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineImportType( // S_OK or error. + IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the TypeDef. + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + IMetaDataImport *pImport, // [IN] Scope containing the TypeDef. + mdTypeDef tdImport, // [IN] The imported TypeDef. + IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the TypeDef is imported. + mdTypeRef *ptr) // [OUT] Put TypeRef token here. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + IMetaDataImport2 *pImport2 = NULL; + IMDCommon *pImport2MDCommon = NULL; + + IMDCommon *pAssemImportMDCommon = NULL; + + RegMeta *pAssemEmitRM = NULL; + CMiniMdRW *pMiniMdAssemEmit = NULL; + CMiniMdRW *pMiniMdEmit = NULL; + + IMetaModelCommon *pAssemImportMetaModelCommon; + IMetaModelCommon *pImport2MetaModelCommon; + + LOG((LOGMD, "MD RegMeta::DefineImportType(0x%08x, 0x%08x, 0x%08x, 0x%08x, " + "0x%08x, 0x%08x, 0x%08x)\n", + pAssemImport, pbHashValue, cbHashValue, + pImport, tdImport, pAssemEmit, ptr)); + + START_MD_PERF(); + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + IfFailGo(pImport->QueryInterface(IID_IMetaDataImport2, (void**)&pImport2)); + + if (pAssemImport) + { + IfFailGo(pAssemImport->QueryInterface(IID_IMDCommon, (void**)&pAssemImportMDCommon)); + } + + pAssemImportMetaModelCommon = pAssemImportMDCommon ? pAssemImportMDCommon->GetMetaModelCommon() : 0; + + IfFailGo(pImport2->QueryInterface(IID_IMDCommon, (void**)&pImport2MDCommon)); + pImport2MetaModelCommon = pImport2MDCommon->GetMetaModelCommon(); + + pAssemEmitRM = static_cast<RegMeta*>(pAssemEmit); + pMiniMdAssemEmit = pAssemEmitRM ? static_cast<CMiniMdRW*>(&pAssemEmitRM->m_pStgdb->m_MiniMd) : 0; + pMiniMdEmit = &m_pStgdb->m_MiniMd; + + IfFailGo(ImportHelper::ImportTypeDef( + pMiniMdAssemEmit, + pMiniMdEmit, + pAssemImportMetaModelCommon, + pbHashValue, cbHashValue, + pImport2MetaModelCommon, + tdImport, + false, // Do not optimize to TypeDef if import and emit scopes are identical. + ptr)); + +ErrExit: + if (pImport2) + pImport2->Release(); + if (pImport2MDCommon) + pImport2MDCommon->Release(); + if (pAssemImportMDCommon) + pAssemImportMDCommon->Release(); + STOP_MD_PERF(DefineImportType); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineImportType + +//***************************************************************************** +// Create and set a MemberRef record. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineMemberRef( // S_OK or error + mdToken tkImport, // [IN] ClassRef or ClassDef importing a member. + LPCWSTR szName, // [IN] member's name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMemberRef *pmr) // [OUT] memberref token +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + MemberRefRec *pRecord = 0; // The MemberRef record. + RID iRecord; // RID of new MemberRef record. + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + + LOG((LOGMD, "MD RegMeta::DefineMemberRef(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + tkImport, MDSTR(szName), pvSigBlob, cbSigBlob, pmr)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tkImport) == mdtTypeRef || + TypeFromToken(tkImport) == mdtModuleRef || + TypeFromToken(tkImport) == mdtMethodDef || + TypeFromToken(tkImport) == mdtTypeSpec || + (TypeFromToken(tkImport) == mdtTypeDef) || + IsNilToken(tkImport)); + + _ASSERTE(szName && pvSigBlob && cbSigBlob && pmr); + + // _ASSERTE(_IsValidToken(tkImport)); + + // Set token to m_tdModule if referring to a global function. + if (IsNilToken(tkImport)) + tkImport = m_tdModule; + + // If the MemberRef already exists, just return the token, else + // create a new record. + if (CheckDups(MDDupMemberRef)) + { + hr = ImportHelper::FindMemberRef(&(m_pStgdb->m_MiniMd), tkImport, szNameUtf8, pvSigBlob, cbSigBlob, pmr); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(*pmr), &pRecord)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) // MemberRef exists + IfFailGo(hr); + } + + if (!pRecord) + { // Create the record. + IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefRecord(&pRecord, &iRecord)); + + // record the more defs are introduced. + SetMemberDefDirty(true); + + // Give token to caller. + *pmr = TokenFromRid(iRecord, mdtMemberRef); + } + + // Save row data. + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_MemberRef, MemberRefRec::COL_Name, pRecord, szNameUtf8)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pRecord, tkImport)); + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_MemberRef, MemberRefRec::COL_Signature, pRecord, + pvSigBlob, cbSigBlob)); + + IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefToHash(*pmr) ); + + IfFailGo(UpdateENCLog(*pmr)); + +ErrExit: + + STOP_MD_PERF(DefineMemberRef); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineMemberRef + +//***************************************************************************** +// Create a MemberRef record based on a member in an import scope. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineImportMember( // S_OK or error. + IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the Member. + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + IMetaDataImport *pImport, // [IN] Import scope, with member. + mdToken mbMember, // [IN] Member in import scope. + IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the Member is imported. + mdToken tkImport, // [IN] Classref or classdef in emit scope. + mdMemberRef *pmr) // [OUT] Put member ref here. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD RegMeta::DefineImportMember(" + "0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x," + " 0x%08x, 0x%08x, 0x%08x)\n", + pAssemImport, pbHashValue, cbHashValue, pImport, mbMember, + pAssemEmit, tkImport, pmr)); + START_MD_PERF(); + + // No need to lock this function. All the functions that it calls are public APIs. + + _ASSERTE(pImport && pmr); + _ASSERTE(TypeFromToken(tkImport) == mdtTypeRef || TypeFromToken(tkImport) == mdtModuleRef || + IsNilToken(tkImport) || TypeFromToken(tkImport) == mdtTypeSpec); + _ASSERTE((TypeFromToken(mbMember) == mdtMethodDef && mbMember != mdMethodDefNil) || + (TypeFromToken(mbMember) == mdtFieldDef && mbMember != mdFieldDefNil)); + + CQuickArray<WCHAR> qbMemberName; // Name of the imported member. + CQuickArray<WCHAR> qbScopeName; // Name of the imported member's scope. + GUID mvidImport; // MVID of the import module. + GUID mvidEmit; // MVID of the emit module. + ULONG cchName; // Length of a name, in wide chars. + PCCOR_SIGNATURE pvSig; // Member's signature. + ULONG cbSig; // Length of member's signature. + CQuickBytes cqbTranslatedSig; // Buffer for signature translation. + ULONG cbTranslatedSig; // Length of translated signature. + + if (TypeFromToken(mbMember) == mdtMethodDef) + { + do { + hr = pImport->GetMethodProps(mbMember, 0, qbMemberName.Ptr(),(DWORD)qbMemberName.MaxSize(),&cchName, + 0, &pvSig,&cbSig, 0,0); + if (hr == CLDB_S_TRUNCATION) + { + IfFailGo(qbMemberName.ReSizeNoThrow(cchName)); + continue; + } + break; + } while (1); + } + else // TypeFromToken(mbMember) == mdtFieldDef + { + do { + hr = pImport->GetFieldProps(mbMember, 0, qbMemberName.Ptr(),(DWORD)qbMemberName.MaxSize(),&cchName, + 0, &pvSig,&cbSig, 0,0, 0); + if (hr == CLDB_S_TRUNCATION) + { + IfFailGo(qbMemberName.ReSizeNoThrow(cchName)); + continue; + } + break; + } while (1); + } + IfFailGo(hr); + + IfFailGo(cqbTranslatedSig.ReSizeNoThrow(cbSig * 3)); // Set size conservatively. + + IfFailGo(TranslateSigWithScope( + pAssemImport, + pbHashValue, + cbHashValue, + pImport, + pvSig, + cbSig, + pAssemEmit, + static_cast<IMetaDataEmit*>(static_cast<IMetaDataEmit2*>(this)), + (COR_SIGNATURE *)cqbTranslatedSig.Ptr(), + cbSig * 3, + &cbTranslatedSig)); + + // Define ModuleRef for imported Member functions + + // Check if the Member being imported is a global function. + IfFailGo(GetScopeProps(0, 0, 0, &mvidEmit)); + IfFailGo(pImport->GetScopeProps(0, 0,&cchName, &mvidImport)); + if (mvidEmit != mvidImport && IsNilToken(tkImport)) + { + IfFailGo(qbScopeName.ReSizeNoThrow(cchName)); + IfFailGo(pImport->GetScopeProps(qbScopeName.Ptr(),(DWORD)qbScopeName.MaxSize(), + 0, 0)); + IfFailGo(DefineModuleRef(qbScopeName.Ptr(), &tkImport)); + } + + // Define MemberRef base on the name, sig, and parent + IfFailGo(DefineMemberRef( + tkImport, + qbMemberName.Ptr(), + reinterpret_cast<PCCOR_SIGNATURE>(cqbTranslatedSig.Ptr()), + cbTranslatedSig, + pmr)); + +ErrExit: + STOP_MD_PERF(DefineImportMember); + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineImportMember + +//***************************************************************************** +// Define and set a Event record. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineEvent( + mdTypeDef td, // [IN] the class/interface on which the event is being defined + LPCWSTR szEvent, // [IN] Name of the event + DWORD dwEventFlags, // [IN] CorEventAttr + mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef(to the Event class + mdMethodDef mdAddOn, // [IN] required add method + mdMethodDef mdRemoveOn, // [IN] required remove method + mdMethodDef mdFire, // [IN] optional fire method + mdMethodDef rmdOtherMethods[], // [IN] optional array of other methods associate with the event + mdEvent *pmdEvent) // [OUT] output event token +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD RegMeta::DefineEvent(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + td, szEvent, dwEventFlags, tkEventType, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods, pmdEvent)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && td != mdTypeDefNil); + _ASSERTE(IsNilToken(tkEventType) || TypeFromToken(tkEventType) == mdtTypeDef || + TypeFromToken(tkEventType) == mdtTypeRef || TypeFromToken(tkEventType) == mdtTypeSpec); + _ASSERTE(TypeFromToken(mdAddOn) == mdtMethodDef && mdAddOn != mdMethodDefNil); + _ASSERTE(TypeFromToken(mdRemoveOn) == mdtMethodDef && mdRemoveOn != mdMethodDefNil); + _ASSERTE(IsNilToken(mdFire) || TypeFromToken(mdFire) == mdtMethodDef); + _ASSERTE(szEvent && pmdEvent); + + hr = _DefineEvent(td, szEvent, dwEventFlags, tkEventType, pmdEvent); + if (hr != S_OK) + goto ErrExit; + + IfFailGo(_SetEventProps2(*pmdEvent, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods, IsENCOn())); + IfFailGo(UpdateENCLog(*pmdEvent)); +ErrExit: + + STOP_MD_PERF(DefineEvent); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineEvent + +//***************************************************************************** +// Set the ClassLayout information. +// +// If a row already exists for this class in the layout table, the layout +// information is overwritten. +//***************************************************************************** +STDMETHODIMP RegMeta::SetClassLayout( + mdTypeDef td, // [IN] typedef + DWORD dwPackSize, // [IN] packing size specified as 1, 2, 4, 8, or 16 + COR_FIELD_OFFSET rFieldOffsets[], // [IN] array of layout specification + ULONG ulClassSize) // [IN] size of the class +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + int index = 0; // Loop control. + + LOG((LOGMD, "MD RegMeta::SetClassLayout(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + td, dwPackSize, rFieldOffsets, ulClassSize)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + // Create entries in the FieldLayout table. + if (rFieldOffsets) + { + mdFieldDef tkfd; + // Iterate the list of fields... + for (index = 0; rFieldOffsets[index].ridOfField != mdFieldDefNil; index++) + { + if (rFieldOffsets[index].ulOffset != UINT32_MAX) + { + tkfd = TokenFromRid(rFieldOffsets[index].ridOfField, mdtFieldDef); + + IfFailGo(_SetFieldOffset(tkfd, rFieldOffsets[index].ulOffset)); + } + } + } + + IfFailGo(_SetClassLayout(td, dwPackSize, ulClassSize)); + +ErrExit: + + STOP_MD_PERF(SetClassLayout); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetClassLayout + +//***************************************************************************** +// Helper function to set a class layout for a given class. +//***************************************************************************** +HRESULT RegMeta::_SetClassLayout( // S_OK or error. + mdTypeDef td, // [IN] The class. + ULONG dwPackSize, // [IN] The packing size. + ULONG ulClassSize) // [IN, OPTIONAL] The class size. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; // A result. + ClassLayoutRec *pClassLayout; // A classlayout record. + RID iClassLayout = 0; // RID of classlayout record. + + // See if a ClassLayout record already exists for the given TypeDef. + IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &iClassLayout)); + + if (InvalidRid(iClassLayout)) + { + IfFailGo(m_pStgdb->m_MiniMd.AddClassLayoutRecord(&pClassLayout, &iClassLayout)); + // Set the Parent entry. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ClassLayout, ClassLayoutRec::COL_Parent, + pClassLayout, td)); + IfFailGo( m_pStgdb->m_MiniMd.AddClassLayoutToHash(iClassLayout) ); + } + else + { + IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(iClassLayout, &pClassLayout)); + } + + // Set the data. + if (dwPackSize != UINT32_MAX) + pClassLayout->SetPackingSize(static_cast<USHORT>(dwPackSize)); + if (ulClassSize != UINT32_MAX) + pClassLayout->SetClassSize(ulClassSize); + + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_ClassLayout, iClassLayout)); + +ErrExit: + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::_SetClassLayout + +//***************************************************************************** +// Helper function to set a field offset for a given field def. +//***************************************************************************** +HRESULT RegMeta::_SetFieldOffset( // S_OK or error. + mdFieldDef fd, // [IN] The field. + ULONG ulOffset) // [IN] The offset of the field. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr; + FieldLayoutRec * pFieldLayoutRec=0; // A FieldLayout record. + RID iFieldLayoutRec=0; // RID of a FieldLayout record. + + // See if an entry already exists for the Field in the FieldLayout table. + IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(fd, &iFieldLayoutRec)); + if (InvalidRid(iFieldLayoutRec)) + { + IfFailGo(m_pStgdb->m_MiniMd.AddFieldLayoutRecord(&pFieldLayoutRec, &iFieldLayoutRec)); + // Set the Field entry. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldLayout, FieldLayoutRec::COL_Field, + pFieldLayoutRec, fd)); + IfFailGo( m_pStgdb->m_MiniMd.AddFieldLayoutToHash(iFieldLayoutRec) ); + } + else + { + IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iFieldLayoutRec, &pFieldLayoutRec)); + } + + // Set the offset. + pFieldLayoutRec->SetOffSet(ulOffset); + + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_FieldLayout, iFieldLayoutRec)); + +ErrExit: + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::_SetFieldOffset + +//***************************************************************************** +// Delete the ClassLayout information. +//***************************************************************************** +STDMETHODIMP RegMeta::DeleteClassLayout( + mdTypeDef td) // [IN] typdef token +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ClassLayoutRec *pClassLayoutRec; + TypeDefRec *pTypeDefRec; + FieldLayoutRec *pFieldLayoutRec; + RID iClassLayoutRec; + RID iFieldLayoutRec; + RID ridStart; + RID ridEnd; + RID ridCur; + ULONG index; + + LOG((LOGMD, "MD RegMeta::DeleteClassLayout(0x%08x)\n", td)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(!m_bSaveOptimized && "Cannot change records after PreSave() and before Save()."); + _ASSERTE(TypeFromToken(td) == mdtTypeDef && !IsNilToken(td)); + + // Get the ClassLayout record. + IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &iClassLayoutRec)); + if (InvalidRid(iClassLayoutRec)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(iClassLayoutRec, &pClassLayoutRec)); + + // Clear the parent. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ClassLayout, + ClassLayoutRec::COL_Parent, + pClassLayoutRec, mdTypeDefNil)); + + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_ClassLayout, iClassLayoutRec)); + + // Delete all the corresponding FieldLayout records if there are any. + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd)); + + for (index = ridStart; index < ridEnd; index++) + { + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &ridCur)); + IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(TokenFromRid(ridCur, mdtFieldDef), &iFieldLayoutRec)); + if (InvalidRid(iFieldLayoutRec)) + continue; + else + { + IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iFieldLayoutRec, &pFieldLayoutRec)); + // Set the Field entry. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldLayout, FieldLayoutRec::COL_Field, + pFieldLayoutRec, mdFieldDefNil)); + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_FieldLayout, iFieldLayoutRec)); + } + } +ErrExit: + STOP_MD_PERF(DeleteClassLayout); + END_ENTRYPOINT_NOTHROW; + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::DeleteClassLayout + +//***************************************************************************** +// Set the field's native type. +//***************************************************************************** +STDMETHODIMP RegMeta::SetFieldMarshal( + mdToken tk, // [IN] given a fieldDef or paramDef token + PCCOR_SIGNATURE pvNativeType, // [IN] native type specification + ULONG cbNativeType) // [IN] count of bytes of pvNativeType +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD RegMeta::SetFieldMarshal(0x%08x, 0x%08x, 0x%08x)\n", + tk, pvNativeType, cbNativeType)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _SetFieldMarshal(tk, pvNativeType, cbNativeType); + +ErrExit: + STOP_MD_PERF(SetFieldMarshal); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetFieldMarshal + +HRESULT RegMeta::_SetFieldMarshal( + mdToken tk, // [IN] given a fieldDef or paramDef token + PCCOR_SIGNATURE pvNativeType, // [IN] native type specification + ULONG cbNativeType) // [IN] count of bytes of pvNativeType +{ + HRESULT hr = S_OK; + FieldMarshalRec *pFieldMarshRec; + RID iFieldMarshRec = 0; // initialize to invalid rid + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtParamDef); + _ASSERTE(!IsNilToken(tk)); + + // turn on the HasFieldMarshal + if (TypeFromToken(tk) == mdtFieldDef) + { + FieldRec *pFieldRec; + + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec)); + pFieldRec->AddFlags(fdHasFieldMarshal); + } + else // TypeFromToken(tk) == mdtParamDef + { + ParamRec *pParamRec; + + IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(tk), &pParamRec)); + pParamRec->AddFlags(pdHasFieldMarshal); + } + IfFailGo(UpdateENCLog(tk)); + + IfFailGo(m_pStgdb->m_MiniMd.FindFieldMarshalHelper(tk, &iFieldMarshRec)); + if (InvalidRid(iFieldMarshRec)) + { + IfFailGo(m_pStgdb->m_MiniMd.AddFieldMarshalRecord(&pFieldMarshRec, &iFieldMarshRec)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldMarshal, FieldMarshalRec::COL_Parent, pFieldMarshRec, tk)); + IfFailGo( m_pStgdb->m_MiniMd.AddFieldMarshalToHash(iFieldMarshRec) ); + } + else + { + IfFailGo(m_pStgdb->m_MiniMd.GetFieldMarshalRecord(iFieldMarshRec, &pFieldMarshRec)); + } + + // Set data. + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_FieldMarshal, FieldMarshalRec::COL_NativeType, pFieldMarshRec, + pvNativeType, cbNativeType)); + + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_FieldMarshal, iFieldMarshRec)); + +ErrExit: + + return hr; +} // RegMeta::_SetFieldMarshal + + +//***************************************************************************** +// Delete the FieldMarshal record for the given token. +//***************************************************************************** +STDMETHODIMP RegMeta::DeleteFieldMarshal( + mdToken tk) // [IN] fieldDef or paramDef token to be deleted. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + FieldMarshalRec *pFieldMarshRec; + RID iFieldMarshRec; + + + + LOG((LOGMD, "MD RegMeta::DeleteFieldMarshal(0x%08x)\n", tk)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtParamDef); + _ASSERTE(!IsNilToken(tk)); + _ASSERTE(!m_bSaveOptimized && "Cannot delete records after PreSave() and before Save()."); + + // Get the FieldMarshal record. + IfFailGo(m_pStgdb->m_MiniMd.FindFieldMarshalHelper(tk, &iFieldMarshRec)); + if (InvalidRid(iFieldMarshRec)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + IfFailGo(m_pStgdb->m_MiniMd.GetFieldMarshalRecord(iFieldMarshRec, &pFieldMarshRec)); + // Clear the parent token from the FieldMarshal record. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_FieldMarshal, + FieldMarshalRec::COL_Parent, pFieldMarshRec, mdFieldDefNil)); + + // turn off the HasFieldMarshal + if (TypeFromToken(tk) == mdtFieldDef) + { + FieldRec *pFieldRec; + + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec)); + pFieldRec->RemoveFlags(fdHasFieldMarshal); + } + else // TypeFromToken(tk) == mdtParamDef + { + ParamRec *pParamRec; + + IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(tk), &pParamRec)); + pParamRec->RemoveFlags(pdHasFieldMarshal); + } + + // Update the ENC log for the parent token. + IfFailGo(UpdateENCLog(tk)); + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_FieldMarshal, iFieldMarshRec)); + +ErrExit: + STOP_MD_PERF(DeleteFieldMarshal); + END_ENTRYPOINT_NOTHROW; + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::DeleteFieldMarshal + +//***************************************************************************** +// Define a new permission set for an object. +//***************************************************************************** +STDMETHODIMP RegMeta::DefinePermissionSet( + mdToken tk, // [IN] the object to be decorated. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] permission blob. + ULONG cbPermission, // [IN] count of bytes of pvPermission. + mdPermission *ppm) // [OUT] returned permission token. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::DefinePermissionSet(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + tk, dwAction, pvPermission, cbPermission, ppm)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + IfFailGo(_DefinePermissionSet(tk, dwAction, pvPermission, cbPermission, ppm)); + +ErrExit: + STOP_MD_PERF(DefinePermissionSet); + END_ENTRYPOINT_NOTHROW; + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::DefinePermissionSet + + +//***************************************************************************** +// Define a new permission set for an object. +//***************************************************************************** +HRESULT RegMeta::_DefinePermissionSet( + mdToken tk, // [IN] the object to be decorated. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] permission blob. + ULONG cbPermission, // [IN] count of bytes of pvPermission. + mdPermission *ppm) // [OUT] returned permission token. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + DeclSecurityRec *pDeclSec = NULL; + RID iDeclSec; + short sAction = static_cast<short>(dwAction); // To match with the type in DeclSecurityRec. + mdPermission tkPerm; // New permission token. + + _ASSERTE(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef || + TypeFromToken(tk) == mdtAssembly); + + // Check for valid Action. + if (sAction == 0 || sAction > dclMaximumValue) + IfFailGo(E_INVALIDARG); + + if (CheckDups(MDDupPermission)) + { + hr = ImportHelper::FindPermission(&(m_pStgdb->m_MiniMd), tk, sAction, &tkPerm); + + if (SUCCEEDED(hr)) + { + // Set output parameter. + if (ppm) + *ppm = tkPerm; + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkPerm), &pDeclSec)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + // Create a new record. + if (!pDeclSec) + { + IfFailGo(m_pStgdb->m_MiniMd.AddDeclSecurityRecord(&pDeclSec, &iDeclSec)); + tkPerm = TokenFromRid(iDeclSec, mdtPermission); + + // Set output parameter. + if (ppm) + *ppm = tkPerm; + + // Save parent and action information. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_DeclSecurity, DeclSecurityRec::COL_Parent, pDeclSec, tk)); + pDeclSec->SetAction(sAction); + + // Turn on the internal security flag on the parent. + if (TypeFromToken(tk) == mdtTypeDef) + IfFailGo(_TurnInternalFlagsOn(tk, tdHasSecurity)); + else if (TypeFromToken(tk) == mdtMethodDef) + IfFailGo(_TurnInternalFlagsOn(tk, mdHasSecurity)); + IfFailGo(UpdateENCLog(tk)); + } + + IfFailGo(_SetPermissionSetProps(tkPerm, sAction, pvPermission, cbPermission)); + IfFailGo(UpdateENCLog(tkPerm)); +ErrExit: + + STOP_MD_PERF(DefinePermissionSet); + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::_DefinePermissionSet + + + +//***************************************************************************** +// Set the RVA of a methoddef +//***************************************************************************** +STDMETHODIMP RegMeta::SetRVA( // [IN] S_OK or error. + mdToken md, // [IN] Member for which to set offset + ULONG ulRVA) // [IN] The offset#endif +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::SetRVA(0x%08x, 0x%08x)\n", + md, ulRVA)); + START_MD_PERF(); + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + IfFailGo(_SetRVA(md, ulRVA, UINT32_MAX)); // 0xbaad + +ErrExit: + STOP_MD_PERF(SetRVA); + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetRVA + +//***************************************************************************** +// Given a signature, return a token to the user. If there isn't an existing +// token, create a new record. This should more appropriately be called +// DefineSignature. +//***************************************************************************** +STDMETHODIMP RegMeta::GetTokenFromSig( // [IN] S_OK or error. + PCCOR_SIGNATURE pvSig, // [IN] Signature to define. + ULONG cbSig, // [IN] Size of signature data. + mdSignature *pmsig) // [OUT] returned signature token. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::GetTokenFromSig(0x%08x, 0x%08x, 0x%08x)\n", + pvSig, cbSig, pmsig)); + START_MD_PERF(); + + LOCKWRITE(); + + _ASSERTE(pmsig); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + IfFailGo(_GetTokenFromSig(pvSig, cbSig, pmsig)); + +ErrExit: + STOP_MD_PERF(GetTokenFromSig); + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::GetTokenFromSig + +//***************************************************************************** +// Define and set a ModuleRef record. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineModuleRef( // S_OK or error. + LPCWSTR szName, // [IN] DLL name + mdModuleRef *pmur) // [OUT] returned module ref token +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::DefineModuleRef(%S, 0x%08x)\n", MDSTR(szName), pmur)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _DefineModuleRef(szName, pmur); + +ErrExit: + STOP_MD_PERF(DefineModuleRef); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineModuleRef + +HRESULT RegMeta::_DefineModuleRef( // S_OK or error. + LPCWSTR szName, // [IN] DLL name + mdModuleRef *pmur) // [OUT] returned module ref token +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + ModuleRefRec *pModuleRef = 0; // The ModuleRef record. + RID iModuleRef; // Rid of new ModuleRef record. + LPUTF8 szUTF8Name; + UTF8STR((LPCWSTR)szName, szUTF8Name); + + _ASSERTE(szName && pmur); + + // See if the given ModuleRef already exists. If it exists just return. + // Else create a new record. + if (CheckDups(MDDupModuleRef)) + { + hr = ImportHelper::FindModuleRef(&(m_pStgdb->m_MiniMd), szUTF8Name, pmur); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetModuleRefRecord(RidFromToken(*pmur), &pModuleRef)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + if (!pModuleRef) + { + // Create new record and set the values. + IfFailGo(m_pStgdb->m_MiniMd.AddModuleRefRecord(&pModuleRef, &iModuleRef)); + + // Set the output parameter. + *pmur = TokenFromRid(iModuleRef, mdtModuleRef); + } + + // Save the data. + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, + pModuleRef, szUTF8Name)); + IfFailGo(UpdateENCLog(*pmur)); + +ErrExit: + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::_DefineModuleRef + +//***************************************************************************** +// Set the parent for the specified MemberRef. +//***************************************************************************** +STDMETHODIMP RegMeta::SetParent( // S_OK or error. + mdMemberRef mr, // [IN] Token for the ref to be fixed up. + mdToken tk) // [IN] The ref parent. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + MemberRefRec *pMemberRef; + + LOG((LOGMD, "MD RegMeta::SetParent(0x%08x, 0x%08x)\n", + mr, tk)); + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(TypeFromToken(mr) == mdtMemberRef); + _ASSERTE(IsNilToken(tk) || TypeFromToken(tk) == mdtTypeRef || TypeFromToken(tk) == mdtTypeDef || + TypeFromToken(tk) == mdtModuleRef || TypeFromToken(tk) == mdtMethodDef); + + IfFailGo(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(mr), &pMemberRef)); + + // If the token is nil set it to to m_tdModule. + tk = IsNilToken(tk) ? m_tdModule : tk; + + // Set the parent. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pMemberRef, tk)); + + // Add the updated MemberRef to the hash. + IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefToHash(mr) ); + + IfFailGo(UpdateENCLog(mr)); + +ErrExit: + + STOP_MD_PERF(SetParent); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetParent + +//***************************************************************************** +// Define an TypeSpec token given a type description. +//***************************************************************************** +STDMETHODIMP RegMeta::GetTokenFromTypeSpec( // [IN] S_OK or error. + PCCOR_SIGNATURE pvSig, // [IN] Signature to define. + ULONG cbSig, // [IN] Size of signature data. + mdTypeSpec *ptypespec) // [OUT] returned signature token. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + TypeSpecRec *pTypeSpecRec; + RID iRec; + + LOG((LOGMD, "MD RegMeta::GetTokenFromTypeSpec(0x%08x, 0x%08x, 0x%08x)\n", + pvSig, cbSig, ptypespec)); + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(ptypespec); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + if (CheckDups(MDDupTypeSpec)) + { + hr = ImportHelper::FindTypeSpec(&(m_pStgdb->m_MiniMd), pvSig, cbSig, ptypespec); + if (SUCCEEDED(hr)) + { + //@GENERICS: Generalizing from similar code in this file, should we not set + // hr = META_S_DUPLICATE; + // here? + goto ErrExit; + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddTypeSpecRecord(&pTypeSpecRec, &iRec)); + + // Set output parameter. + *ptypespec = TokenFromRid(iRec, mdtTypeSpec); + + // Set the signature field + IfFailGo(m_pStgdb->m_MiniMd.PutBlob( + TBL_TypeSpec, + TypeSpecRec::COL_Signature, + pTypeSpecRec, + pvSig, + cbSig)); + IfFailGo(UpdateENCLog(*ptypespec)); + +ErrExit: + + STOP_MD_PERF(GetTokenFromTypeSpec); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::GetTokenFromTypeSpec + +//***************************************************************************** +// This API defines a user literal string to be stored in the MetaData section. +// The token for this string has embedded in it the offset into the BLOB pool +// where the string is stored in UNICODE format. An additional byte is padded +// at the end to indicate whether the string has any characters that are >= 0x80. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineUserString( // S_OK or error. + LPCWSTR szString, // [IN] User literal string. + ULONG cchString, // [IN] Length of string. + mdString *pstk) // [OUT] String token. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + UINT32 nIndex; // Index into the user string heap. + CQuickBytes qb; // For storing the string with the byte prefix. + ULONG ulMemSize; // Size of memory taken by the string passed in. + PBYTE pb; // Pointer into memory allocated by qb. + + + + LOG((LOGMD, "MD RegMeta::DefineUserString(0x%08x, 0x%08x, 0x%08x)\n", + szString, cchString, pstk)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(pstk && szString && cchString != UINT32_MAX); + + + // Copy over the string to memory. + ulMemSize = cchString * sizeof(WCHAR); + IfFailGo(qb.ReSizeNoThrow(ulMemSize + 1)); + pb = reinterpret_cast<PBYTE>(qb.Ptr()); + memcpy(pb, szString, ulMemSize); + SwapStringLength((WCHAR *) pb, cchString); + // Always set the last byte of memory to indicate that there may be a 80+ or special character. + // This byte is not used by the runtime. + *(pb + ulMemSize) = 1; + + IfFailGo(m_pStgdb->m_MiniMd.PutUserString( + MetaData::DataBlob(pb, ulMemSize + 1), + &nIndex)); + + // Fail if the offset requires the high byte which is reserved for the token ID. + if (nIndex & 0xff000000) + IfFailGo(META_E_STRINGSPACE_FULL); + else + *pstk = TokenFromRid(nIndex, mdtString); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + STOP_MD_PERF(DefineUserString); + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineUserString + +//***************************************************************************** +// Delete a token. +// We only allow deleting a subset of tokens at this moment. These are TypeDef, +// MethodDef, FieldDef, Event, Property, and CustomAttribute. Except +// CustomAttribute, all the other tokens are named. We reserved a special +// name COR_DELETED_NAME_A to indicating a named record is deleted when +// xxRTSpecialName is set. +//***************************************************************************** + +STDMETHODIMP RegMeta::DeleteToken( + mdToken tkObj) // [IN] The token to be deleted +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::DeleteToken(0x%08x)\n", tkObj)); + START_MD_PERF(); + LOCKWRITE(); + + if (!IsValidToken(tkObj)) + IfFailGo( E_INVALIDARG ); + + // make sure that MetaData scope is opened for incremental compilation + if (!m_pStgdb->m_MiniMd.HasDelete()) + { + _ASSERTE( !"You cannot call delete token when you did not open the scope with proper Update flags in the SetOption!"); + IfFailGo( E_INVALIDARG ); + } + + _ASSERTE(!m_bSaveOptimized && "Cannot delete records after PreSave() and before Save()."); + + switch ( TypeFromToken(tkObj) ) + { + case mdtTypeDef: + { + TypeDefRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkObj), &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeDef, TypeDefRec::COL_Name, pRecord, COR_DELETED_NAME_A)); + pRecord->AddFlags(tdSpecialName | tdRTSpecialName); + break; + } + case mdtMethodDef: + { + MethodRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkObj), &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Method, MethodRec::COL_Name, pRecord, COR_DELETED_NAME_A)); + pRecord->AddFlags(mdSpecialName | mdRTSpecialName); + break; + } + case mdtFieldDef: + { + FieldRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tkObj), &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Field, FieldRec::COL_Name, pRecord, COR_DELETED_NAME_A)); + pRecord->AddFlags(fdSpecialName | fdRTSpecialName); + break; + } + case mdtEvent: + { + EventRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(tkObj), &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Event, EventRec::COL_Name, pRecord, COR_DELETED_NAME_A)); + pRecord->AddEventFlags(evSpecialName | evRTSpecialName); + break; + } + case mdtProperty: + { + PropertyRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(tkObj), &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Property, PropertyRec::COL_Name, pRecord, COR_DELETED_NAME_A)); + pRecord->AddPropFlags(prSpecialName | prRTSpecialName); + break; + } + case mdtExportedType: + { + ExportedTypeRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(tkObj), &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_ExportedType, ExportedTypeRec::COL_TypeName, pRecord, COR_DELETED_NAME_A)); + break; + } + case mdtCustomAttribute: + { + mdToken tkParent; + CustomAttributeRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkObj), &pRecord)); + + // replace the parent column of the custom value record to a nil token. + tkParent = m_pStgdb->m_MiniMd.getParentOfCustomAttribute(pRecord); + tkParent = TokenFromRid( mdTokenNil, TypeFromToken(tkParent) ); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, pRecord, tkParent)); + + // now the CustomAttribute table is no longer sorted + m_pStgdb->m_MiniMd.SetSorted(TBL_CustomAttribute, false); + break; + } + case mdtGenericParam: + { + mdToken tkParent; + GenericParamRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(RidFromToken(tkObj), &pRecord)); + + // replace the Parent column of the GenericParam record with a nil token. + tkParent = m_pStgdb->m_MiniMd.getOwnerOfGenericParam(pRecord); + tkParent = TokenFromRid( mdTokenNil, TypeFromToken(tkParent) ); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_GenericParam, GenericParamRec::COL_Owner, + pRecord, tkParent)); + + // now the GenericParam table is no longer sorted + m_pStgdb->m_MiniMd.SetSorted(TBL_GenericParam, false); + break; + } + case mdtGenericParamConstraint: + { + GenericParamConstraintRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintRecord(RidFromToken(tkObj), &pRecord)); + + // replace the Param column of the GenericParamConstraint record with zero RID. + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_GenericParamConstraint, + GenericParamConstraintRec::COL_Owner,pRecord, 0)); + // now the GenericParamConstraint table is no longer sorted + m_pStgdb->m_MiniMd.SetSorted(TBL_GenericParamConstraint, false); + break; + } + case mdtPermission: + { + mdToken tkParent; + mdToken tkNil; + DeclSecurityRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkObj), &pRecord)); + + // Replace the parent column of the permission record with a nil tokne. + tkParent = m_pStgdb->m_MiniMd.getParentOfDeclSecurity(pRecord); + tkNil = TokenFromRid( mdTokenNil, TypeFromToken(tkParent) ); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_DeclSecurity, DeclSecurityRec::COL_Parent, pRecord, tkNil )); + + // The table is no longer sorted. + m_pStgdb->m_MiniMd.SetSorted(TBL_DeclSecurity, false); + + // If the parent has no more security attributes, turn off the "has security" bit. + HCORENUM hEnum = 0; + mdPermission rPerms[1]; + ULONG cPerms = 0; + EnumPermissionSets(&hEnum, tkParent, 0 /* all actions */, rPerms, 1, &cPerms); + CloseEnum(hEnum); + if (cPerms == 0) + { + void *pRow; + ULONG ixTbl; + // Get the row for the parent object. + ixTbl = m_pStgdb->m_MiniMd.GetTblForToken(tkParent); + _ASSERTE(ixTbl >= 0 && ixTbl <= m_pStgdb->m_MiniMd.GetCountTables()); + IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, RidFromToken(tkParent), &pRow)); + + switch (TypeFromToken(tkParent)) + { + case mdtTypeDef: + reinterpret_cast<TypeDefRec*>(pRow)->RemoveFlags(tdHasSecurity); + break; + case mdtMethodDef: + reinterpret_cast<MethodRec*>(pRow)->RemoveFlags(mdHasSecurity); + break; + case mdtAssembly: + // No security bit. + break; + } + } + break; + } + default: + _ASSERTE(!"Bad token type!"); + IfFailGo( E_INVALIDARG ); + break; + } + + ErrExit: + + STOP_MD_PERF(DeleteToken); + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::DeleteToken + +//***************************************************************************** +// Set the properties on the given TypeDef token. +//***************************************************************************** +STDMETHODIMP RegMeta::SetTypeDefProps( // S_OK or error. + mdTypeDef td, // [IN] The TypeDef. + DWORD dwTypeDefFlags, // [IN] TypeDef flags. + mdToken tkExtends, // [IN] Base TypeDef or TypeRef. + mdToken rtkImplements[]) // [IN] Implemented interfaces. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::SetTypeDefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + td, dwTypeDefFlags, tkExtends, rtkImplements)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _SetTypeDefProps(td, dwTypeDefFlags, tkExtends, rtkImplements); + +ErrExit: + + STOP_MD_PERF(SetTypeDefProps); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetTypeDefProps + + +//***************************************************************************** +// Define a Nested Type. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineNestedType( // S_OK or error. + LPCWSTR szTypeDef, // [IN] Name of TypeDef + DWORD dwTypeDefFlags, // [IN] CustomAttribute flags + mdToken tkExtends, // [IN] extends this TypeDef or typeref + mdToken rtkImplements[], // [IN] Implements interfaces + mdTypeDef tdEncloser, // [IN] TypeDef token of the enclosing type. + mdTypeDef *ptd) // [OUT] Put TypeDef token here +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineNestedType(%S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + MDSTR(szTypeDef), dwTypeDefFlags, tkExtends, + rtkImplements, tdEncloser, ptd)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tdEncloser) == mdtTypeDef && !IsNilToken(tdEncloser)); + _ASSERTE(IsTdNested(dwTypeDefFlags)); + + IfFailGo(_DefineTypeDef(szTypeDef, dwTypeDefFlags, + tkExtends, rtkImplements, tdEncloser, ptd)); + +ErrExit: + STOP_MD_PERF(DefineNestedType); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineNestedType + +//***************************************************************************** +// Define a formal type parameter for the given TypeDef or MethodDef token. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineGenericParam( // S_OK or error. + mdToken tkOwner, // [IN] TypeDef or MethodDef + ULONG ulParamSeq, // [IN] Index of the type parameter + DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance) + LPCWSTR szName, // [IN] Name + DWORD reserved, // [IN] For future use + mdToken rtkConstraints[], // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec) + mdGenericParam *pgp) // [OUT] Put GenericParam token here +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + mdToken tkRet = mdGenericParamNil; + mdToken tkOwnerType = TypeFromToken(tkOwner); + + LOG((LOGMD, "RegMeta::DefineGenericParam(0x%08x, %d, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + tkOwner, ulParamSeq, dwParamFlags, szName, reserved, rtkConstraints, pgp)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + if (reserved != 0) + IfFailGo(META_E_BAD_INPUT_PARAMETER); + + // See if this version of the metadata can do Generics + if (!m_pStgdb->m_MiniMd.SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + if ((tkOwnerType == mdtTypeDef) || (tkOwnerType == mdtMethodDef)) + { + // 1. Find/create GP (unique tkOwner+ulParamSeq) = tkRet + GenericParamRec *pGenericParam = NULL; + RID iGenericParam,rid; + RID ridStart; + RID ridEnd; + + // See if this GenericParam has already been defined. + if (CheckDups(MDDupGenericParam)) + { + // Enumerate any GenericParams for the parent, looking for this sequence number. + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamsForToken(tkOwner, &ridStart, &ridEnd)); + for (rid = ridStart; rid < ridEnd; rid++) + { + iGenericParam = m_pStgdb->m_MiniMd.GetGenericParamRid(rid); + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(iGenericParam, &pGenericParam)); + // Is this the desired GenericParam #? + if (pGenericParam->GetNumber() == (USHORT)ulParamSeq) + { + tkRet = TokenFromRid(iGenericParam,mdtGenericParam); + // This is a duplicate. If not ENC, just return 'DUPLICATE'. If ENC, overwrite. + if (!IsENCOn()) + { + IfFailGo(META_S_DUPLICATE); + } + break; + } + } + } + else + { // Clear rid, ridStart, ridEnd, so we no we didn't find one. + rid = ridStart = ridEnd = 0; + } + + // If none was found, create one. + if(rid >= ridEnd) + { + IfFailGo(m_pStgdb->m_MiniMd.AddGenericParamRecord(&pGenericParam, &iGenericParam)); + pGenericParam->SetNumber((USHORT)ulParamSeq); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_GenericParam, GenericParamRec::COL_Owner, + pGenericParam, tkOwner)); + tkRet = TokenFromRid(iGenericParam,mdtGenericParam); + } + + // 2. Set its props + IfFailGo(_SetGenericParamProps(tkRet, pGenericParam, dwParamFlags, szName, reserved ,rtkConstraints)); + IfFailGo(UpdateENCLog(tkRet)); + } + else + hr = META_E_BAD_INPUT_PARAMETER; + +ErrExit: + + if(pgp != NULL) + *pgp = tkRet; + STOP_MD_PERF(DefineGenericParam); + + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineGenericParam + +//***************************************************************************** +// Set props of a formal type parameter. +//***************************************************************************** +STDMETHODIMP RegMeta::SetGenericParamProps( // S_OK or error. + mdGenericParam gp, // [IN] GenericParam + DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance) + LPCWSTR szName, // [IN] Optional name + DWORD reserved, // [IN] For future use (e.g. non-type parameters) + mdToken rtkConstraints[]) // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec) +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::SetGenericParamProps(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + gp, dwParamFlags,szName,reserved,rtkConstraints)); + START_MD_PERF(); + + if (reserved != 0) + IfFailGo(META_E_BAD_INPUT_PARAMETER); + + // See if this version of the metadata can do Generics + if (!m_pStgdb->m_MiniMd.SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + if (TypeFromToken(gp) == mdtGenericParam) + { + GenericParamRec *pGenericParam; + + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(RidFromToken(gp), &pGenericParam)); + IfFailGo(_SetGenericParamProps(gp,pGenericParam,dwParamFlags,szName,reserved,rtkConstraints)); + IfFailGo(UpdateENCLog(gp)); + } + else + hr = META_E_BAD_INPUT_PARAMETER; + +ErrExit: + STOP_MD_PERF(SetGenericParamProps); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetGenericParamProps + +//***************************************************************************** +// Set props of a formal type parameter (internal). +//***************************************************************************** +HRESULT RegMeta::_SetGenericParamProps( // S_OK or error. + mdGenericParam tkGP, // [IN] Formal parameter token + GenericParamRec *pGenericParam, // [IN] GenericParam record ptr + DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance) + LPCWSTR szName, // [IN] Optional name + DWORD reserved, // [IN] For future use (e.g. non-type parameters) + mdToken rtkConstraints[]) // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec) +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + if (pGenericParam != NULL) + { + // If there is a name, set it. + if ((szName != NULL) && (*szName != 0)) + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_GenericParam, GenericParamRec::COL_Name, + pGenericParam, szName)); + + // If there are new flags, set them. + if (dwParamFlags != (DWORD) -1) + pGenericParam->SetFlags((USHORT)dwParamFlags); + + // If there is a new array of constraints, apply it. + if (rtkConstraints != NULL) + { + //Clear existing constraints + GenericParamConstraintRec* pGPCRec; + RID ridGPC; + RID rid; + RID ridStart; + RID ridEnd; + + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintsForToken(tkGP, &ridStart, &ridEnd)); + for (rid = ridStart; rid < ridEnd; rid++) + { + ridGPC = m_pStgdb->m_MiniMd.GetGenericParamConstraintRid(rid); + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintRecord(ridGPC, &pGPCRec)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_GenericParamConstraint, + GenericParamConstraintRec::COL_Owner, + pGPCRec, 0)); + IfFailGo(UpdateENCLog(TokenFromRid(ridGPC,mdtGenericParamConstraint))); + } + + //Emit new constraints + mdToken* ptk; + for (ptk = rtkConstraints; (ptk != NULL)&&(RidFromToken(*ptk)!=0); ptk++) + { + IfFailGo(m_pStgdb->m_MiniMd.AddGenericParamConstraintRecord(&pGPCRec, &ridGPC)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_GenericParamConstraint, + GenericParamConstraintRec::COL_Owner, + pGPCRec, RidFromToken(tkGP))); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_GenericParamConstraint, + GenericParamConstraintRec::COL_Constraint, + pGPCRec, *ptk)); + IfFailGo(UpdateENCLog(TokenFromRid(ridGPC,mdtGenericParamConstraint))); + } + } + } + else + hr = META_E_BAD_INPUT_PARAMETER; + +ErrExit: + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::_SetGenericParamProps + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +//***************************************************************************** +// Get referenced type system metadata tables. +//***************************************************************************** +STDMETHODIMP RegMeta::GetReferencedTypeSysTables( // S_OK or error. + ULONG64 *refTables, // [OUT] Bit vector of referenced type system metadata tables. + ULONG refTableRows[], // [OUT] Array of number of rows for each referenced type system table. + const ULONG maxTableRowsSize, // [IN] Max size of the rows array. + ULONG *tableRowsSize) // [OUT] Size of the rows array. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::GetReferencedTypeSysTables()\n")); + START_MD_PERF(); + + ULONG64 refTablesBitVector = 0; + ULONG count = 0; + ULONG* ptr = NULL; + ULONG rowsSize = 0; + + for (ULONG i = 0; i < TBL_COUNT; i++) + { + if (m_pStgdb->m_MiniMd.m_Tables[i].GetRecordCount() > 0) + { + refTablesBitVector |= (ULONG64)1UL << i; + count++; + } + } + + _ASSERTE(count <= maxTableRowsSize); + if (count > maxTableRowsSize) + { + hr = META_E_BADMETADATA; + goto ErrExit; + } + + *refTables = refTablesBitVector; + *tableRowsSize = count; + + ptr = refTableRows; + for (ULONG i = 0; i < TBL_COUNT; i++) + { + rowsSize = m_pStgdb->m_MiniMd.m_Tables[i].GetRecordCount(); + if (rowsSize > 0) + *ptr++ = rowsSize; + } + +ErrExit: + STOP_MD_PERF(GetReferencedTypeSysTables); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::GetReferencedTypeSysTables + + +//***************************************************************************** +// Defines PDB stream data for portable PDB metadata +//***************************************************************************** +STDMETHODIMP RegMeta::DefinePdbStream( // S_OK or error. + PORT_PDB_STREAM* pdbStream) // [IN] Portable pdb stream data. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefinePdbStream()\n")); + START_MD_PERF(); + + IfFailGo(m_pStgdb->m_pPdbHeap->SetData(pdbStream)); + +ErrExit: + STOP_MD_PERF(DefinePdbStream); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefinePdbStream + +//***************************************************************************** +// Defines a document for portable PDB metadata +//***************************************************************************** +STDMETHODIMP RegMeta::DefineDocument( // S_OK or error. + char *docName, // [IN] Document name (string will be tokenized). + GUID *hashAlg, // [IN] Hash algorithm GUID. + BYTE *hashVal, // [IN] Hash value. + ULONG hashValSize, // [IN] Hash value size. + GUID *lang, // [IN] Language GUID. + mdDocument *docMdToken) // [OUT] Token of the defined document. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + char delim[2] = ""; + ULONG docNameBlobSize = 0; + ULONG docNameBlobMaxSize = 0; + BYTE* docNameBlob = NULL; + BYTE* docNameBlobPtr = NULL; + ULONG partsCount = 0; + ULONG partsIndexesCount = 0; + UINT32* partsIndexes = NULL; + UINT32* partsIndexesPtr = NULL; + char* stringToken = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineDocument(%s)\n", docName)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + // determine separator and number of separated parts + GetPathSeparator(docName, delim, &partsCount); + delim[1] = '\0'; + + // allocate the maximum size of a document blob + // treating each compressed index to take maximum of 4 bytes. + // the actual size will be calculated once we compress each index. + docNameBlobMaxSize = sizeof(char) + sizeof(ULONG) * partsCount; // (delim + 4 * partsCount) + docNameBlob = new BYTE[docNameBlobMaxSize]; + partsIndexes = new UINT32[partsCount]; + + // add path parts to blob heap and store their indexes + partsIndexesPtr = partsIndexes; + if (*delim == *docName) + { + // if the path starts with the delimiter (e.g. /home/user/...) store an empty string + *partsIndexesPtr++ = 0; + partsIndexesCount++; + } + stringToken = strtok(docName, (const char*)delim); + while (stringToken != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.m_BlobHeap.AddBlob(MetaData::DataBlob((BYTE*)stringToken, (ULONG)strlen(stringToken)), partsIndexesPtr++)); + stringToken = strtok(NULL, (const char*)delim); + partsIndexesCount++; + } + + _ASSERTE(partsIndexesCount == partsCount); + + // build up the documentBlob ::= separator part+ + docNameBlobPtr = docNameBlob; + // put separator + *docNameBlobPtr = delim[0]; + docNameBlobPtr++; + docNameBlobSize++; + // put part+: compress and put each part index + for (ULONG i = 0; i < partsCount; i++) + { + ULONG cnt = CorSigCompressData(partsIndexes[i], docNameBlobPtr); + docNameBlobPtr += cnt; + docNameBlobSize += cnt; + } + + _ASSERTE(docNameBlobSize <= docNameBlobMaxSize); + + // Add record + ULONG docRecord; + DocumentRec* pDocument; + IfFailGo(m_pStgdb->m_MiniMd.AddDocumentRecord(&pDocument, &docRecord)); + // Name column + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Document, DocumentRec::COL_Name, pDocument, docNameBlob, docNameBlobSize)); + // HashAlgorithm column + IfFailGo(m_pStgdb->m_MiniMd.PutGuid(TBL_Document, DocumentRec::COL_HashAlgorithm, pDocument, *hashAlg)); + // HashValue column + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Document, DocumentRec::COL_Hash, pDocument, hashVal, hashValSize)); + // Language column + IfFailGo(m_pStgdb->m_MiniMd.PutGuid(TBL_Document, DocumentRec::COL_Language, pDocument, *lang)); + + *docMdToken = TokenFromRid(docRecord, mdtDocument); + +ErrExit: + if (docNameBlob != NULL) + delete[] docNameBlob; + + if (partsIndexes != NULL) + delete[] partsIndexes; + + STOP_MD_PERF(DefineDocument); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineDocument + +//***************************************************************************** +// Defines sequence points for portable PDB metadata +//***************************************************************************** +STDMETHODIMP RegMeta::DefineSequencePoints( // S_OK or error. + ULONG docRid, // [IN] Document RID. + BYTE *sequencePtsBlob, // [IN] Sequence point blob. + ULONG sequencePtsBlobSize) // [IN] Sequence point blob size. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineSequencePoints()\n")); + START_MD_PERF(); + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + ULONG methodDbgInfoRec; + MethodDebugInformationRec* pMethodDbgInfo; + IfFailGo(m_pStgdb->m_MiniMd.AddMethodDebugInformationRecord(&pMethodDbgInfo, &methodDbgInfoRec)); + // Document column + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_MethodDebugInformation, + MethodDebugInformationRec::COL_Document, pMethodDbgInfo, docRid)); + // Sequence points column + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_MethodDebugInformation, + MethodDebugInformationRec::COL_SequencePoints, pMethodDbgInfo, sequencePtsBlob, sequencePtsBlobSize)); + +ErrExit: + STOP_MD_PERF(DefineSequencePoints); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineSequencePoints + +//***************************************************************************** +// Defines a local scope for portable PDB metadata +//***************************************************************************** +STDMETHODIMP RegMeta::DefineLocalScope( // S_OK or error. + ULONG methodDefRid, // [IN] Method RID. + ULONG importScopeRid, // [IN] Import scope RID. + ULONG firstLocalVarRid, // [IN] First local variable RID (of the continous run). + ULONG firstLocalConstRid, // [IN] First local constant RID (of the continous run). + ULONG startOffset, // [IN] Start offset of the scope. + ULONG length) // [IN] Scope length. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineLocalScope()\n")); + START_MD_PERF(); + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + ULONG localScopeRecord; + LocalScopeRec* pLocalScope; + IfFailGo(m_pStgdb->m_MiniMd.AddLocalScopeRecord(&pLocalScope, &localScopeRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_LocalScope, LocalScopeRec::COL_Method, pLocalScope, methodDefRid)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_LocalScope, LocalScopeRec::COL_ImportScope, pLocalScope, importScopeRid)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_LocalScope, LocalScopeRec::COL_VariableList, pLocalScope, firstLocalVarRid)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_LocalScope, LocalScopeRec::COL_ConstantList, pLocalScope, firstLocalConstRid)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_LocalScope, LocalScopeRec::COL_StartOffset, pLocalScope, startOffset)); + IfFailGo(m_pStgdb->m_MiniMd.PutCol(TBL_LocalScope, LocalScopeRec::COL_Length, pLocalScope, length)); + + // TODO: Force set sorted tables flag, do this properly + m_pStgdb->m_MiniMd.SetSorted(TBL_LocalScope, true); + +ErrExit: + STOP_MD_PERF(DefineLocalScope); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineLocalScope + +//***************************************************************************** +// Defines a local variable for portable PDB metadata +//***************************************************************************** +STDMETHODIMP RegMeta::DefineLocalVariable( // S_OK or error. + USHORT attribute, // [IN] Variable attribute. + USHORT index, // [IN] Variable index (slot). + char *name, // [IN] Variable name. + mdLocalVariable* locVarToken) // [OUT] Token of the defined variable. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineLocalVariable(%s)\n", name)); + START_MD_PERF(); + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + ULONG localVariableRecord; + LocalVariableRec* pLocalVariable; + IfFailGo(m_pStgdb->m_MiniMd.AddLocalVariableRecord(&pLocalVariable, &localVariableRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_LocalVariable, LocalVariableRec::COL_Name, pLocalVariable, name)); + + pLocalVariable->SetAttributes(attribute); + pLocalVariable->SetIndex(index); + + *locVarToken = TokenFromRid(localVariableRecord, mdtLocalVariable); + +ErrExit: + STOP_MD_PERF(DefineLocalVariable); + + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineLocalVariable +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + +//***************************************************************************** +// Create and set a MethodSpec record. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineMethodSpec( // S_OK or error + mdToken tkImport, // [IN] MethodDef or MemberRef + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodSpec *pmi) // [OUT] method instantiation token +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + MethodSpecRec *pRecord = 0; // The MethodSpec record. + RID iRecord; // RID of new MethodSpec record. + + + LOG((LOGMD, "MD RegMeta::DefineMethodSpec(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + tkImport, pvSigBlob, cbSigBlob, pmi)); + START_MD_PERF(); + LOCKWRITE(); + + // See if this version of the metadata can do Generics + if (!m_pStgdb->m_MiniMd.SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + // Check that it is a method, or at least memberref. + if ((TypeFromToken(tkImport) != mdtMethodDef) && (TypeFromToken(tkImport) != mdtMemberRef)) + IfFailGo(META_E_BAD_INPUT_PARAMETER); + + // Must have a signature, and someplace to return the token. + if ((pvSigBlob == NULL) || (cbSigBlob == 0) || (pmi == NULL)) + IfFailGo(META_E_BAD_INPUT_PARAMETER); + + // If the MethodSpec already exists, just return the token, else + // create a new record. + if (CheckDups(MDDupMethodSpec)) + { + hr = ImportHelper::FindMethodSpecByMethodAndInstantiation(&(m_pStgdb->m_MiniMd), tkImport,pvSigBlob, cbSigBlob, pmi); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) //GENERICS: is this correct? Do we really want to support ENC of MethodSpecs? + IfFailGo(m_pStgdb->m_MiniMd.GetMethodSpecRecord(RidFromToken(*pmi), &pRecord)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) // MemberRef exists + IfFailGo(hr); + } + + + if (!pRecord) + { // Create the record. + IfFailGo(m_pStgdb->m_MiniMd.AddMethodSpecRecord(&pRecord, &iRecord)); + + /*GENERICS: do we need to do anything like this? + Probably not, since SetMemberDefDirty is for ref to def optimization, and there are no method spec "refs". + // record the more defs are introduced. + SetMemberDefDirty(true); + */ + + // Give token to caller. + *pmi = TokenFromRid(iRecord, mdtMethodSpec); + } + + // Save row data. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSpec, MethodSpecRec::COL_Method, pRecord, tkImport)); + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_MethodSpec, MethodSpecRec::COL_Instantiation, pRecord, + pvSigBlob, cbSigBlob)); + /*@GENERICS: todo: update MethodSpec hash table */ + /* IfFailGo(m_pStgdb->m_MiniMd.AddMemberRefToHash(*pmi) ); */ + + IfFailGo(UpdateENCLog(*pmi)); + +ErrExit: + + STOP_MD_PERF(DefineMethodSpec); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineMethodSpec + +//***************************************************************************** +// Set the properties on the given Method token. +//***************************************************************************** +STDMETHODIMP RegMeta::SetMethodProps( // S_OK or error. + mdMethodDef md, // [IN] The MethodDef. + DWORD dwMethodFlags, // [IN] Method attributes. + ULONG ulCodeRVA, // [IN] Code RVA. + DWORD dwImplFlags) // [IN] MethodImpl flags. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::SetMethodProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + md, dwMethodFlags, ulCodeRVA, dwImplFlags)); + START_MD_PERF(); + LOCKWRITE(); + + if (dwMethodFlags != UINT32_MAX) + { + // Make sure no one sets the reserved bits on the way in. + _ASSERTE((dwMethodFlags & (mdReservedMask&~mdRTSpecialName)) == 0); + dwMethodFlags &= (~mdReservedMask); + } + + hr = _SetMethodProps(md, dwMethodFlags, ulCodeRVA, dwImplFlags); + +ErrExit: + + STOP_MD_PERF(SetMethodProps); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetMethodProps + +//***************************************************************************** +// Set the properties on the given Event token. +//***************************************************************************** +STDMETHODIMP RegMeta::SetEventProps( // S_OK or error. + mdEvent ev, // [IN] The event token. + DWORD dwEventFlags, // [IN] CorEventAttr. + mdToken tkEventType, // [IN] A reference (mdTypeRef or mdTypeRef) to the Event class. + mdMethodDef mdAddOn, // [IN] Add method. + mdMethodDef mdRemoveOn, // [IN] Remove method. + mdMethodDef mdFire, // [IN] Fire method. + mdMethodDef rmdOtherMethods[]) // [IN] Array of other methods associate with the event. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::SetEventProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + ev, dwEventFlags, tkEventType, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + IfFailGo(_SetEventProps1(ev, dwEventFlags, tkEventType)); + IfFailGo(_SetEventProps2(ev, mdAddOn, mdRemoveOn, mdFire, rmdOtherMethods, true)); + +ErrExit: + + STOP_MD_PERF(SetEventProps); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetEventProps + +//***************************************************************************** +// Set the properties on the given Permission token. +//***************************************************************************** +STDMETHODIMP RegMeta::SetPermissionSetProps( // S_OK or error. + mdToken tk, // [IN] The object to be decorated. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] Permission blob. + ULONG cbPermission, // [IN] Count of bytes of pvPermission. + mdPermission *ppm) // [OUT] Permission token. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + USHORT sAction = static_cast<USHORT>(dwAction); // Corresponding DeclSec field is a USHORT. + mdPermission tkPerm; + + LOG((LOGMD, "MD RegMeta::SetPermissionSetProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + tk, dwAction, pvPermission, cbPermission, ppm)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef || + TypeFromToken(tk) == mdtAssembly); + + // Check for valid Action. + if (dwAction == UINT32_MAX || dwAction == 0 || dwAction > dclMaximumValue) + IfFailGo(E_INVALIDARG); + + IfFailGo(ImportHelper::FindPermission(&(m_pStgdb->m_MiniMd), tk, sAction, &tkPerm)); + if (ppm) + *ppm = tkPerm; + IfFailGo(_SetPermissionSetProps(tkPerm, dwAction, pvPermission, cbPermission)); +ErrExit: + + STOP_MD_PERF(SetPermissionSetProps); + END_ENTRYPOINT_NOTHROW; + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::SetPermissionSetProps + +//***************************************************************************** +// This routine sets the p-invoke information for the specified Field or Method. +//***************************************************************************** +STDMETHODIMP RegMeta::DefinePinvokeMap( // Return code. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD dwMappingFlags, // [IN] Flags used for mapping. + LPCWSTR szImportName, // [IN] Import name. + mdModuleRef mrImportDLL) // [IN] ModuleRef token for the target DLL. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::DefinePinvokeMap(0x%08x, 0x%08x, %S, 0x%08x)\n", + tk, dwMappingFlags, MDSTR(szImportName), mrImportDLL)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _DefinePinvokeMap(tk, dwMappingFlags, szImportName, mrImportDLL); + +ErrExit: + + STOP_MD_PERF(DefinePinvokeMap); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefinePinvokeMap + +//***************************************************************************** +// Internal worker function for setting p-invoke info. +//***************************************************************************** +HRESULT RegMeta::_DefinePinvokeMap( // Return hresult. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD dwMappingFlags, // [IN] Flags used for mapping. + LPCWSTR szImportName, // [IN] Import name. + mdModuleRef mrImportDLL) // [IN] ModuleRef token for the target DLL. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + ImplMapRec *pRecord; + ULONG iRecord; + bool bDupFound = false; + HRESULT hr = S_OK; + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtMethodDef); + _ASSERTE(TypeFromToken(mrImportDLL) == mdtModuleRef); + _ASSERTE(RidFromToken(tk) && RidFromToken(mrImportDLL) && szImportName); + + // Turn on the quick lookup flag. + if (TypeFromToken(tk) == mdtMethodDef) + { + if (CheckDups(MDDupMethodDef)) + { + IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord)); + if (! InvalidRid(iRecord)) + bDupFound = true; + } + MethodRec *pMethod; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethod)); + pMethod->AddFlags(mdPinvokeImpl); + } + else // TypeFromToken(tk) == mdtFieldDef + { + if (CheckDups(MDDupFieldDef)) + { + IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord)); + if (!InvalidRid(iRecord)) + bDupFound = true; + } + FieldRec *pField; + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pField)); + pField->AddFlags(fdPinvokeImpl); + } + + // Create a new record. + if (bDupFound) + { + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(RidFromToken(iRecord), &pRecord)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else + { + IfFailGo(UpdateENCLog(tk)); + IfFailGo(m_pStgdb->m_MiniMd.AddImplMapRecord(&pRecord, &iRecord)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap, + ImplMapRec::COL_MemberForwarded, pRecord, tk)); + IfFailGo( m_pStgdb->m_MiniMd.AddImplMapToHash(iRecord) ); + + } + + // If no module, create a dummy, empty module. + if (IsNilToken(mrImportDLL)) + { + hr = ImportHelper::FindModuleRef(&m_pStgdb->m_MiniMd, "", &mrImportDLL); + if (hr == CLDB_E_RECORD_NOTFOUND) + IfFailGo(_DefineModuleRef(W(""), &mrImportDLL)); + } + + // Set the data. + if (dwMappingFlags != UINT32_MAX) + pRecord->SetMappingFlags(static_cast<USHORT>(dwMappingFlags)); + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_ImplMap, ImplMapRec::COL_ImportName, + pRecord, szImportName)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap, + ImplMapRec::COL_ImportScope, pRecord, mrImportDLL)); + + IfFailGo(UpdateENCLog2(TBL_ImplMap, iRecord)); + +ErrExit: + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefinePinvokeMap + +//***************************************************************************** +// This routine sets the p-invoke information for the specified Field or Method. +//***************************************************************************** +STDMETHODIMP RegMeta::SetPinvokeMap( // Return code. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD dwMappingFlags, // [IN] Flags used for mapping. + LPCWSTR szImportName, // [IN] Import name. + mdModuleRef mrImportDLL) // [IN] ModuleRef token for the target DLL. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ImplMapRec *pRecord; + ULONG iRecord; + + LOG((LOGMD, "MD RegMeta::SetPinvokeMap(0x%08x, 0x%08x, %S, 0x%08x)\n", + tk, dwMappingFlags, MDSTR(szImportName), mrImportDLL)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtMethodDef); + _ASSERTE(RidFromToken(tk)); + + IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord)); + + if (InvalidRid(iRecord)) + IfFailGo(CLDB_E_RECORD_NOTFOUND); + else + IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord)); + + // Set the data. + if (dwMappingFlags != UINT32_MAX) + pRecord->SetMappingFlags(static_cast<USHORT>(dwMappingFlags)); + if (szImportName) + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_ImplMap, ImplMapRec::COL_ImportName, + pRecord, szImportName)); + if (! IsNilToken(mrImportDLL)) + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap, ImplMapRec::COL_ImportScope, + pRecord, mrImportDLL)); + + IfFailGo(UpdateENCLog2(TBL_ImplMap, iRecord)); + +ErrExit: + + STOP_MD_PERF(SetPinvokeMap); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetPinvokeMap + +//***************************************************************************** +// This routine deletes the p-invoke record for the specified Field or Method. +//***************************************************************************** +STDMETHODIMP RegMeta::DeletePinvokeMap( // Return code. + mdToken tk) // [IN]FieldDef or MethodDef. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ImplMapRec *pRecord; + RID iRecord; + + LOG((LOGMD, "MD RegMeta::DeletePinvokeMap(0x%08x)\n", tk)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || TypeFromToken(tk) == mdtMethodDef); + _ASSERTE(!IsNilToken(tk)); + _ASSERTE(!m_bSaveOptimized && "Cannot delete records after PreSave() and before Save()."); + + // Get the PinvokeMap record. + IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord)); + if (InvalidRid(iRecord)) + { + IfFailGo(CLDB_E_RECORD_NOTFOUND); + } + IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord)); + + // Clear the MemberForwarded token from the PinvokeMap record. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ImplMap, + ImplMapRec::COL_MemberForwarded, pRecord, mdFieldDefNil)); + + // turn off the PinvokeImpl bit. + if (TypeFromToken(tk) == mdtFieldDef) + { + FieldRec *pFieldRec; + + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pFieldRec)); + pFieldRec->RemoveFlags(fdPinvokeImpl); + } + else // TypeFromToken(tk) == mdtMethodDef + { + MethodRec *pMethodRec; + + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec)); + pMethodRec->RemoveFlags(mdPinvokeImpl); + } + + // Update the ENC log for the parent token. + IfFailGo(UpdateENCLog(tk)); + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_ImplMap, iRecord)); + +ErrExit: + STOP_MD_PERF(DeletePinvokeMap); + END_ENTRYPOINT_NOTHROW; + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::DeletePinvokeMap + +//***************************************************************************** +// Create and define a new FieldDef record. +//***************************************************************************** +HRESULT RegMeta::DefineField( // S_OK or error. + mdTypeDef td, // Parent TypeDef + LPCWSTR szName, // Name of member + DWORD dwFieldFlags, // Member attributes + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_* + void const *pValue, // [IN] constant value + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdFieldDef *pmd) // [OUT] Put member token here +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + FieldRec *pRecord = NULL; // The new record. + RID iRecord; // RID of new record. + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + + LOG((LOGMD, "MD: RegMeta::DefineField(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + td, MDSTR(szName), dwFieldFlags, pvSigBlob, cbSigBlob, dwCPlusTypeFlag, pValue, cchValue, pmd)); + + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(pmd); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + IsGlobalMethodParent(&td); + + // Validate flags. + if (dwFieldFlags != UINT32_MAX) + { + // fdHasFieldRVA is settable, but not re-settable by applications. + _ASSERTE((dwFieldFlags & (fdReservedMask&~(fdHasFieldRVA|fdRTSpecialName))) == 0); + dwFieldFlags &= ~(fdReservedMask&~fdHasFieldRVA); + } + + // See if this field has already been defined as a forward reference + // from a MemberRef. If so, then update the data to match what we know now. + if (CheckDups(MDDupFieldDef)) + { + + hr = ImportHelper::FindField(&(m_pStgdb->m_MiniMd), + td, + szNameUtf8, + pvSigBlob, + cbSigBlob, + pmd); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(*pmd), &pRecord)); + } + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + { + IfFailGo(hr); + } + } + + // Create a new record. + if (pRecord == NULL) + { + // Create the field record. + IfFailGo(m_pStgdb->m_MiniMd.AddFieldRecord(&pRecord, &iRecord)); + + // Set output parameter pmd. + *pmd = TokenFromRid(iRecord, mdtFieldDef); + + // Add to parent's list of child records. + IfFailGo(m_pStgdb->m_MiniMd.AddFieldToTypeDef(RidFromToken(td), iRecord)); + + IfFailGo(UpdateENCLog(td, CMiniMdRW::eDeltaFieldCreate)); + + // record the more defs are introduced. + SetMemberDefDirty(true); + } + + // Set the Field properties. + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Field, FieldRec::COL_Name, pRecord, szNameUtf8)); + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Field, FieldRec::COL_Signature, pRecord, + pvSigBlob, cbSigBlob)); + + // Check to see if it is value__ for enum type + // <TODO>@FUTURE: shouldn't we have checked the type containing the field to be a Enum type first of all?</TODO> + // value__ is defined in corhdr.h. However, corhdr.h does not have the + // the W() macro we need (since it's distributed to windows). We substitute the values of the + // macro in the code below to work around this issue. + // #define COR_ENUM_FIELD_NAME_W L"value__" + + if (!wcscmp(szName, W("value__"))) + { + dwFieldFlags |= fdRTSpecialName | fdSpecialName; + } + SetCallerDefine(); + IfFailGo(_SetFieldProps(*pmd, dwFieldFlags, dwCPlusTypeFlag, pValue, cchValue)); + IfFailGo(m_pStgdb->m_MiniMd.AddMemberDefToHash(*pmd, td) ); + +ErrExit: + SetCallerExternal(); + + STOP_MD_PERF(DefineField); + + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineField + +//***************************************************************************** +// Define and set a Property record. +//***************************************************************************** +HRESULT RegMeta::DefineProperty( + mdTypeDef td, // [IN] the class/interface on which the property is being defined + LPCWSTR szProperty, // [IN] Name of the property + DWORD dwPropFlags, // [IN] CorPropertyAttr + PCCOR_SIGNATURE pvSig, // [IN] the required type signature + ULONG cbSig, // [IN] the size of the type signature blob + DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_* + void const *pValue, // [IN] constant value + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdMethodDef mdSetter, // [IN] optional setter of the property + mdMethodDef mdGetter, // [IN] optional getter of the property + mdMethodDef rmdOtherMethods[], // [IN] an optional array of other methods + mdProperty *pmdProp) // [OUT] output property token +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + PropertyRec *pPropRec = NULL; + RID iPropRec; + PropertyMapRec *pPropMap; + RID iPropMap; + LPUTF8 szUTF8Property; + UTF8STR(szProperty, szUTF8Property); + + LOG((LOGMD, "MD RegMeta::DefineProperty(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + td, szProperty, dwPropFlags, pvSig, cbSig, dwCPlusTypeFlag, pValue, cchValue, mdSetter, mdGetter, + rmdOtherMethods, pmdProp)); + + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && td != mdTypeDefNil && + szProperty && pvSig && cbSig && pmdProp); + + if (CheckDups(MDDupProperty)) + { + hr = ImportHelper::FindProperty(&(m_pStgdb->m_MiniMd), td, szUTF8Property, pvSig, cbSig, pmdProp); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(*pmdProp), &pPropRec)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + if (! pPropRec) + { + // Create a new map if one doesn't exist already, else retrieve the existing one. + // The property map must be created before the PropertyRecord, the new property + // map will be pointing past the first property record. + IfFailGo(m_pStgdb->m_MiniMd.FindPropertyMapFor(RidFromToken(td), &iPropMap)); + if (InvalidRid(iPropMap)) + { + // Create new record. + IfFailGo(m_pStgdb->m_MiniMd.AddPropertyMapRecord(&pPropMap, &iPropMap)); + // Set parent. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_PropertyMap, + PropertyMapRec::COL_Parent, pPropMap, td)); + IfFailGo(UpdateENCLog2(TBL_PropertyMap, iPropMap)); + } + else + { + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(iPropMap, &pPropMap)); + } + + // Create a new record. + IfFailGo(m_pStgdb->m_MiniMd.AddPropertyRecord(&pPropRec, &iPropRec)); + + // Set output parameter. + *pmdProp = TokenFromRid(iPropRec, mdtProperty); + + // Add Property to the PropertyMap. + IfFailGo(m_pStgdb->m_MiniMd.AddPropertyToPropertyMap(RidFromToken(iPropMap), iPropRec)); + + IfFailGo(UpdateENCLog2(TBL_PropertyMap, iPropMap, CMiniMdRW::eDeltaPropertyCreate)); + } + + // Save the data. + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Property, PropertyRec::COL_Type, pPropRec, + pvSig, cbSig)); + IfFailGo( m_pStgdb->m_MiniMd.PutString(TBL_Property, PropertyRec::COL_Name, + pPropRec, szUTF8Property) ); + + SetCallerDefine(); + IfFailGo(_SetPropertyProps(*pmdProp, dwPropFlags, dwCPlusTypeFlag, pValue, cchValue, mdSetter, + mdGetter, rmdOtherMethods)); + + // Add the <property token, typedef token> to the lookup table + if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Property)) + IfFailGo( m_pStgdb->m_MiniMd.AddPropertyToLookUpTable(*pmdProp, td) ); + +ErrExit: + SetCallerExternal(); + + STOP_MD_PERF(DefineProperty); + + END_ENTRYPOINT_NOTHROW; + + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineProperty + +//***************************************************************************** +// Create a record in the Param table. Any set of name, flags, or default value +// may be set. +//***************************************************************************** +HRESULT RegMeta::DefineParam( + mdMethodDef md, // [IN] Owning method + ULONG ulParamSeq, // [IN] Which param + LPCWSTR szName, // [IN] Optional param name + DWORD dwParamFlags, // [IN] Optional param flags + DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_* + void const *pValue, // [IN] constant value + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdParamDef *ppd) // [OUT] Put param token here +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + RID iRecord; + ParamRec *pRecord = 0; + + LOG((LOGMD, "MD RegMeta::DefineParam(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + md, ulParamSeq, MDSTR(szName), dwParamFlags, dwCPlusTypeFlag, pValue, cchValue, ppd)); + START_MD_PERF(); + LOCKWRITE(); + + _ASSERTE(TypeFromToken(md) == mdtMethodDef && md != mdMethodDefNil && + ulParamSeq != UINT32_MAX && ppd); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + // Retrieve or create the Param row. + if (CheckDups(MDDupParamDef)) + { + hr = _FindParamOfMethod(md, ulParamSeq, ppd); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(*ppd), &pRecord)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + if (!pRecord) + { + // Create the Param record. + IfFailGo(m_pStgdb->m_MiniMd.AddParamRecord(&pRecord, &iRecord)); + + // Set the output parameter. + *ppd = TokenFromRid(iRecord, mdtParamDef); + + // Set sequence number. + pRecord->SetSequence(static_cast<USHORT>(ulParamSeq)); + + // Add to the parent's list of child records. + IfFailGo(m_pStgdb->m_MiniMd.AddParamToMethod(RidFromToken(md), iRecord)); + + IfFailGo(UpdateENCLog(md, CMiniMdRW::eDeltaParamCreate)); + } + + SetCallerDefine(); + // Set the properties. + IfFailGo(_SetParamProps(*ppd, szName, dwParamFlags, dwCPlusTypeFlag, pValue, cchValue)); + +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + SetCallerExternal(); + + STOP_MD_PERF(DefineParam); + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::DefineParam + +//***************************************************************************** +// Set the properties on the given Field token. +//***************************************************************************** +HRESULT RegMeta::SetFieldProps( // S_OK or error. + mdFieldDef fd, // [IN] The FieldDef. + DWORD dwFieldFlags, // [IN] Field attributes. + DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue) // [IN] size of constant value (string, in wide chars). +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD: RegMeta::SetFieldProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + fd, dwFieldFlags, dwCPlusTypeFlag, pValue, cchValue)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + // Validate flags. + if (dwFieldFlags != UINT32_MAX) + { + // fdHasFieldRVA is settable, but not re-settable by applications. + _ASSERTE((dwFieldFlags & (fdReservedMask&~(fdHasFieldRVA|fdRTSpecialName))) == 0); + dwFieldFlags &= ~(fdReservedMask&~fdHasFieldRVA); + } + + hr = _SetFieldProps(fd, dwFieldFlags, dwCPlusTypeFlag, pValue, cchValue); + +ErrExit: + + STOP_MD_PERF(SetFieldProps); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetFieldProps + +//***************************************************************************** +// Set the properties on the given Property token. +//***************************************************************************** +HRESULT RegMeta::SetPropertyProps( // S_OK or error. + mdProperty pr, // [IN] Property token. + DWORD dwPropFlags, // [IN] CorPropertyAttr. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdMethodDef mdSetter, // [IN] Setter of the property. + mdMethodDef mdGetter, // [IN] Getter of the property. + mdMethodDef rmdOtherMethods[]) // [IN] Array of other methods. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD RegMeta::SetPropertyProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + pr, dwPropFlags, dwCPlusTypeFlag, pValue, cchValue, mdSetter, mdGetter, + rmdOtherMethods)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _SetPropertyProps(pr, dwPropFlags, dwCPlusTypeFlag, pValue, cchValue, mdSetter, mdGetter, rmdOtherMethods); + +ErrExit: + + STOP_MD_PERF(SetPropertyProps); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetPropertyProps + + +//***************************************************************************** +// This routine sets properties on the given Param token. +//***************************************************************************** +HRESULT RegMeta::SetParamProps( // Return code. + mdParamDef pd, // [IN] Param token. + LPCWSTR szName, // [IN] Param name. + DWORD dwParamFlags, // [IN] Param flags. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*. + void const *pValue, // [OUT] Constant value. + ULONG cchValue) // [IN] size of constant value (string, in wide chars). +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::SetParamProps(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + pd, MDSTR(szName), dwParamFlags, dwCPlusTypeFlag, pValue, cchValue)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _SetParamProps(pd, szName, dwParamFlags, dwCPlusTypeFlag, pValue, cchValue); + +ErrExit: + + STOP_MD_PERF(SetParamProps); + END_ENTRYPOINT_NOTHROW; + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::SetParamProps + +//***************************************************************************** +// Apply edit and continue changes to this metadata. +//***************************************************************************** +STDMETHODIMP RegMeta::ApplyEditAndContinue( // S_OK or error. + IUnknown *pUnk) // [IN] Metadata from the delta PE. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + IMetaDataImport2 *pImport=0; // Interface on the delta metadata. + RegMeta *pDeltaMD=0; // The delta metadata. + CMiniMdRW *mdDelta = NULL; + CMiniMdRW *mdBase = NULL; + + // Get the MiniMd on the delta. + IfFailGo(pUnk->QueryInterface(IID_IMetaDataImport2, (void**)&pImport)); + + pDeltaMD = static_cast<RegMeta*>(pImport); + + mdDelta = &(pDeltaMD->m_pStgdb->m_MiniMd); + mdBase = &(m_pStgdb->m_MiniMd); + + IfFailGo(mdBase->ConvertToRW()); + IfFailGo(mdBase->ApplyDelta(*mdDelta)); + +ErrExit: + if (pImport) + pImport->Release(); + END_ENTRYPOINT_NOTHROW; + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::ApplyEditAndContinue + +#endif //FEATURE_METADATA_EMIT diff --git a/src/coreclr/md/compiler/filtermanager.cpp b/src/coreclr/md/compiler/filtermanager.cpp new file mode 100644 index 00000000000..87df90334b7 --- /dev/null +++ b/src/coreclr/md/compiler/filtermanager.cpp @@ -0,0 +1,1457 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// FilterManager.cpp +// + +// +// contains utility code to MD directory +// +//***************************************************************************** +#include "stdafx.h" +#include "filtermanager.h" + +#define IsGlobalTypeDef(td) ((td) == TokenFromRid(mdtTypeDef, 1)) + +//***************************************************************************** +// Walk up to the containing tree and +// mark the transitive closure of the root token +//***************************************************************************** +HRESULT FilterManager::Mark(mdToken tk) +{ + HRESULT hr = NOERROR; + mdTypeDef td; + + // We hard coded System.Object as mdTypeDefNil + // The backing Field of property can be NULL as well. + if (RidFromToken(tk) == mdTokenNil) + goto ErrExit; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + switch ( TypeFromToken(tk) ) + { + case mdtTypeDef: + IfFailGo( MarkTypeDef(tk) ); + break; + + case mdtMethodDef: + // Get the typedef containing the MethodDef and mark the whole type + IfFailGo( m_pMiniMd->FindParentOfMethodHelper(tk, &td) ); + + // Global function so only mark the function itself and the typedef. + // Don't call MarkTypeDef. That will trigger all of the global methods/fields + // marked. + // + if (IsGlobalTypeDef(td)) + { + IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef(td) ); + IfFailGo( MarkMethod(tk) ); + } + else + { + IfFailGo( MarkTypeDef(td) ); + } + break; + + case mdtFieldDef: + // Get the typedef containing the FieldDef and mark the whole type + IfFailGo( m_pMiniMd->FindParentOfFieldHelper(tk, &td) ); + if (IsGlobalTypeDef(td)) + { + IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef(td) ); + IfFailGo( MarkField(tk) ); + } + else + { + IfFailGo( MarkTypeDef(td) ); + } + break; + + case mdtMemberRef: + IfFailGo( MarkMemberRef(tk) ); + break; + + case mdtTypeRef: + IfFailGo( MarkTypeRef(tk) ); + break; + + case mdtTypeSpec: + IfFailGo( MarkTypeSpec(tk) ); + break; + case mdtSignature: + IfFailGo( MarkStandAloneSig(tk) ); + break; + + case mdtModuleRef: + IfFailGo( MarkModuleRef(tk) ); + break; + + case mdtAssemblyRef: + IfFailGo( MarkAssemblyRef(tk) ); + break; + + case mdtModule: + IfFailGo( MarkModule(tk) ); + break; + + case mdtString: + IfFailGo( MarkUserString(tk) ); + break; + + case mdtBaseType: + // don't need to mark any base type. + break; + + case mdtAssembly: + IfFailGo( MarkAssembly(tk) ); + break; + + case mdtMethodSpec: + IfFailGo( MarkMethodSpec(tk) ); + break; + + case mdtProperty: + case mdtEvent: + case mdtParamDef: + case mdtInterfaceImpl: + default: + _ASSERTE(!" unknown type!"); + hr = E_INVALIDARG; + break; + } +ErrExit: + return hr; +} // HRESULT FilterManager::Mark() + + + +//***************************************************************************** +// marking only module property +//***************************************************************************** +HRESULT FilterManager::MarkAssembly(mdAssembly as) +{ + HRESULT hr = NOERROR; + + if (!hasAssemblyBeenMarked) + { + hasAssemblyBeenMarked = true; + IfFailGo( MarkCustomAttributesWithParentToken(as) ); + IfFailGo( MarkDeclSecuritiesWithParentToken(as) ); + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkAssembly() + + +//***************************************************************************** +// marking only module property +//***************************************************************************** +HRESULT FilterManager::MarkModule(mdModule mo) +{ + HRESULT hr = NOERROR; + + if (!hasModuleBeenMarked) + { + hasModuleBeenMarked = true; + IfFailGo( MarkCustomAttributesWithParentToken(mo) ); + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkModule() + + +//***************************************************************************** +// cascading Mark of a CustomAttribute +//***************************************************************************** +HRESULT FilterManager::MarkCustomAttribute(mdCustomAttribute cv) +{ + HRESULT hr = NOERROR; + CustomAttributeRec *pRec; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkCustomAttribute( cv ) ); + + // Mark the type (and any family) of the CustomAttribue. + IfFailGo(m_pMiniMd->GetCustomAttributeRecord(RidFromToken(cv), &pRec)); + IfFailGo( Mark(m_pMiniMd->getTypeOfCustomAttribute(pRec)) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkCustomAttribute() + + +//***************************************************************************** +// cascading Mark of a DeclSecurity +//***************************************************************************** +HRESULT FilterManager::MarkDeclSecurity(mdPermission pe) +{ + HRESULT hr = NOERROR; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkDeclSecurity( pe ) ); +ErrExit: + return hr; +} // HRESULT FilterManager::MarkDeclSecurity() + + + +//***************************************************************************** +// cascading Mark of a signature +//***************************************************************************** +HRESULT FilterManager::MarkStandAloneSig(mdSignature sig) +{ + HRESULT hr = NOERROR; + StandAloneSigRec *pRec; + ULONG cbSize; + ULONG cbUsed; + PCCOR_SIGNATURE pbSig; + IHostFilter *pFilter = m_pMiniMd->GetHostFilter(); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if TypeRef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsSignatureMarked(sig)) + goto ErrExit; + + // To mark the signature, we will need to mark + // all of the embedded TypeRef or TypeDef + // + IfFailGo( m_pMiniMd->GetFilterTable()->MarkSignature( sig ) ); + + if (pFilter) + pFilter->MarkToken(sig); + + // Walk the signature and mark all of the embedded types + IfFailGo(m_pMiniMd->GetStandAloneSigRecord(RidFromToken(sig), &pRec)); + IfFailGo(m_pMiniMd->getSignatureOfStandAloneSig(pRec, &pbSig, &cbSize)); + IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) ); + + IfFailGo( MarkCustomAttributesWithParentToken(sig) ); +ErrExit: + return hr; +} // HRESULT FilterManager::MarkStandAloneSig() + + + +//***************************************************************************** +// cascading Mark of a TypeSpec +//***************************************************************************** +HRESULT FilterManager::MarkTypeSpec(mdTypeSpec ts) +{ + HRESULT hr = NOERROR; + TypeSpecRec *pRec; + ULONG cbSize; + ULONG cbUsed; + PCCOR_SIGNATURE pbSig; + IHostFilter *pFilter = m_pMiniMd->GetHostFilter(); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if TypeRef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsTypeSpecMarked(ts)) + goto ErrExit; + + // To mark the TypeSpec, we will need to mark + // all of the embedded TypeRef or TypeDef + // + IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeSpec( ts ) ); + + if (pFilter) + pFilter->MarkToken(ts); + + // Walk the signature and mark all of the embedded types + IfFailGo(m_pMiniMd->GetTypeSpecRecord(RidFromToken(ts), &pRec)); + IfFailGo(m_pMiniMd->getSignatureOfTypeSpec(pRec, &pbSig, &cbSize)); + IfFailGo( MarkFieldSignature(pbSig, cbSize, &cbUsed) ); + IfFailGo( MarkCustomAttributesWithParentToken(ts) ); + + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkTypeSpec() + + + + +//***************************************************************************** +// cascading Mark of a TypeRef +//***************************************************************************** +HRESULT FilterManager::MarkTypeRef(mdTypeRef tr) +{ + HRESULT hr = NOERROR; + TOKENMAP *tkMap; + mdTypeDef td; + IHostFilter *pFilter = m_pMiniMd->GetHostFilter(); + TypeRefRec *pRec; + mdToken parentTk; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if TypeRef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsTypeRefMarked(tr)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeRef( tr ) ); + + if (pFilter) + pFilter->MarkToken(tr); + + IfFailGo(m_pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pRec)); + parentTk = m_pMiniMd->getResolutionScopeOfTypeRef(pRec); + if ( RidFromToken(parentTk) ) + { + IfFailGo( Mark( parentTk ) ); + } + + tkMap = m_pMiniMd->GetTypeRefToTypeDefMap(); + PREFIX_ASSUME(tkMap != NULL); + td = *(tkMap->Get(RidFromToken(tr))); + if ( td != mdTokenNil ) + { + // TypeRef is referring to a TypeDef within the same module. + // Mark the TypeDef as well. + // + IfFailGo( Mark(td) ); + } + + IfFailGo( MarkCustomAttributesWithParentToken(tr) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkTypeRef() + + +//***************************************************************************** +// cascading Mark of a MemberRef +//***************************************************************************** +HRESULT FilterManager::MarkMemberRef(mdMemberRef mr) +{ + HRESULT hr = NOERROR; + MemberRefRec *pRec; + ULONG cbSize; + ULONG cbUsed; + PCCOR_SIGNATURE pbSig; + IHostFilter *pFilter = m_pMiniMd->GetHostFilter(); + mdToken md; + TOKENMAP *tkMap; + mdToken tkParent; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if MemberRef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsMemberRefMarked(mr)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkMemberRef( mr ) ); + + if (pFilter) + pFilter->MarkToken(mr); + + IfFailGo(m_pMiniMd->GetMemberRefRecord(RidFromToken(mr), &pRec)); + + // we want to mark the parent of MemberRef as well + tkParent = m_pMiniMd->getClassOfMemberRef(pRec); + + // If the parent is the global TypeDef, mark only the TypeDef itself (low-level function). + // Other parents, do the transitive mark (ie, the high-level function). + // + if (IsGlobalTypeDef(tkParent)) + IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef( tkParent ) ); + else + IfFailGo( Mark( tkParent ) ); + + // Walk the signature and mark all of the embedded types + IfFailGo(m_pMiniMd->getSignatureOfMemberRef(pRec, &pbSig, &cbSize)); + IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) ); + + tkMap = m_pMiniMd->GetMemberRefToMemberDefMap(); + PREFIX_ASSUME(tkMap != NULL); + md = *(tkMap->Get(RidFromToken(mr))); // can be fielddef or methoddef + if ( RidFromToken(md) != mdTokenNil ) + { + // MemberRef is referring to either a FieldDef or MethodDef. + // If it is referring to MethodDef, we have fix the parent of MemberRef to be the MethodDef. + // However, if it is mapped to a FieldDef, the parent column does not track this information. + // Therefore we need to mark it explicitly. + // + IfFailGo( Mark(md) ); + } + + IfFailGo( MarkCustomAttributesWithParentToken(mr) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkMemberRef() + + +//***************************************************************************** +// cascading Mark of a UserString +//***************************************************************************** +HRESULT FilterManager::MarkUserString(mdString str) +{ + HRESULT hr = NOERROR; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if UserString is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsUserStringMarked(str)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkUserString( str ) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkUserString() + + +//***************************************************************************** +// Mark of a new UserString +//***************************************************************************** +HRESULT FilterManager::MarkNewUserString(mdString str) +{ + HRESULT hr = NOERROR; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkNewUserString( str ) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkUserString() + + +//***************************************************************************** +// cascading Mark of a MethodSpec +//***************************************************************************** +HRESULT FilterManager::MarkMethodSpec(mdMethodSpec ms) +{ + HRESULT hr = NOERROR; + MethodSpecRec *pRec; + ULONG cbSize; + ULONG cbUsed; + PCCOR_SIGNATURE pbSig; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if MethodSpec is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsMethodSpecMarked(ms)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkMethodSpec( ms ) ); + + // Mark MethodRef or MethodDef and embedded TypeRef and TypeDef tokens + + IfFailGo(m_pMiniMd->GetMethodSpecRecord(RidFromToken(ms), &pRec)); + + IfFailGo( Mark(m_pMiniMd->getMethodOfMethodSpec(pRec)) ); + + IfFailGo(m_pMiniMd->getInstantiationOfMethodSpec(pRec, &pbSig, &cbSize)); + IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkMethodSpec() + + +//***************************************************************************** +// cascading Mark of a ModuleRef +//***************************************************************************** +HRESULT FilterManager::MarkModuleRef(mdModuleRef mr) +{ + HRESULT hr = NOERROR; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if ModuleRef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsModuleRefMarked(mr)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkModuleRef( mr ) ); + IfFailGo( MarkCustomAttributesWithParentToken(mr) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkModuleRef() + + +//***************************************************************************** +// cascading Mark of a AssemblyRef +//***************************************************************************** +HRESULT FilterManager::MarkAssemblyRef(mdAssemblyRef ar) +{ + HRESULT hr = NOERROR; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if ModuleREf is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsAssemblyRefMarked(ar)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkAssemblyRef( ar ) ); + IfFailGo( MarkCustomAttributesWithParentToken(ar) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkAssemblyRef() + + +//***************************************************************************** +// cascading Mark of all of the custom values associated with a token +//***************************************************************************** +HRESULT FilterManager::MarkCustomAttributesWithParentToken(mdToken tkParent) +{ + HRESULT hr = NOERROR; + RID ridStart, ridEnd; + RID index; + CustomAttributeRec *pRec; + + if ( m_pMiniMd->IsSorted( TBL_CustomAttribute ) ) + { + // table is sorted. ridStart to ridEnd - 1 are all CustomAttribute + // associated with tkParent + // + IfFailGo(m_pMiniMd->getCustomAttributeForToken(tkParent, &ridEnd, &ridStart)); + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo( MarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) ); + } + } + else + { + // table scan is needed + ridStart = 1; + ridEnd = m_pMiniMd->getCountCustomAttributes() + 1; + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(m_pMiniMd->GetCustomAttributeRecord(index, &pRec)); + if ( tkParent == m_pMiniMd->getParentOfCustomAttribute(pRec) ) + { + // This CustomAttribute is associated with tkParent + IfFailGo( MarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) ); + } + } + } + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkCustomAttributesWithParentToken() + + +//***************************************************************************** +// cascading Mark of all securities associated with a token +//***************************************************************************** +HRESULT FilterManager::MarkDeclSecuritiesWithParentToken(mdToken tkParent) +{ + HRESULT hr = NOERROR; + RID ridStart, ridEnd; + RID index; + DeclSecurityRec *pRec; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + if ( m_pMiniMd->IsSorted( TBL_DeclSecurity ) ) + { + // table is sorted. ridStart to ridEnd - 1 are all DeclSecurity + // associated with tkParent + // + IfFailGo(m_pMiniMd->getDeclSecurityForToken(tkParent, &ridEnd, &ridStart)); + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo( m_pMiniMd->GetFilterTable()->MarkDeclSecurity( TokenFromRid(index, mdtPermission) ) ); + } + } + else + { + // table scan is needed + ridStart = 1; + ridEnd = m_pMiniMd->getCountDeclSecuritys() + 1; + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(m_pMiniMd->GetDeclSecurityRecord(index, &pRec)); + if ( tkParent == m_pMiniMd->getParentOfDeclSecurity(pRec) ) + { + // This DeclSecurity is associated with tkParent + IfFailGo( m_pMiniMd->GetFilterTable()->MarkDeclSecurity( TokenFromRid(index, mdtPermission) ) ); + } + } + } + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkDeclSecuritiesWithParentToken() + + +//***************************************************************************** +// cascading Mark of all MemberRefs associated with a parent token +//***************************************************************************** +HRESULT FilterManager::MarkMemberRefsWithParentToken(mdToken tk) +{ + HRESULT hr = NOERROR; + RID ulEnd; + RID index; + mdToken tkParent; + MemberRefRec *pRec; + + ulEnd = m_pMiniMd->getCountMemberRefs(); + + for (index = 1; index <= ulEnd; index ++ ) + { + // memberRef table is not sorted. Table scan is needed. + IfFailGo(m_pMiniMd->GetMemberRefRecord(index, &pRec)); + tkParent = m_pMiniMd->getClassOfMemberRef(pRec); + if ( tk == tkParent ) + { + IfFailGo( MarkMemberRef( TokenFromRid(index, mdtMemberRef) ) ); + } + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkMemberRefsWithParentToken() + + +//***************************************************************************** +// cascading Mark of a ParamDef token +//***************************************************************************** +HRESULT FilterManager::MarkParam(mdParamDef pd) +{ + HRESULT hr; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkParam( pd ) ); + + IfFailGo( MarkCustomAttributesWithParentToken(pd) ); + // Parameter does not have declsecurity + // IfFailGo( MarkDeclSecuritiesWithParentToken(pd) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkParam() + + +//***************************************************************************** +// cascading Mark of a method token +//***************************************************************************** +HRESULT FilterManager::MarkMethod(mdMethodDef md) +{ + HRESULT hr = NOERROR; + MethodRec *pRec; + ULONG cbSize; + ULONG cbUsed; + PCCOR_SIGNATURE pbSig; + ULONG i, iCount; + ImplMapRec *pImplMapRec = NULL; + mdMethodDef mdImp; + mdModuleRef mrImp; + IHostFilter *pFilter = m_pMiniMd->GetHostFilter(); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if MethodDef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsMethodMarked(md)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkMethod( md ) ); + if (pFilter) + pFilter->MarkToken(md); + + IfFailGo( MarkParamsWithParentToken(md) ); + + // mark any GenericParam of this Method + IfFailGo( MarkGenericParamWithParentToken(md) ); + + + // Walk the signature and mark all of the embedded types + IfFailGo(m_pMiniMd->GetMethodRecord(RidFromToken(md), &pRec)); + IfFailGo(m_pMiniMd->getSignatureOfMethod(pRec, &pbSig, &cbSize)); + IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) ); + + iCount = m_pMiniMd->getCountImplMaps(); + + // loop through all ImplMaps and find the Impl map associated with this method def tokens + // and mark the Module Ref tokens in the entries + // + for (i = 1; i <= iCount; i++) + { + IfFailGo(m_pMiniMd->GetImplMapRecord(i, &pImplMapRec)); + + // Get the MethodDef that the impl map is associated with + mdImp = m_pMiniMd->getMemberForwardedOfImplMap(pImplMapRec); + + if (mdImp != md) + { + // Impl Map entry does not associated with the method def that we are marking + continue; + } + + // Get the ModuleRef token + mrImp = m_pMiniMd->getImportScopeOfImplMap(pImplMapRec); + IfFailGo( Mark(mrImp) ); + } + + // We should not mark all of the memberref with the parent of this methoddef token. + // Because not all of the call sites are needed. + // + // IfFailGo( MarkMemberRefsWithParentToken(md) ); + IfFailGo( MarkCustomAttributesWithParentToken(md) ); + IfFailGo( MarkDeclSecuritiesWithParentToken(md) ); +ErrExit: + return hr; +} // HRESULT FilterManager::MarkMethod() + + +//***************************************************************************** +// cascading Mark of a field token +//***************************************************************************** +HRESULT FilterManager::MarkField(mdFieldDef fd) +{ + HRESULT hr = NOERROR; + FieldRec *pRec; + ULONG cbSize; + ULONG cbUsed; + PCCOR_SIGNATURE pbSig; + IHostFilter *pFilter = m_pMiniMd->GetHostFilter(); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if FieldDef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsFieldMarked(fd)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkField( fd ) ); + if (pFilter) + pFilter->MarkToken(fd); + + // We should not mark all of the MemberRef with the parent of this FieldDef token. + // Because not all of the call sites are needed. + // + + // Walk the signature and mark all of the embedded types + IfFailGo(m_pMiniMd->GetFieldRecord(RidFromToken(fd), &pRec)); + IfFailGo(m_pMiniMd->getSignatureOfField(pRec, &pbSig, &cbSize)); + IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) ); + + IfFailGo( MarkCustomAttributesWithParentToken(fd) ); + // IfFailGo( MarkDeclSecuritiesWithParentToken(fd) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkField() + + +//***************************************************************************** +// cascading Mark of an event token +//***************************************************************************** +HRESULT FilterManager::MarkEvent(mdEvent ev) +{ + HRESULT hr = NOERROR; + EventRec *pRec; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if Event is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsEventMarked(ev)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkEvent( ev ) ); + + // mark the event type as well + IfFailGo(m_pMiniMd->GetEventRecord(RidFromToken(ev), &pRec)); + IfFailGo( Mark(m_pMiniMd->getEventTypeOfEvent(pRec)) ); + + // Note that we don't need to mark the MethodSemantics. Because the association of MethodSemantics + // is marked. The Method column can only store MethodDef, ie the MethodDef has the same parent as + // this Event. + + IfFailGo( MarkCustomAttributesWithParentToken(ev) ); + // IfFailGo( MarkDeclSecuritiesWithParentToken(ev) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkEvent() + + + +//***************************************************************************** +// cascading Mark of a Property token +//***************************************************************************** +HRESULT FilterManager::MarkProperty(mdProperty pr) +{ + HRESULT hr = NOERROR; + PropertyRec *pRec; + ULONG cbSize; + ULONG cbUsed; + PCCOR_SIGNATURE pbSig; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if Property is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsPropertyMarked(pr)) + goto ErrExit; + + IfFailGo( m_pMiniMd->GetFilterTable()->MarkProperty( pr ) ); + + // marking the backing field, event changing and event changed + IfFailGo(m_pMiniMd->GetPropertyRecord(RidFromToken(pr), &pRec)); + + // Walk the signature and mark all of the embedded types + IfFailGo(m_pMiniMd->getTypeOfProperty(pRec, &pbSig, &cbSize)); + IfFailGo( MarkSignature(pbSig, cbSize, &cbUsed) ); + + // Note that we don't need to mark the MethodSemantics. Because the association of MethodSemantics + // is marked. The Method column can only store MethodDef, ie the MethodDef has the same parent as + // this Property. + + IfFailGo( MarkCustomAttributesWithParentToken(pr) ); + // IfFailGo( MarkDeclSecuritiesWithParentToken(pr) ); + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkProperty() + +//***************************************************************************** +// cascading Mark of all ParamDef associated with a methoddef +//***************************************************************************** +HRESULT FilterManager::MarkParamsWithParentToken(mdMethodDef md) +{ + HRESULT hr = NOERROR; + RID ulStart, ulEnd; + RID index; + MethodRec *pMethodRec; + + IfFailGo(m_pMiniMd->GetMethodRecord(RidFromToken(md), &pMethodRec)); + + // figure out the start rid and end rid of the parameter list of this methoddef + ulStart = m_pMiniMd->getParamListOfMethod(pMethodRec); + IfFailGo(m_pMiniMd->getEndParamListOfMethod(RidFromToken(md), &ulEnd)); + for (index = ulStart; index < ulEnd; index ++ ) + { + RID rid; + IfFailGo(m_pMiniMd->GetParamRid(index, &rid)); + IfFailGo(MarkParam(TokenFromRid( + rid, + mdtParamDef))); + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkParamsWithParentToken() + + +//***************************************************************************** +// cascading Mark of all methods associated with a TypeDef token +//***************************************************************************** +HRESULT FilterManager::MarkMethodsWithParentToken(mdTypeDef td) +{ + HRESULT hr = NOERROR; + RID ulStart, ulEnd; + RID index; + TypeDefRec *pTypeDefRec; + + IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + ulStart = m_pMiniMd->getMethodListOfTypeDef( pTypeDefRec ); + IfFailGo(m_pMiniMd->getEndMethodListOfTypeDef(RidFromToken(td), &ulEnd)); + for ( index = ulStart; index < ulEnd; index ++ ) + { + RID rid; + IfFailGo(m_pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(MarkMethod(TokenFromRid( + rid, + mdtMethodDef))); + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkMethodsWithParentToken() + + +//***************************************************************************** +// cascading Mark of all MethodImpls associated with a TypeDef token +//***************************************************************************** +HRESULT FilterManager::MarkMethodImplsWithParentToken(mdTypeDef td) +{ + HRESULT hr = NOERROR; + RID index; + mdToken tkBody; + mdToken tkDecl; + MethodImplRec *pMethodImplRec; + HENUMInternal hEnum; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + HENUMInternal::ZeroEnum(&hEnum); + IfFailGo( m_pMiniMd->FindMethodImplHelper(td, &hEnum) ); + + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&index)) + { + IfFailGo(m_pMiniMd->GetMethodImplRecord(index, &pMethodImplRec)); + IfFailGo(m_pMiniMd->GetFilterTable()->MarkMethodImpl(index)); + + tkBody = m_pMiniMd->getMethodBodyOfMethodImpl(pMethodImplRec); + IfFailGo( Mark(tkBody) ); + + tkDecl = m_pMiniMd->getMethodDeclarationOfMethodImpl(pMethodImplRec); + IfFailGo( Mark(tkDecl) ); + } +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + return hr; +} // HRESULT FilterManager::MarkMethodImplsWithParentToken() + + +//***************************************************************************** +// cascading Mark of all fields associated with a TypeDef token +//***************************************************************************** +HRESULT FilterManager::MarkFieldsWithParentToken(mdTypeDef td) +{ + HRESULT hr = NOERROR; + RID ulStart, ulEnd; + RID index; + TypeDefRec *pTypeDefRec; + + IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + ulStart = m_pMiniMd->getFieldListOfTypeDef( pTypeDefRec ); + IfFailGo(m_pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ulEnd)); + for ( index = ulStart; index < ulEnd; index ++ ) + { + RID rid; + IfFailGo(m_pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(MarkField(TokenFromRid( + rid, + mdtFieldDef))); + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkFieldsWithParentToken() + + +//***************************************************************************** +// cascading Mark of all events associated with a TypeDef token +//***************************************************************************** +HRESULT FilterManager::MarkEventsWithParentToken( + mdTypeDef td) +{ + HRESULT hr = NOERROR; + RID ridEventMap; + RID ulStart, ulEnd; + RID index; + EventMapRec *pEventMapRec; + + // get the starting/ending rid of Events of this typedef + IfFailGo(m_pMiniMd->FindEventMapFor(RidFromToken(td), &ridEventMap)); + if ( !InvalidRid(ridEventMap) ) + { + IfFailGo(m_pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec)); + ulStart = m_pMiniMd->getEventListOfEventMap( pEventMapRec ); + IfFailGo(m_pMiniMd->getEndEventListOfEventMap(ridEventMap, &ulEnd)); + for ( index = ulStart; index < ulEnd; index ++ ) + { + RID rid; + IfFailGo(m_pMiniMd->GetEventRid(index, &rid)); + IfFailGo(MarkEvent(TokenFromRid( + rid, + mdtEvent))); + } + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkEventsWithParentToken() + + + +//***************************************************************************** +// cascading Mark of all properties associated with a TypeDef token +//***************************************************************************** +HRESULT FilterManager::MarkPropertiesWithParentToken( + mdTypeDef td) +{ + HRESULT hr = NOERROR; + RID ridPropertyMap; + RID ulStart, ulEnd; + RID index; + PropertyMapRec *pPropertyMapRec; + + // get the starting/ending rid of properties of this typedef + IfFailGo(m_pMiniMd->FindPropertyMapFor(RidFromToken(td), &ridPropertyMap)); + if ( !InvalidRid(ridPropertyMap) ) + { + IfFailGo(m_pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ulStart = m_pMiniMd->getPropertyListOfPropertyMap( pPropertyMapRec ); + IfFailGo(m_pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ulEnd)); + for ( index = ulStart; index < ulEnd; index ++ ) + { + RID rid; + IfFailGo(m_pMiniMd->GetPropertyRid(index, &rid)); + IfFailGo(MarkProperty(TokenFromRid( + rid, + mdtProperty))); + } + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkPropertiesWithParentToken() + + +//***************************************************************************** +// cascading Mark of all GenericPar associated with a TypeDef or MethodDef token +//***************************************************************************** +HRESULT FilterManager::MarkGenericParamWithParentToken( + mdToken tk) +{ + HRESULT hr = NOERROR; + RID ulStart, ulEnd; + RID index; + GenericParamRec *pGenericParamRec; + mdToken constraint; + HENUMInternal hEnum; // To enumerate constraints. + + // Enumerate the GenericPar + //@todo: Handle the unsorted case. + IfFailGo( m_pMiniMd->GetGenericParamsForToken(tk, &ulStart, &ulEnd) ); + + for (; ulStart < ulEnd; ++ulStart) + { + index = m_pMiniMd->GetGenericParamRid(ulStart); + IfFailGo(m_pMiniMd->GetGenericParamRecord(index, &pGenericParamRec)); + + RID ridConstraint; + IfFailGo( m_pMiniMd->FindGenericParamConstraintHelper(TokenFromRid(ulStart, mdtGenericParam), &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &ridConstraint)) + { + // Get the constraint. + GenericParamConstraintRec *pRec; + IfFailGo(m_pMiniMd->GetGenericParamConstraintRecord(RidFromToken(ridConstraint), &pRec)); + constraint = m_pMiniMd->getConstraintOfGenericParamConstraint(pRec); + + // Mark it. + IfFailGo( Mark(constraint) ); + } + HENUMInternal::ClearEnum(&hEnum); + } + +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + + return hr; +} // HRESULT FilterManager::MarkGenericParamWithParentToken() + + +//***************************************************************************** +// cascading Mark of an TypeDef token +//***************************************************************************** +HRESULT FilterManager::MarkInterfaceImpls( + mdTypeDef td) +{ + HRESULT hr = NOERROR; + ULONG ridStart, ridEnd; + ULONG i; + InterfaceImplRec *pRec; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + if ( m_pMiniMd->IsSorted(TBL_InterfaceImpl) ) + { + IfFailGo(m_pMiniMd->getInterfaceImplsForTypeDef(RidFromToken(td), &ridEnd, &ridStart)); + } + else + { + ridStart = 1; + ridEnd = m_pMiniMd->getCountInterfaceImpls() + 1; + } + + // Search for the interfaceimpl with the parent of td + for (i = ridStart; i < ridEnd; i++) + { + IfFailGo(m_pMiniMd->GetInterfaceImplRecord(i, &pRec)); + if ( td != m_pMiniMd->getClassOfInterfaceImpl(pRec) ) + continue; + + // found an InterfaceImpl associate with td. Mark the interface row and the interfaceimpl type + IfFailGo( m_pMiniMd->GetFilterTable()->MarkInterfaceImpl(TokenFromRid(i, mdtInterfaceImpl)) ); + IfFailGo( MarkCustomAttributesWithParentToken(TokenFromRid(i, mdtInterfaceImpl)) ); + // IfFailGo( MarkDeclSecuritiesWithParentToken(TokenFromRid(i, mdtInterfaceImpl)) ); + IfFailGo( Mark(m_pMiniMd->getInterfaceOfInterfaceImpl(pRec)) ); + } +ErrExit: + return hr; +} // HRESULT FilterManager::MarkInterfaceImpls() + +//***************************************************************************** +// cascading Mark of an TypeDef token +//***************************************************************************** +HRESULT FilterManager::MarkTypeDef( + mdTypeDef td) +{ + HRESULT hr = NOERROR; + TypeDefRec *pRec; + IHostFilter *pFilter = m_pMiniMd->GetHostFilter(); + DWORD dwFlags; + RID iNester; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if TypeDef is already marked, just return + if (m_pMiniMd->GetFilterTable()->IsTypeDefMarked(td)) + goto ErrExit; + + // Mark the TypeDef first to avoid duplicate marking + IfFailGo( m_pMiniMd->GetFilterTable()->MarkTypeDef(td) ); + if (pFilter) + pFilter->MarkToken(td); + + // We don't need to mark InterfaceImpl but we need to mark the + // TypeDef/TypeRef associated with InterfaceImpl. + IfFailGo( MarkInterfaceImpls(td) ); + + // mark the base class + IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec)); + IfFailGo( Mark(m_pMiniMd->getExtendsOfTypeDef(pRec)) ); + + // mark all of the children of this TypeDef + IfFailGo( MarkMethodsWithParentToken(td) ); + IfFailGo( MarkMethodImplsWithParentToken(td) ); + IfFailGo( MarkFieldsWithParentToken(td) ); + IfFailGo( MarkEventsWithParentToken(td) ); + IfFailGo( MarkPropertiesWithParentToken(td) ); + + // mark any GenericParam of this TypeDef + IfFailGo( MarkGenericParamWithParentToken(td) ); + + // mark custom value and permission + IfFailGo( MarkCustomAttributesWithParentToken(td) ); + IfFailGo( MarkDeclSecuritiesWithParentToken(td) ); + + // If the class is a Nested class mark the parent, recursively. + dwFlags = m_pMiniMd->getFlagsOfTypeDef(pRec); + if (IsTdNested(dwFlags)) + { + NestedClassRec *pNestClassRec; + IfFailGo(m_pMiniMd->FindNestedClassHelper(td, &iNester)); + if (InvalidRid(iNester)) + IfFailGo(CLDB_E_RECORD_NOTFOUND); + IfFailGo(m_pMiniMd->GetNestedClassRecord(iNester, &pNestClassRec)); + IfFailGo(MarkTypeDef(m_pMiniMd->getEnclosingClassOfNestedClass(pNestClassRec))); + } + +ErrExit: + return hr; +} // HRESULT FilterManager::MarkTypeDef() + + +//***************************************************************************** +// walk signature and mark tokens embedded in the signature +//***************************************************************************** + +#define VALIDATE_SIGNATURE_LEN(x) \ + do{ cb = (x); \ + cbUsed += cb; pbSig += cb; \ + if (cbUsed > cbSig) IfFailGo(META_E_BAD_SIGNATURE); \ + }while (0) + +#define VALIDATE_SIGNATURE_LEN_HR(x) \ + do{ IfFailGo(x); \ + cbUsed += cb; pbSig += cb; \ + if (cbUsed > cbSig) IfFailGo(META_E_BAD_SIGNATURE); \ + }while (0) + +HRESULT FilterManager::MarkSignature( + PCCOR_SIGNATURE pbSig, // [IN] point to the current byte to visit in the signature + ULONG cbSig, // [IN] count of bytes available. + ULONG *pcbUsed) // [OUT] count of bytes consumed. +{ + HRESULT hr = NOERROR; // A result. + ULONG cArg = 0; // count of arguments in the signature + ULONG cTypes = 0; // Count of argument types in the signature. + ULONG cb; // Bytes used in a sig element. + ULONG cbUsed = 0; // Total bytes consumed. + ULONG callingconv = IMAGE_CEE_CS_CALLCONV_MAX; + + // calling convention + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &callingconv) ); + + if ((callingconv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX) + IfFailGo(META_E_BAD_SIGNATURE); + + // Field signature is a single element. + if (isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + // It is a FieldDef + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + } + else + { + // If Generic call, get count of type parameters. + //@TODO: where are the type params? + if (callingconv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &cTypes) ); + } + + // Count of arguments passed in call. + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &cArg) ); + + // Mark the return type, if there is one (LocalVarSig and GenericInst don't have return types). + if ( !( isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) || isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_GENERICINST)) ) + { // process the return type + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + } + + // Iterate over the arguments, and mark each one. + while (cArg--) + { + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + } + } + +ErrExit: + *pcbUsed = cbUsed; + return hr; +} // HRESULT FilterManager::MarkSignature() + + +//***************************************************************************** +// walk one type and mark tokens embedded in the signature +//***************************************************************************** +HRESULT FilterManager::MarkFieldSignature( + PCCOR_SIGNATURE pbSig, // [IN] point to the current byte to visit in the signature + ULONG cbSig, // [IN] count of bytes available. + ULONG *pcbUsed) // [OUT] count of bytes consumed. +{ + HRESULT hr = NOERROR; // A result. + ULONG cb; // Bytes in one signature element. + ULONG cbUsed = 0; // Total bytes consumed from signature. + CorElementType ulElementType; // ELEMENT_TYPE_xxx from signature. + ULONG ulData; // Some data (like a count) from the signature. + ULONG ulTemp; // Unused data. + mdToken token; // A token from the signature. + int iData; // Integer data from signature. + + VALIDATE_SIGNATURE_LEN( CorSigUncompressElementType(pbSig, &ulElementType) ); + + // Skip the modifiers... + while (CorIsModifierElementType((CorElementType) ulElementType)) + { + VALIDATE_SIGNATURE_LEN( CorSigUncompressElementType(pbSig, &ulElementType) ); + } + + // Examine the signature element + switch (ulElementType) + { + case ELEMENT_TYPE_SZARRAY: + // syntax: SZARRAY <BaseType> + + // conver the base type for the SZARRAY or GENERICARRAY + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + break; + + case ELEMENT_TYPE_CMOD_REQD: + case ELEMENT_TYPE_CMOD_OPT: + // syntax: {CMOD_REQD|CMOD_OPT} <token> <signature> + + // now get the embedded token + VALIDATE_SIGNATURE_LEN( CorSigUncompressToken(pbSig, &token) ); + + // Mark the token + IfFailGo( Mark(token) ); + + // mark the base type + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + break; + + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + // syntax: VAR <index> + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) ); + break; + + case ELEMENT_TYPE_ARRAY: + // syntax: ARRAY BaseType <rank> [i size_1... size_i] [j lowerbound_1 ... lowerbound_j] + + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + + // Parse for the rank + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) ); + + // if rank == 0, we are done + if (ulData == 0) + break; + + // Any size of dimension specified? + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) ); + + // Consume sizes of dimension. + while (ulData--) + { + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulTemp) ); + } + + // Any lower bounds specified? + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) ); + + // Consume lower bounds. + while (ulData--) + { + VALIDATE_SIGNATURE_LEN( CorSigUncompressSignedInt(pbSig, &iData) ); + } + + break; + + case ELEMENT_TYPE_FNPTR: + // function pointer is followed by another complete signature + VALIDATE_SIGNATURE_LEN_HR( MarkSignature(pbSig, cbSig - cbUsed, &cb) ); + break; + + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + // syntax: {CLASS | VALUECLASS} <token> + VALIDATE_SIGNATURE_LEN( CorSigUncompressToken(pbSig, &token) ); + + // Mark it. + IfFailGo( Mark(token) ); + break; + + case ELEMENT_TYPE_GENERICINST: + // syntax: ELEMENT_TYPE_GEENRICINST <ELEMENT_TYPE_CLASS | ELEMENT_TYPE_VALUECLASS> <token> <n> <n params> + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + + // Get the number of generic parameters + VALIDATE_SIGNATURE_LEN( CorSigUncompressData(pbSig, &ulData) ); + + // Get the generic parameters + while (ulData--) + { + VALIDATE_SIGNATURE_LEN_HR( MarkFieldSignature(pbSig, cbSig - cbUsed, &cb) ); + } + break; + + default: + // If valid element (I4, etc), great. Otherwise, return error. + if ((ulElementType >= ELEMENT_TYPE_MAX) || + (ulElementType == ELEMENT_TYPE_PTR) || + (ulElementType == ELEMENT_TYPE_BYREF) || + (ulElementType == ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED)) + { + IfFailGo(META_E_BAD_SIGNATURE); + } + break; + } + +ErrExit: + *pcbUsed = cbUsed; + return hr; +} // HRESULT FilterManager::MarkFieldSignature() + + + +//***************************************************************************** +// +// Unmark the TypeDef +// +//***************************************************************************** +HRESULT FilterManager::UnmarkTypeDef( + mdTypeDef td) +{ + HRESULT hr = NOERROR; + TypeDefRec *pTypeDefRec; + RID ridStart, ridEnd; + RID index; + CustomAttributeRec *pCARec; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME(m_pMiniMd->GetFilterTable() != NULL); + + // if TypeDef is already unmarked, just return + if (m_pMiniMd->GetFilterTable()->IsTypeDefMarked(td) == false) + goto ErrExit; + + // Mark the TypeDef first to avoid duplicate marking + IfFailGo( m_pMiniMd->GetFilterTable()->UnmarkTypeDef(td) ); + + // Don't need to unmark InterfaceImpl because the TypeDef is unmarked that will make + // the InterfaceImpl automatically unmarked. + + // unmark all of the children of this TypeDef + IfFailGo(m_pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + + // unmark the methods + ridStart = m_pMiniMd->getMethodListOfTypeDef(pTypeDefRec); + IfFailGo(m_pMiniMd->getEndMethodListOfTypeDef(RidFromToken(td), &ridEnd)); + for ( index = ridStart; index < ridEnd; index ++ ) + { + RID rid; + IfFailGo(m_pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(m_pMiniMd->GetFilterTable()->UnmarkMethod(TokenFromRid( + rid, + mdtMethodDef))); + } + + // unmark the fields + ridStart = m_pMiniMd->getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(m_pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd)); + for ( index = ridStart; index < ridEnd; index ++ ) + { + RID rid; + IfFailGo(m_pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(m_pMiniMd->GetFilterTable()->UnmarkField(TokenFromRid( + rid, + mdtFieldDef))); + } + + // unmark custom value + if ( m_pMiniMd->IsSorted( TBL_CustomAttribute ) ) + { + // table is sorted. ridStart to ridEnd - 1 are all CustomAttribute + // associated with tkParent + // + IfFailGo(m_pMiniMd->getCustomAttributeForToken(td, &ridEnd, &ridStart)); + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo( m_pMiniMd->GetFilterTable()->UnmarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) ); + } + } + else + { + // table scan is needed + ridStart = 1; + ridEnd = m_pMiniMd->getCountCustomAttributes() + 1; + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(m_pMiniMd->GetCustomAttributeRecord(index, &pCARec)); + if ( td == m_pMiniMd->getParentOfCustomAttribute(pCARec) ) + { + // This CustomAttribute is associated with tkParent + IfFailGo( m_pMiniMd->GetFilterTable()->UnmarkCustomAttribute( TokenFromRid(index, mdtCustomAttribute) ) ); + } + } + } + + // We don't support nested type!! + +ErrExit: + return hr; + +} // HRESULT FilterManager::UnmarkTypeDef() + + diff --git a/src/coreclr/md/compiler/filtermanager.h b/src/coreclr/md/compiler/filtermanager.h new file mode 100644 index 00000000000..24fcd0128c2 --- /dev/null +++ b/src/coreclr/md/compiler/filtermanager.h @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// FilterManager.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __FilterManager__h__ +#define __FilterManager__h__ + + + + +//********************************************************************* +// FilterManager Class +//********************************************************************* +class FilterManager +{ +public: + FilterManager(CMiniMdRW *pMiniMd) {m_pMiniMd = pMiniMd; hasModuleBeenMarked = false; hasAssemblyBeenMarked = false;} + ~FilterManager() {}; + + HRESULT Mark(mdToken tk); + + // Unmark helper + HRESULT UnmarkTypeDef(mdTypeDef td); + HRESULT MarkNewUserString(mdString str); + + +private: + HRESULT MarkCustomAttribute(mdCustomAttribute cv); + HRESULT MarkDeclSecurity(mdPermission pe); + HRESULT MarkStandAloneSig(mdSignature sig); + HRESULT MarkTypeSpec(mdTypeSpec ts); + HRESULT MarkTypeRef(mdTypeRef tr); + HRESULT MarkMemberRef(mdMemberRef mr); + HRESULT MarkModuleRef(mdModuleRef mr); + HRESULT MarkAssemblyRef(mdAssemblyRef ar); + HRESULT MarkModule(mdModule mo); + HRESULT MarkAssembly(mdAssembly as); + HRESULT MarkInterfaceImpls(mdTypeDef td); + HRESULT MarkUserString(mdString str); + + HRESULT MarkMethodSpec(mdMethodSpec ms); + + HRESULT MarkCustomAttributesWithParentToken(mdToken tkParent); + HRESULT MarkDeclSecuritiesWithParentToken(mdToken tkParent); + HRESULT MarkMemberRefsWithParentToken(mdToken tk); + + HRESULT MarkParam(mdParamDef pd); + HRESULT MarkMethod(mdMethodDef md); + HRESULT MarkField(mdFieldDef fd); + HRESULT MarkEvent(mdEvent ev); + HRESULT MarkProperty(mdProperty pr); + + HRESULT MarkParamsWithParentToken(mdMethodDef md); + HRESULT MarkMethodsWithParentToken(mdTypeDef td); + HRESULT MarkMethodImplsWithParentToken(mdTypeDef td); + HRESULT MarkFieldsWithParentToken(mdTypeDef td); + HRESULT MarkEventsWithParentToken(mdTypeDef td); + HRESULT MarkPropertiesWithParentToken(mdTypeDef td); + + HRESULT MarkGenericParamWithParentToken(mdToken tk); + + + HRESULT MarkTypeDef(mdTypeDef td); + + + // <TODO>We don't want to keep track the debug info with bits because these are going away...</TODO> + HRESULT MarkMethodDebugInfo(mdMethodDef md); + + // walk the signature and mark all of the embedded TypeDef or TypeRef + HRESULT MarkSignature(PCCOR_SIGNATURE pbSig, ULONG cbSig, ULONG *pcbUsed); + HRESULT MarkFieldSignature(PCCOR_SIGNATURE pbSig, ULONG cbSig, ULONG *pcbUsed); + + +private: + CMiniMdRW *m_pMiniMd; + bool hasModuleBeenMarked; + bool hasAssemblyBeenMarked; +}; + +#endif // __FilterManager__h__ diff --git a/src/coreclr/md/compiler/helper.cpp b/src/coreclr/md/compiler/helper.cpp new file mode 100644 index 00000000000..a4e7b10c8be --- /dev/null +++ b/src/coreclr/md/compiler/helper.cpp @@ -0,0 +1,500 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// Helper.cpp +// + +// +// Implementation of some internal APIs from code:IMetaDataHelper and code:IMetaDataEmitHelper. +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "importhelper.h" +#include "mdlog.h" + +#if defined(FEATURE_METADATA_EMIT) || defined(FEATURE_METADATA_INTERNAL_APIS) + +//***************************************************************************** +// translating signature from one scope to another scope +// +// Implements public API code:IMetaDataEmit::TranslateSigWithScope. +// Implements internal API code:IMetaDataHelper::TranslateSigWithScope. +//***************************************************************************** +STDMETHODIMP RegMeta::TranslateSigWithScope( // S_OK or error. + IMetaDataAssemblyImport *pAssemImport, // [IN] importing assembly interface + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + IMetaDataImport *pImport, // [IN] importing interface + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit *pAssemEmit,// [IN] emit assembly interface + IMetaDataEmit *pEmit, // [IN] emit interface + PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature + ULONG cbTranslatedSigMax, + ULONG *pcbTranslatedSig) // [OUT] count of bytes in the translated signature +{ +#ifdef FEATURE_METADATA_EMIT + HRESULT hr = S_OK; + + IMDCommon *pAssemImportMDCommon = NULL; + IMDCommon *pImportMDCommon = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + RegMeta *pRegMetaAssemEmit = static_cast<RegMeta*>(pAssemEmit); + RegMeta *pRegMetaEmit = NULL; + + CQuickBytes qkSigEmit; + ULONG cbEmit; + + pRegMetaEmit = static_cast<RegMeta*>(pEmit); + + { + // This function can cause new TypeRef being introduced. + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(pvTranslatedSig && pcbTranslatedSig); + + if (pAssemImport) + { + IfFailGo(pAssemImport->QueryInterface(IID_IMDCommon, (void**)&pAssemImportMDCommon)); + } + IMetaModelCommon *pAssemImportMetaModelCommon = pAssemImportMDCommon ? pAssemImportMDCommon->GetMetaModelCommon() : 0; + + IfFailGo(pImport->QueryInterface(IID_IMDCommon, (void**)&pImportMDCommon)); + IMetaModelCommon *pImportMetaModelCommon = pImportMDCommon->GetMetaModelCommon(); + + IfFailGo( ImportHelper::MergeUpdateTokenInSig( // S_OK or error. + pRegMetaAssemEmit ? &(pRegMetaAssemEmit->m_pStgdb->m_MiniMd) : 0, // The assembly emit scope. + &(pRegMetaEmit->m_pStgdb->m_MiniMd), // The emit scope. + pAssemImportMetaModelCommon, // Assembly where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes. + pImportMetaModelCommon, // The scope where signature is from. + pbSigBlob, // signature from the imported scope + NULL, // Internal OID mapping structure. + &qkSigEmit, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit + memcpy(pvTranslatedSig, qkSigEmit.Ptr(), cbEmit > cbTranslatedSigMax ? cbTranslatedSigMax :cbEmit ); + *pcbTranslatedSig = cbEmit; + if (cbEmit > cbTranslatedSigMax) + hr = CLDB_S_TRUNCATION; + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + if (pAssemImportMDCommon) + pAssemImportMDCommon->Release(); + if (pImportMDCommon) + pImportMDCommon->Release(); + + return hr; +#else //!FEATURE_METADATA_EMIT + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT +} // RegMeta::TranslateSigWithScope + +#endif //FEATURE_METADATA_EMIT || FEATURE_METADATA_INTERNAL_APIS + +#if defined(FEATURE_METADATA_EMIT) && defined(FEATURE_METADATA_INTERNAL_APIS) + +//***************************************************************************** +// Helper : Set ResolutionScope of a TypeRef +// +// Implements internal API code:IMetaDataEmitHelper::SetResolutionScopeHelper. +//***************************************************************************** +HRESULT RegMeta::SetResolutionScopeHelper( // Return hresult. + mdTypeRef tr, // [IN] TypeRef record to update + mdToken rs) // [IN] new ResolutionScope +{ + HRESULT hr = NOERROR; + TypeRefRec * pTypeRef; + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.GetTypeRefRecord(RidFromToken(tr), &pTypeRef)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_TypeRef, TypeRefRec::COL_ResolutionScope, pTypeRef, rs)); + +ErrExit: + return hr; +} // RegMeta::SetResolutionScopeHelper + + +//***************************************************************************** +// Helper : Set offset of a ManifestResource +// +// Implements internal API code:IMetaDataEmitHelper::SetManifestResourceOffsetHelper. +//***************************************************************************** +HRESULT +RegMeta::SetManifestResourceOffsetHelper( + mdManifestResource mr, // [IN] The manifest token + ULONG ulOffset) // [IN] new offset +{ + HRESULT hr = NOERROR; + ManifestResourceRec * pRec; + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(mr), &pRec)); + pRec->SetOffset(ulOffset); + +ErrExit: + return hr; +} // RegMeta::SetManifestResourceOffsetHelper + +//******************************************************************************* +// +// Following APIs are used by reflection emit. +// +//******************************************************************************* + +//******************************************************************************* +// helper to define method semantics +// +// Implements internal API code:IMetaDataEmitHelper::DefineMethodSemanticsHelper. +//******************************************************************************* +HRESULT RegMeta::DefineMethodSemanticsHelper( + mdToken tkAssociation, // [IN] property or event token + DWORD dwFlags, // [IN] semantics + mdMethodDef md) // [IN] method to associated with +{ + HRESULT hr; + LOCKWRITE(); + hr = _DefineMethodSemantics((USHORT) dwFlags, md, tkAssociation, false); + +ErrExit: + return hr; +} // RegMeta::DefineMethodSemantics + +//******************************************************************************* +// helper to set field layout +// +// Implements internal API code:IMetaDataEmitHelper::SetFieldLayoutHelper. +//******************************************************************************* +HRESULT RegMeta::SetFieldLayoutHelper( // Return hresult. + mdFieldDef fd, // [IN] field to associate the layout info + ULONG ulOffset) // [IN] the offset for the field +{ + HRESULT hr; + FieldLayoutRec *pFieldLayoutRec; + RID iFieldLayoutRec; + + LOCKWRITE(); + + if (ulOffset == UINT32_MAX) + { + // invalid argument + IfFailGo( E_INVALIDARG ); + } + + // create a field layout record + IfFailGo(m_pStgdb->m_MiniMd.AddFieldLayoutRecord(&pFieldLayoutRec, &iFieldLayoutRec)); + + // Set the Field entry. + IfFailGo(m_pStgdb->m_MiniMd.PutToken( + TBL_FieldLayout, + FieldLayoutRec::COL_Field, + pFieldLayoutRec, + fd)); + pFieldLayoutRec->SetOffSet(ulOffset); + IfFailGo( m_pStgdb->m_MiniMd.AddFieldLayoutToHash(iFieldLayoutRec) ); + +ErrExit: + + return hr; +} // RegMeta::SetFieldLayout + +//******************************************************************************* +// helper to define event +// +// Implements internal API code:IMetaDataEmitHelper::DefineEventHelper. +//******************************************************************************* +STDMETHODIMP RegMeta::DefineEventHelper( // Return hresult. + mdTypeDef td, // [IN] the class/interface on which the event is being defined + LPCWSTR szEvent, // [IN] Name of the event + DWORD dwEventFlags, // [IN] CorEventAttr + mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class + mdEvent *pmdEvent) // [OUT] output event token +{ + HRESULT hr = S_OK; + LOG((LOGMD, "MD RegMeta::DefineEventHelper(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + td, szEvent, dwEventFlags, tkEventType, pmdEvent)); + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _DefineEvent(td, szEvent, dwEventFlags, tkEventType, pmdEvent); + +ErrExit: + return hr; +} // RegMeta::DefineEvent + + +//******************************************************************************* +// helper to add a declarative security blob to a class or method +// +// Implements internal API code:IMetaDataEmitHelper::AddDeclarativeSecurityHelper. +//******************************************************************************* +STDMETHODIMP RegMeta::AddDeclarativeSecurityHelper( + mdToken tk, // [IN] Parent token (typedef/methoddef) + DWORD dwAction, // [IN] Security action (CorDeclSecurity) + void const *pValue, // [IN] Permission set blob + DWORD cbValue, // [IN] Byte count of permission set blob + mdPermission*pmdPermission) // [OUT] Output permission token +{ + HRESULT hr = S_OK; + DeclSecurityRec *pDeclSec = NULL; + RID iDeclSec; + short sAction = static_cast<short>(dwAction); + mdPermission tkPerm; + + LOG((LOGMD, "MD RegMeta::AddDeclarativeSecurityHelper(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + tk, dwAction, pValue, cbValue, pmdPermission)); + + LOCKWRITE(); + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtMethodDef || TypeFromToken(tk) == mdtAssembly); + + // Check for valid Action. + if (sAction == 0 || sAction > dclMaximumValue) + IfFailGo(E_INVALIDARG); + + if (CheckDups(MDDupPermission)) + { + hr = ImportHelper::FindPermission(&(m_pStgdb->m_MiniMd), tk, sAction, &tkPerm); + + if (SUCCEEDED(hr)) + { + // Set output parameter. + if (pmdPermission) + *pmdPermission = tkPerm; + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkPerm), &pDeclSec)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + // Create a new record. + if (!pDeclSec) + { + IfFailGo(m_pStgdb->m_MiniMd.AddDeclSecurityRecord(&pDeclSec, &iDeclSec)); + tkPerm = TokenFromRid(iDeclSec, mdtPermission); + + // Set output parameter. + if (pmdPermission) + *pmdPermission = tkPerm; + + // Save parent and action information. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_DeclSecurity, DeclSecurityRec::COL_Parent, pDeclSec, tk)); + pDeclSec->SetAction(sAction); + + // Turn on the internal security flag on the parent. + if (TypeFromToken(tk) == mdtTypeDef) + IfFailGo(_TurnInternalFlagsOn(tk, tdHasSecurity)); + else if (TypeFromToken(tk) == mdtMethodDef) + IfFailGo(_TurnInternalFlagsOn(tk, mdHasSecurity)); + IfFailGo(UpdateENCLog(tk)); + } + + // Write the blob into the record. + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_DeclSecurity, DeclSecurityRec::COL_PermissionSet, + pDeclSec, pValue, cbValue)); + + IfFailGo(UpdateENCLog(tkPerm)); + +ErrExit: + + return hr; +} // RegMeta::AddDeclarativeSecurityHelper + + +//******************************************************************************* +// helper to set type's extends column +// +// Implements internal API code:IMetaDataEmitHelper::SetTypeParent. +//******************************************************************************* +HRESULT RegMeta::SetTypeParent( // Return hresult. + mdTypeDef td, // [IN] Type definition + mdToken tkExtends) // [IN] parent type +{ + HRESULT hr; + TypeDefRec *pRec; + + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRec)); + IfFailGo( m_pStgdb->m_MiniMd.PutToken(TBL_TypeDef, TypeDefRec::COL_Extends, pRec, tkExtends) ); + +ErrExit: + return hr; +} // RegMeta::SetTypeParent + + +//******************************************************************************* +// helper to set type's extends column +// +// Implements internal API code:IMetaDataEmitHelper::AddInterfaceImpl. +//******************************************************************************* +HRESULT RegMeta::AddInterfaceImpl( // Return hresult. + mdTypeDef td, // [IN] Type definition + mdToken tkInterface) // [IN] interface type +{ + HRESULT hr; + InterfaceImplRec *pRec; + RID ii; + + LOCKWRITE(); + hr = ImportHelper::FindInterfaceImpl(&(m_pStgdb->m_MiniMd), td, tkInterface, (mdInterfaceImpl *)&ii); + if (hr == S_OK) + goto ErrExit; + IfFailGo(m_pStgdb->m_MiniMd.AddInterfaceImplRecord(&pRec, &ii)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Class, pRec, td)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Interface, pRec, tkInterface)); + +ErrExit: + return hr; +} // RegMeta::AddInterfaceImpl + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +//******************************************************************************* +// Helper to determine path separator and number of separated parts. +// +// Implements internal API code:IMDInternalEmit::GetPathSeparator. +//******************************************************************************* +HRESULT +RegMeta::GetPathSeparator( + char *path, + char *separator, + ULONG *partsCount) +{ + const char delimiters[] = { '\\', '/', '\0'}; + ULONG tokens = 1; + + // try first + char delim = delimiters[0]; + char* charPtr = strchr(path, delim); + + if (charPtr != NULL) + { + // count tokens + while (charPtr != NULL) + { + tokens++; + charPtr = strchr(charPtr + 1, delim); + } + } + else + { + // try second + delim = delimiters[1]; + charPtr = strchr(path, delim); + if (charPtr != NULL) + { + // count tokens + while (charPtr != NULL) + { + tokens++; + charPtr = strchr(charPtr + 1, delim); + } + } + else + { + // delimiter not found - set to \0; + delim = delimiters[2]; + } + } + + *separator = delim; + *partsCount = tokens; + + return S_OK; +} // RegMeta::GetPathSeparator +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + +#endif //FEATURE_METADATA_EMIT && FEATURE_METADATA_INTERNAL_APIS + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +//***************************************************************************** +// Helper : get metadata information +// +// Implements internal API code:IMetaDataHelper::GetMetadata. +//***************************************************************************** +STDMETHODIMP +RegMeta::GetMetadata( + ULONG ulSelect, // [IN] Selector. + void ** ppData) // [OUT] Put pointer to data here. +{ + + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + switch (ulSelect) + { + case 0: + *ppData = &m_pStgdb->m_MiniMd; + break; + case 1: + *ppData = (void*)g_CodedTokens; + break; + case 2: + *ppData = (void*)g_Tables; + break; + default: + *ppData = 0; + break; + } + + return S_OK; +} // RegMeta::GetMetadata + +//******************************************************************************* +// helper to change MVID +// +// Implements internal API code:IMDInternalEmit::ChangeMvid. +//******************************************************************************* +HRESULT RegMeta::ChangeMvid( // S_OK or error. + REFGUID newMvid) // GUID to use as the MVID +{ + return GetMiniMd()->ChangeMvid(newMvid); +} + +//******************************************************************************* +// Helper to change MDUpdateMode value to updateMode. +// +// Implements internal API code:IMDInternalEmit::SetMDUpdateMode. +//******************************************************************************* +HRESULT +RegMeta::SetMDUpdateMode( + ULONG updateMode, + ULONG * pPreviousUpdateMode) +{ + HRESULT hr; + + OptionValue optionValue; + IfFailGo(m_pStgdb->m_MiniMd.GetOption(&optionValue)); + if (pPreviousUpdateMode != NULL) + { + *pPreviousUpdateMode = optionValue.m_UpdateMode; + } + optionValue.m_UpdateMode = updateMode; + IfFailGo(m_pStgdb->m_MiniMd.SetOption(&optionValue)); + +ErrExit: + return hr; +} // RegMeta::SetMDUpdateMode + +#endif //FEATURE_METADATA_INTERNAL_APIS diff --git a/src/coreclr/md/compiler/import.cpp b/src/coreclr/md/compiler/import.cpp new file mode 100644 index 00000000000..9f3beded986 --- /dev/null +++ b/src/coreclr/md/compiler/import.cpp @@ -0,0 +1,3808 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// Import.cpp +// + +// +// Methods of code:RegMeta class which implement public API interfaces: +// * code:IMetaDataImport, and +// * code:IMetaDataImport2. +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "corpriv.h" +#include "importhelper.h" +#include "mdlog.h" +#include "mdperf.h" +#include "stgio.h" + +//***************************************************************************** +// Enumerate over all the Methods in a TypeDef. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumMembers( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + mdToken rMembers[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStartMethod; + ULONG ridEndMethod; + ULONG ridStartField; + ULONG ridEndField; + ULONG index; + ULONG indexField; + TypeDefRec *pRec; + HENUMInternal *pEnum = *ppmdEnum; + + LOG((LOGMD, "MD RegMeta::EnumMembers(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, cl, rMembers, cMax, pcTokens)); + + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + if ( IsGlobalMethodParentTk(cl) ) + { + cl = m_tdModule; + } + + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(cl), &pRec)); + + ridStartMethod = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(cl), &ridEndMethod)); + + ridStartField = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(cl), &ridEndField)); + + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) ); + + // add all methods to the dynamic array + for (index = ridStartMethod; index < ridEndMethod; index++ ) + { + RID rid; + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtMethodDef))); + } + + // add all fields to the dynamic array + for (indexField = ridStartField; indexField < ridEndField; indexField++ ) + { + RID rid; + IfFailGo(pMiniMd->GetFieldRid(indexField, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtFieldDef))); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMembers, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumMembers); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMembers() + +//***************************************************************************** +// Enumerate over all the Methods in a TypeDef that has szName +//***************************************************************************** +STDMETHODIMP RegMeta::EnumMembersWithName( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + LPCWSTR szName, // [IN] Limit results to those with this name. + mdToken rMembers[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + ULONG index; + TypeDefRec *pRec; + MethodRec *pMethod; + FieldRec *pField; + HENUMInternal *pEnum = *ppmdEnum; + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + LPCUTF8 szNameUtf8Tmp; + + LOG((LOGMD, "MD RegMeta::EnumMembersWithName(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, cl, MDSTR(szName), rMembers, cMax, pcTokens)); + + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) ); + + if ( IsGlobalMethodParentTk(cl) ) + { + cl = m_tdModule; + } + + // get the range of method rids given a typedef + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(cl), &pRec)); + ridStart = pMiniMd->getMethodListOfTypeDef(pRec); + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(cl), &ridEnd)); + + for (index = ridStart; index < ridEnd; index++ ) + { + if (szNameUtf8 == NULL) + { + RID rid; + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtMethodDef))); + } + else + { + RID rid; + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(pMiniMd->GetMethodRecord(rid, &pMethod)); + IfFailGo(pMiniMd->getNameOfMethod(pMethod, &szNameUtf8Tmp)); + if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 ) + { + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(rid, mdtMethodDef))); + } + } + } + + ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(cl), &ridEnd)); + + for (index = ridStart; index < ridEnd; index++ ) + { + if (szNameUtf8 == NULL) + { + RID rid; + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(rid, mdtFieldDef))); + } + else + { + RID rid; + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(pMiniMd->GetFieldRecord(rid, &pField)); + IfFailGo(pMiniMd->getNameOfField(pField, &szNameUtf8Tmp)); + if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 ) + { + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtFieldDef))); + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMembers, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumMembersWithName); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMembersWithName() + +//***************************************************************************** +// enumerating through methods given a Typedef and the flag +//***************************************************************************** +STDMETHODIMP RegMeta::EnumMethods( + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdMethodDef rMethods[], // [OUT] Put MethodDefs here. + ULONG cMax, // [IN] Max MethodDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + TypeDefRec *pRec; + HENUMInternal *pEnum = *ppmdEnum; + + LOG((LOGMD, "MD RegMeta::EnumMethods(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, td, rMethods, cMax, pcTokens)); + + + + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // Check for mdTypeDefNil (representing <Module>). + // If so, this will map it to its token. + // + if ( IsGlobalMethodParentTk(td) ) + { + td = m_tdModule; + } + + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRec)); + ridStart = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(td), &ridEnd)); + + if (pMiniMd->HasIndirectTable(TBL_Method) || pMiniMd->HasDelete()) + { + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) ); + + // add all methods to the dynamic array + for (ULONG index = ridStart; index < ridEnd; index++ ) + { + if (pMiniMd->HasDelete() && + ((m_OptionValue.m_ImportOption & MDImportOptionAllMethodDefs) == 0)) + { + MethodRec *pMethRec; + RID rid; + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(pMiniMd->GetMethodRecord(rid, &pMethRec)); + LPCSTR szMethodName; + IfFailGo(pMiniMd->getNameOfMethod(pMethRec, &szMethodName)); + if (IsMdRTSpecialName(pMethRec->GetFlags()) && IsDeletedName(szMethodName) ) + { + continue; + } + } + RID rid; + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtMethodDef))); + } + } + else + { + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtMethodDef, ridStart, ridEnd, &pEnum) ); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumMethods); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMethods() + + + + +//***************************************************************************** +// Enumerate over all the methods with szName in a TypeDef. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumMethodsWithName( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + LPCWSTR szName, // [IN] Limit results to those with this name. + mdMethodDef rMethods[], // [OU] Put MethodDefs here. + ULONG cMax, // [IN] Max MethodDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + ULONG index; + TypeDefRec *pRec; + MethodRec *pMethod; + HENUMInternal *pEnum = *ppmdEnum; + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + LPCUTF8 szNameUtf8Tmp; + + LOG((LOGMD, "MD RegMeta::EnumMethodsWithName(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, cl, MDSTR(szName), rMethods, cMax, pcTokens)); + + + + START_MD_PERF(); + LOCKREAD(); + + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // Check for mdTypeDefNil (representing <Module>). + // If so, this will map it to its token. + // + if ( IsGlobalMethodParentTk(cl) ) + { + cl = m_tdModule; + } + + + // create the enumerator + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) ); + + // get the range of method rids given a typedef + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(cl), &pRec)); + ridStart = pMiniMd->getMethodListOfTypeDef(pRec); + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(cl), &ridEnd)); + + for (index = ridStart; index < ridEnd; index++ ) + { + if ( szNameUtf8 == NULL ) + { + RID rid; + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtMethodDef))); + } + else + { + RID rid; + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(pMiniMd->GetMethodRecord(rid, &pMethod)); + IfFailGo(pMiniMd->getNameOfMethod(pMethod, &szNameUtf8Tmp)); + if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 ) + { + IfFailGo(pMiniMd->GetMethodRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtMethodDef))); + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumMethodsWithName); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMethodsWithName() + + + +//***************************************************************************** +// Enumerate over all the fields in a TypeDef and a flag. +//***************************************************************************** +STDMETHODIMP +RegMeta::EnumFields( + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdFieldDef rFields[], // [OUT] Put FieldDefs here. + ULONG cMax, // [IN] Max FieldDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **>(phEnum); + ULONG ridStart; + ULONG ridEnd; + TypeDefRec *pRec; + HENUMInternal *pEnum = *ppmdEnum; + + LOG((LOGMD, "MD RegMeta::EnumFields(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, td, rFields, cMax, pcTokens)); + + START_MD_PERF(); + LOCKREAD(); + + if (pEnum == NULL) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // Check for mdTypeDefNil (representing <Module>). + // If so, this will map it to its token. + // + if (IsGlobalMethodParentTk(td)) + { + td = m_tdModule; + } + + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRec)); + ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd)); + + if (pMiniMd->HasIndirectTable(TBL_Field) || pMiniMd->HasDelete()) + { + IfFailGo(HENUMInternal::CreateDynamicArrayEnum(mdtFieldDef, &pEnum)); + + // add all methods to the dynamic array + for (ULONG index = ridStart; index < ridEnd; index++) + { + if (pMiniMd->HasDelete() && + ((m_OptionValue.m_ImportOption & MDImportOptionAllFieldDefs) == 0)) + { + FieldRec *pFieldRec; + RID rid; + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(pMiniMd->GetFieldRecord(rid, &pFieldRec)); + LPCUTF8 szFieldName; + IfFailGo(pMiniMd->getNameOfField(pFieldRec, &szFieldName)); + if (IsFdRTSpecialName(pFieldRec->GetFlags()) && IsDeletedName(szFieldName)) + { + continue; + } + } + RID rid; + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtFieldDef))); + } + } + else + { + IfFailGo(HENUMInternal::CreateSimpleEnum(mdtFieldDef, ridStart, ridEnd, &pEnum)); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rFields, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumFields); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumFields + + + +//***************************************************************************** +// Enumerate over all the fields with szName in a TypeDef. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumFieldsWithName( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + LPCWSTR szName, // [IN] Limit results to those with this name. + mdFieldDef rFields[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + ULONG index; + TypeDefRec *pRec; + FieldRec *pField; + HENUMInternal *pEnum = *ppmdEnum; + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + LPCUTF8 szNameUtf8Tmp; + + LOG((LOGMD, "MD RegMeta::EnumFields(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, cl, MDSTR(szName), rFields, cMax, pcTokens)); + + + + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // Check for mdTypeDefNil (representing <Module>). + // If so, this will map it to its token. + // + if ( IsGlobalMethodParentTk(cl) ) + { + cl = m_tdModule; + } + + // create the enumerator + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMethodDef, &pEnum) ); + + // get the range of field rids given a typedef + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(cl), &pRec)); + ridStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(cl), &ridEnd)); + + for (index = ridStart; index < ridEnd; index++ ) + { + if ( szNameUtf8 == NULL ) + { + RID rid; + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtFieldDef))); + } + else + { + RID rid; + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo(pMiniMd->GetFieldRecord(rid, &pField)); + IfFailGo(pMiniMd->getNameOfField(pField, &szNameUtf8Tmp)); + if ( strcmp(szNameUtf8Tmp, szNameUtf8) == 0 ) + { + IfFailGo(pMiniMd->GetFieldRid(index, &rid)); + IfFailGo( HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtFieldDef) ) ); + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rFields, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumFieldsWithName); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumFieldsWithName() + + +//***************************************************************************** +// Enumerate over the ParamDefs in a Method. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumParams( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdMethodDef mb, // [IN] MethodDef to scope the enumeration. + mdParamDef rParams[], // [OUT] Put ParamDefs here. + ULONG cMax, // [IN] Max ParamDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + MethodRec *pRec; + HENUMInternal *pEnum = *ppmdEnum; + + LOG((LOGMD, "MD RegMeta::EnumParams(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, mb, rParams, cMax, pcTokens)); + START_MD_PERF(); + LOCKREAD(); + + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(mb), &pRec)); + ridStart = m_pStgdb->m_MiniMd.getParamListOfMethod(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(mb), &ridEnd)); + + if (pMiniMd->HasIndirectTable(TBL_Param)) + { + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtParamDef, &pEnum) ); + + // add all methods to the dynamic array + for (ULONG index = ridStart; index < ridEnd; index++ ) + { + RID rid; + IfFailGo(pMiniMd->GetParamRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtParamDef))); + } + } + else + { + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtParamDef, ridStart, ridEnd, &pEnum) ); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rParams, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumParams); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumParams() + + + +//***************************************************************************** +// Enumerate the MemberRefs given the parent token. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumMemberRefs( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken tkParent, // [IN] Parent token to scope the enumeration. + mdMemberRef rMemberRefs[], // [OUT] Put MemberRefs here. + ULONG cMax, // [IN] Max MemberRefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridEnd; + ULONG index; + MemberRefRec *pRec; + HENUMInternal *pEnum = *ppmdEnum; + + LOG((LOGMD, "MD RegMeta::EnumMemberRefs(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, tkParent, rMemberRefs, cMax, pcTokens)); + + + + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + mdToken tk; + + // Check for mdTypeDefNil (representing <Module>). + // If so, this will map it to its token. + // + IsGlobalMethodParent(&tkParent); + + // create the enumerator + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtMemberRef, &pEnum) ); + + // get the range of field rids given a typedef + ridEnd = pMiniMd->getCountMemberRefs(); + + for (index = 1; index <= ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetMemberRefRecord(index, &pRec)); + tk = pMiniMd->getClassOfMemberRef(pRec); + if ( tk == tkParent ) + { + // add the matched ones to the enumerator + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtMemberRef) ) ); + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMemberRefs, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumMemberRefs); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMemberRefs() + + +//***************************************************************************** +// Enumerate methodimpls given a typedef +//***************************************************************************** +STDMETHODIMP RegMeta::EnumMethodImpls( // S_OK, S_FALSE, or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdToken rMethodBody[], // [OUT] Put Method Body tokens here. + mdToken rMethodDecl[], // [OUT] Put Method Declaration tokens here. + ULONG cMax, // [IN] Max tokens to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + MethodImplRec *pRec; + HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal hEnum; + + + LOG((LOGMD, "MD RegMeta::EnumMethodImpls(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, td, rMethodBody, rMethodDecl, cMax, pcTokens)); + + + + START_MD_PERF(); + LOCKREAD(); + + HENUMInternal::ZeroEnum(&hEnum); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + mdToken tkMethodBody; + mdToken tkMethodDecl; + RID ridCur; + + // Get the range of rids. + IfFailGo( pMiniMd->FindMethodImplHelper(td, &hEnum) ); + + // Create the enumerator, DynamicArrayEnum does not use the token type. + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( (TBL_MethodImpl << 24), &pEnum) ); + + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur)) + { + // Get the MethodBody and MethodDeclaration tokens for the current + // MethodImpl record. + IfFailGo(pMiniMd->GetMethodImplRecord(ridCur, &pRec)); + tkMethodBody = pMiniMd->getMethodBodyOfMethodImpl(pRec); + tkMethodDecl = pMiniMd->getMethodDeclarationOfMethodImpl(pRec); + + // Add the Method body/declaration pairs to the Enum + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, tkMethodBody ) ); + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, tkMethodDecl ) ); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethodBody, rMethodDecl, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::ClearEnum(&hEnum); + + STOP_MD_PERF(EnumMethodImpls); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMethodImpls() + + +//***************************************************************************** +// Enumerate over PermissionSets. Optionally limit to an object and/or an +// action. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumPermissionSets( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken tk, // [IN] if !NIL, token to scope the enumeration. + DWORD dwActions, // [IN] if !0, return only these actions. + mdPermission rPermission[], // [OUT] Put Permissions here. + ULONG cMax, // [IN] Max Permissions to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + ULONG index; + DeclSecurityRec *pRec; + HENUMInternal *pEnum = *ppmdEnum; + bool fCompareParent = false; + mdToken typ = TypeFromToken(tk); + mdToken tkParent; + + LOG((LOGMD, "MD RegMeta::EnumPermissionSets(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, tk, dwActions, rPermission, cMax, pcTokens)); + + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // Does this token type even have security? + if (tk != 0 && + !(typ == mdtTypeDef || typ == mdtMethodDef || typ == mdtAssembly)) + { + if (pcTokens) + *pcTokens = 0; + hr = S_FALSE; + goto ErrExit; + } + + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + if (!IsNilToken(tk)) + { + // parent is provided for lookup + if ( pMiniMd->IsSorted( TBL_DeclSecurity ) ) + { + IfFailGo(pMiniMd->getDeclSecurityForToken(tk, &ridEnd, &ridStart)); + } + else + { + // table is not sorted. So we have to do a table scan + ridStart = 1; + ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + fCompareParent = true; + } + } + else + { + ridStart = 1; + ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + } + + if (IsDclActionNil(dwActions) && !fCompareParent && !m_pStgdb->m_MiniMd.HasDelete()) + { + // create simple enumerator + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtPermission, ridStart, ridEnd, &pEnum) ); + } + else + { + // create the dynamic enumerator + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtPermission, &pEnum) ); + + for (index = ridStart; index < ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pRec)); + tkParent = pMiniMd->getParentOfDeclSecurity(pRec); + if ( (fCompareParent && tk != tkParent) || + IsNilToken(tkParent) ) + { + // We need to compare parent token and they are not equal so skip + // over this row. + // + continue; + } + if ( IsDclActionNil(dwActions) || + ( (DWORD)(pMiniMd->getActionOfDeclSecurity(pRec))) == dwActions ) + { + // If we don't need to compare the action, just add to the enum. + // Or we need to compare the action and the action values are equal, add to enum as well. + // + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtPermission) ) ); + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rPermission, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumPermissionSets); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumPermissionSets() + + +//***************************************************************************** +// Find a given member in a TypeDef (typically a class). +//***************************************************************************** +STDMETHODIMP RegMeta::FindMember( + mdTypeDef td, // [IN] given typedef + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdToken *pmb) // [OUT] matching memberdef +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD RegMeta::FindMember(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + td, MDSTR(szName), pvSigBlob, cbSigBlob, pmb)); + + START_MD_PERF(); + + // Don't lock this function. All of the functions that it calls are public APIs. keep it that way. + + // try to match with method first of all + hr = FindMethod( + td, + szName, + pvSigBlob, + cbSigBlob, + pmb); + + if ( hr == CLDB_E_RECORD_NOTFOUND ) + { + // now try field table + IfFailGo( FindField( + td, + szName, + pvSigBlob, + cbSigBlob, + pmb) ); + } +ErrExit: + STOP_MD_PERF(FindMember); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::FindMember() + + + +//***************************************************************************** +// Find a given member in a TypeDef (typically a class). +//***************************************************************************** +STDMETHODIMP RegMeta::FindMethod( + mdTypeDef td, // [IN] given typedef + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodDef *pmb) // [OUT] matching memberdef +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + + LOG((LOGMD, "MD RegMeta::FindMethod(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + td, MDSTR(szName), pvSigBlob, cbSigBlob, pmb)); + + START_MD_PERF(); + LOCKREAD(); + + if (szName == NULL) + IfFailGo(E_INVALIDARG); + PREFIX_ASSUME(szName != NULL); + + // If this is a global method, then use the <Module> typedef as parent. + IsGlobalMethodParent(&td); + + IfFailGo(ImportHelper::FindMethod(pMiniMd, + td, + szNameUtf8, + pvSigBlob, + cbSigBlob, + pmb)); + +ErrExit: + STOP_MD_PERF(FindMethod); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::FindMethod + + +//***************************************************************************** +// Find a given member in a TypeDef (typically a class). +//***************************************************************************** +STDMETHODIMP +RegMeta::FindField( + mdTypeDef td, // [IN] given typedef + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdFieldDef *pmb) // [OUT] matching memberdef +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "MD RegMeta::FindField(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + td, MDSTR(szName), pvSigBlob, cbSigBlob, pmb)); + + START_MD_PERF(); + LOCKREAD(); + + if (szName == NULL) + IfFailGo(E_INVALIDARG); + + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + + // If this is a global method, then use the <Module> typedef as parent. + IsGlobalMethodParent(&td); + + IfFailGo(ImportHelper::FindField(pMiniMd, + td, + szNameUtf8, + pvSigBlob, + cbSigBlob, + pmb)); + +ErrExit: + STOP_MD_PERF(FindField); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::FindField + + +//***************************************************************************** +// Find a given MemberRef in a TypeRef (typically a class). If no TypeRef +// is specified, the query will be for a random member in the scope. +//***************************************************************************** +STDMETHODIMP RegMeta::FindMemberRef( + mdToken tkPar, // [IN] given parent token. + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMemberRef *pmr) // [OUT] matching memberref +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + LPUTF8 szNameUtf8; + UTF8STR(szName, szNameUtf8); + + LOG((LOGMD, "MD RegMeta::FindMemberRef(0x%08x, %S, 0x%08x, 0x%08x, 0x%08x)\n", + tkPar, MDSTR(szName), pvSigBlob, cbSigBlob, pmr)); + + + + START_MD_PERF(); + + // <TODO>@todo: Can this causing building hash table? If so, should this consider the write lock?</TODO> + LOCKREAD(); + + // get the range of field rids given a typedef + _ASSERTE(TypeFromToken(tkPar) == mdtTypeRef || TypeFromToken(tkPar) == mdtMethodDef || + TypeFromToken(tkPar) == mdtModuleRef || TypeFromToken(tkPar) == mdtTypeDef || + TypeFromToken(tkPar) == mdtTypeSpec); + + // Set parent to global class m_tdModule if mdTokenNil is passed. + if (IsNilToken(tkPar)) + tkPar = m_tdModule; + + IfFailGo( ImportHelper::FindMemberRef(pMiniMd, tkPar, szNameUtf8, pvSigBlob, cbSigBlob, pmr) ); + +ErrExit: + + STOP_MD_PERF(FindMemberRef); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::FindMemberRef() + + +//***************************************************************************** +// Return the property of a MethodDef +//***************************************************************************** +STDMETHODIMP RegMeta::GetMethodProps( + mdMethodDef mb, // The method for which to get props. + mdTypeDef *pClass, // Put method's class here. + __out_ecount_opt (cchMethod) LPWSTR szMethod, // Put method's name here. + ULONG cchMethod, // Size of szMethod buffer in wide chars. + ULONG *pchMethod, // Put actual size here + DWORD *pdwAttr, // Put flags here. + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob, // [OUT] actual size of signature blob + ULONG *pulCodeRVA, // [OUT] codeRVA + DWORD *pdwImplFlags) // [OUT] Impl. Flags +{ + HRESULT hr = NOERROR; + BEGIN_ENTRYPOINT_NOTHROW; + + MethodRec *pMethodRec; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "MD RegMeta::GetMethodProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mb, pClass, szMethod, cchMethod, pchMethod, pdwAttr, ppvSigBlob, pcbSigBlob, + pulCodeRVA, pdwImplFlags)); + + + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mb) == mdtMethodDef); + + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(mb), &pMethodRec)); + + if (pClass) + { + // caller wants parent typedef + IfFailGo( pMiniMd->FindParentOfMethodHelper(mb, pClass) ); + + if ( IsGlobalMethodParentToken(*pClass) ) + { + // If the parent of Method is the <Module>, return mdTypeDefNil instead. + *pClass = mdTypeDefNil; + } + + } + if (ppvSigBlob || pcbSigBlob) + { + // caller wants signature information + PCCOR_SIGNATURE pvSigTmp; + ULONG cbSig; + IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRec, &pvSigTmp, &cbSig)); + if ( ppvSigBlob ) + *ppvSigBlob = pvSigTmp; + if ( pcbSigBlob) + *pcbSigBlob = cbSig; + } + if ( pdwAttr ) + { + *pdwAttr = pMiniMd->getFlagsOfMethod(pMethodRec); + } + if ( pulCodeRVA ) + { + *pulCodeRVA = pMiniMd->getRVAOfMethod(pMethodRec); + } + if ( pdwImplFlags ) + { + *pdwImplFlags = (DWORD )pMiniMd->getImplFlagsOfMethod(pMethodRec); + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szMethod || pchMethod) + { + IfFailGo( pMiniMd->getNameOfMethod(pMethodRec, szMethod, cchMethod, pchMethod) ); + } + +ErrExit: + STOP_MD_PERF(GetMethodProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetMethodProps() + + +//***************************************************************************** +// Return the property of a MemberRef +//***************************************************************************** +STDMETHODIMP RegMeta::GetMemberRefProps( // S_OK or error. + mdMemberRef mr, // [IN] given memberref + mdToken *ptk, // [OUT] Put classref or classdef here. + __out_ecount_opt (cchMember) LPWSTR szMember, // [OUT] buffer to fill for member's name + ULONG cchMember, // [IN] the count of char of szMember + ULONG *pchMember, // [OUT] actual count of char in member name + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to meta data blob value + ULONG *pbSig) // [OUT] actual size of signature blob +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + MemberRefRec *pMemberRefRec; + + LOG((LOGMD, "MD RegMeta::GetMemberRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mr, ptk, szMember, cchMember, pchMember, ppvSigBlob, pbSig)); + + + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mr) == mdtMemberRef); + + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(mr), &pMemberRefRec)); + + if (ptk) + { + *ptk = pMiniMd->getClassOfMemberRef(pMemberRefRec); + if ( IsGlobalMethodParentToken(*ptk) ) + { + // If the parent of MemberRef is the <Module>, return mdTypeDefNil instead. + *ptk = mdTypeDefNil; + } + + } + if (ppvSigBlob || pbSig) + { + // caller wants signature information + PCCOR_SIGNATURE pvSigTmp; + ULONG cbSig; + IfFailGo(pMiniMd->getSignatureOfMemberRef(pMemberRefRec, &pvSigTmp, &cbSig)); + if ( ppvSigBlob ) + *ppvSigBlob = pvSigTmp; + if ( pbSig) + *pbSig = cbSig; + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szMember || pchMember) + { + IfFailGo( pMiniMd->getNameOfMemberRef(pMemberRefRec, szMember, cchMember, pchMember) ); + } + +ErrExit: + + STOP_MD_PERF(GetMemberRefProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetMemberRefProps() + + +//***************************************************************************** +// enumerate Property tokens for a typedef +//***************************************************************************** +STDMETHODIMP RegMeta::EnumProperties( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdProperty rProperties[], // [OUT] Put Properties here. + ULONG cMax, // [IN] Max properties to put. + ULONG *pcProperties) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart = 0; + ULONG ridEnd = 0; + ULONG ridMax = 0; + HENUMInternal *pEnum = *ppmdEnum; + + LOG((LOGMD, "MD RegMeta::EnumProperties(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, td, rProperties, cMax, pcProperties)); + + START_MD_PERF(); + LOCKREAD(); + + if (IsNilToken(td)) + { + if (pcProperties) + *pcProperties = 0; + hr = S_FALSE; + goto ErrExit; + } + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + RID ridPropertyMap; + PropertyMapRec *pPropertyMapRec; + + // get the starting/ending rid of properties of this typedef + IfFailGo(pMiniMd->FindPropertyMapFor(RidFromToken(td), &ridPropertyMap)); + if (!InvalidRid(ridPropertyMap)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + ridMax = pMiniMd->getCountPropertys() + 1; + if(ridStart == 0) ridStart = 1; + if(ridEnd > ridMax) ridEnd = ridMax; + if(ridStart > ridEnd) ridStart=ridEnd; + } + + if (pMiniMd->HasIndirectTable(TBL_Property) || pMiniMd->HasDelete()) + { + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtProperty, &pEnum) ); + + // add all methods to the dynamic array + for (ULONG index = ridStart; index < ridEnd; index++ ) + { + if (pMiniMd->HasDelete() && + ((m_OptionValue.m_ImportOption & MDImportOptionAllProperties) == 0)) + { + PropertyRec *pRec; + RID rid; + IfFailGo(pMiniMd->GetPropertyRid(index, &rid)); + IfFailGo(pMiniMd->GetPropertyRecord(rid, &pRec)); + LPCUTF8 szPropertyName; + IfFailGo(pMiniMd->getNameOfProperty(pRec, &szPropertyName)); + if (IsPrRTSpecialName(pRec->GetPropFlags()) && IsDeletedName(szPropertyName)) + { + continue; + } + } + RID rid; + IfFailGo(pMiniMd->GetPropertyRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtProperty))); + } + } + else + { + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtProperty, ridStart, ridEnd, &pEnum) ); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rProperties, pcProperties); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + + STOP_MD_PERF(EnumProperties); + END_ENTRYPOINT_NOTHROW; + + return hr; + +} // STDMETHODIMP RegMeta::EnumProperties() + + +//***************************************************************************** +// enumerate event tokens for a typedef +//***************************************************************************** +STDMETHODIMP RegMeta::EnumEvents( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdEvent rEvents[], // [OUT] Put events here. + ULONG cMax, // [IN] Max events to put. + ULONG *pcEvents) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart = 0; + ULONG ridEnd = 0; + ULONG ridMax = 0; + HENUMInternal *pEnum = *ppmdEnum; + + LOG((LOGMD, "MD RegMeta::EnumEvents(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, td, rEvents, cMax, pcEvents)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + RID ridEventMap; + EventMapRec *pEventMapRec; + + // get the starting/ending rid of properties of this typedef + IfFailGo(pMiniMd->FindEventMapFor(RidFromToken(td), &ridEventMap)); + if (!InvalidRid(ridEventMap)) + { + IfFailGo(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec)); + ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec); + IfFailGo(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd)); + ridMax = pMiniMd->getCountEvents() + 1; + if(ridStart == 0) ridStart = 1; + if(ridEnd > ridMax) ridEnd = ridMax; + if(ridStart > ridEnd) ridStart=ridEnd; + } + + if (pMiniMd->HasIndirectTable(TBL_Event) || pMiniMd->HasDelete()) + { + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtEvent, &pEnum) ); + + // add all methods to the dynamic array + for (ULONG index = ridStart; index < ridEnd; index++ ) + { + if (pMiniMd->HasDelete() && + ((m_OptionValue.m_ImportOption & MDImportOptionAllEvents) == 0)) + { + EventRec *pRec; + RID rid; + IfFailGo(pMiniMd->GetEventRid(index, &rid)); + IfFailGo(pMiniMd->GetEventRecord(rid, &pRec)); + LPCSTR szEventName; + IfFailGo(pMiniMd->getNameOfEvent(pRec, &szEventName)); + if (IsEvRTSpecialName(pRec->GetEventFlags()) && IsDeletedName(szEventName)) + { + continue; + } + } + RID rid; + IfFailGo(pMiniMd->GetEventRid(index, &rid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(rid, mdtEvent))); + } + } + else + { + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtEvent, ridStart, ridEnd, &pEnum) ); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rEvents, pcEvents); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + + STOP_MD_PERF(EnumEvents); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumEvents() + + + +//***************************************************************************** +// return the properties of an event token +//***************************************************************************** +STDMETHODIMP RegMeta::GetEventProps( // S_OK, S_FALSE, or error. + mdEvent ev, // [IN] event token + mdTypeDef *pClass, // [OUT] typedef containing the event declarion. + LPCWSTR szEvent, // [OUT] Event name + ULONG cchEvent, // [IN] the count of wchar of szEvent + ULONG *pchEvent, // [OUT] actual count of wchar for event's name + DWORD *pdwEventFlags, // [OUT] Event flags. + mdToken *ptkEventType, // [OUT] EventType class + mdMethodDef *pmdAddOn, // [OUT] AddOn method of the event + mdMethodDef *pmdRemoveOn, // [OUT] RemoveOn method of the event + mdMethodDef *pmdFire, // [OUT] Fire method of the event + mdMethodDef rmdOtherMethod[], // [OUT] other method of the event + ULONG cMax, // [IN] size of rmdOtherMethod + ULONG *pcOtherMethod) // [OUT] total number of other method of this event +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + EventRec *pRec; + HENUMInternal hEnum; + + LOG((LOGMD, "MD RegMeta::GetEventProps(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + ev, pClass, MDSTR(szEvent), cchEvent, pchEvent, pdwEventFlags, ptkEventType, + pmdAddOn, pmdRemoveOn, pmdFire, rmdOtherMethod, cMax, pcOtherMethod)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(ev) == mdtEvent); + + HENUMInternal::ZeroEnum(&hEnum); + IfFailGo(pMiniMd->GetEventRecord(RidFromToken(ev), &pRec)); + + if ( pClass ) + { + // find the event map entry corresponding to this event + IfFailGo( pMiniMd->FindParentOfEventHelper( ev, pClass ) ); + } + if ( pdwEventFlags ) + { + *pdwEventFlags = pMiniMd->getEventFlagsOfEvent(pRec); + } + if ( ptkEventType ) + { + *ptkEventType = pMiniMd->getEventTypeOfEvent(pRec); + } + { + MethodSemanticsRec *pSemantics; + RID ridCur; + ULONG cCurOtherMethod = 0; + ULONG ulSemantics; + mdMethodDef tkMethod; + + // initialize output parameters + if (pmdAddOn) + *pmdAddOn = mdMethodDefNil; + if (pmdRemoveOn) + *pmdRemoveOn = mdMethodDefNil; + if (pmdFire) + *pmdFire = mdMethodDefNil; + + IfFailGo( pMiniMd->FindMethodSemanticsHelper(ev, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur)) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics)); + ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics); + tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef ); + switch (ulSemantics) + { + case msAddOn: + if (pmdAddOn) *pmdAddOn = tkMethod; + break; + case msRemoveOn: + if (pmdRemoveOn) *pmdRemoveOn = tkMethod; + break; + case msFire: + if (pmdFire) *pmdFire = tkMethod; + break; + case msOther: + if (cCurOtherMethod < cMax) + rmdOtherMethod[cCurOtherMethod] = tkMethod; + cCurOtherMethod++; + break; + default: + _ASSERTE(!"BadKind!"); + } + } + + // set the output parameter + if (pcOtherMethod) + *pcOtherMethod = cCurOtherMethod; + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szEvent || pchEvent) + { + IfFailGo( pMiniMd->getNameOfEvent(pRec, (LPWSTR) szEvent, cchEvent, pchEvent) ); + } + +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + STOP_MD_PERF(GetEventProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetEventProps() + + +//***************************************************************************** +// given a method, return an arra of event/property tokens for each accessor role +// it is defined to have +//***************************************************************************** +STDMETHODIMP RegMeta::EnumMethodSemantics( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdMethodDef mb, // [IN] MethodDef to scope the enumeration. + mdToken rEventProp[], // [OUT] Put Event/Property here. + ULONG cMax, // [IN] Max properties to put. + ULONG *pcEventProp) // [OUT] Put # put here. +{ + HRESULT hr = NOERROR; + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridEnd; + ULONG index; + HENUMInternal *pEnum = *ppmdEnum; + MethodSemanticsRec *pRec; + + LOG((LOGMD, "MD RegMeta::EnumMethodSemantics(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, mb, rEventProp, cMax, pcEventProp)); + + START_MD_PERF(); + LOCKREAD(); + + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( (DWORD) -1, &pEnum) ); + + // get the range of method rids given a typedef + ridEnd = pMiniMd->getCountMethodSemantics(); + + for (index = 1; index <= ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(index, &pRec)); + if ( pMiniMd->getMethodOfMethodSemantics(pRec) == mb ) + { + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, pMiniMd->getAssociationOfMethodSemantics(pRec) ) ); + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rEventProp, pcEventProp); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + + STOP_MD_PERF(EnumMethodSemantics); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMethodSemantics() + + + +//***************************************************************************** +// return the role flags for the method/propevent pair +//***************************************************************************** +STDMETHODIMP RegMeta::GetMethodSemantics( // S_OK, S_FALSE, or error. + mdMethodDef mb, // [IN] method token + mdToken tkEventProp, // [IN] event/property token. + DWORD *pdwSemanticsFlags) // [OUT] the role flags for the method/propevent pair +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + MethodSemanticsRec *pRec; + ULONG ridCur; + HENUMInternal hEnum; + + LOG((LOGMD, "MD RegMeta::GetMethodSemantics(0x%08x, 0x%08x, 0x%08x)\n", + mb, tkEventProp, pdwSemanticsFlags)); + + + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mb) == mdtMethodDef); + _ASSERTE( pdwSemanticsFlags ); + + *pdwSemanticsFlags = 0; + HENUMInternal::ZeroEnum(&hEnum); + + // loop through all methods associated with this tkEventProp + IfFailGo( pMiniMd->FindMethodSemanticsHelper(tkEventProp, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur)) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pRec)); + if ( pMiniMd->getMethodOfMethodSemantics(pRec) == mb ) + { + // we findd the match + *pdwSemanticsFlags = pMiniMd->getSemanticOfMethodSemantics(pRec); + goto ErrExit; + } + } + + IfFailGo( CLDB_E_RECORD_NOTFOUND ); + +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + STOP_MD_PERF(GetMethodSemantics); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetMethodSemantics() + + + +//***************************************************************************** +// return the class layout information +//***************************************************************************** +STDMETHODIMP RegMeta::GetClassLayout( + mdTypeDef td, // [IN] give typedef + DWORD *pdwPackSize, // [OUT] 1, 2, 4, 8, or 16 + COR_FIELD_OFFSET rFieldOffset[], // [OUT] field offset array + ULONG cMax, // [IN] size of the array + ULONG *pcFieldOffset, // [OUT] needed array size + ULONG *pulClassSize) // [OUT] the size of the class +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + ClassLayoutRec *pRec; + RID ridClassLayout; + int bLayout=0; // Was any layout information found? + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + LOG((LOGMD, "MD RegMeta::GetClassLayout(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + td, pdwPackSize, rFieldOffset, cMax, pcFieldOffset, pulClassSize)); + + START_MD_PERF(); + LOCKREAD(); + + IfFailGo(pMiniMd->FindClassLayoutHelper(td, &ridClassLayout)); + + if (InvalidRid(ridClassLayout)) + { // Nothing specified - return default values of 0. + if ( pdwPackSize ) + *pdwPackSize = 0; + if ( pulClassSize ) + *pulClassSize = 0; + } + else + { + IfFailGo(pMiniMd->GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec)); + if ( pdwPackSize ) + *pdwPackSize = pMiniMd->getPackingSizeOfClassLayout(pRec); + if ( pulClassSize ) + *pulClassSize = pMiniMd->getClassSizeOfClassLayout(pRec); + bLayout = 1; + } + + // fill the layout array + if (rFieldOffset || pcFieldOffset) + { + ULONG iFieldOffset = 0; + ULONG ridFieldStart; + ULONG ridFieldEnd; + ULONG ridFieldLayout; + ULONG ulOffset; + TypeDefRec *pTypeDefRec; + FieldLayoutRec *pLayout2Rec; + mdFieldDef fd; + + // record for this typedef in TypeDef Table + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + + // find the starting and end field for this typedef + ridFieldStart = pMiniMd->getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ridFieldEnd)); + + // loop through the field table + + for(; ridFieldStart < ridFieldEnd; ridFieldStart++) + { + // Calculate the field token. + RID rid; + IfFailGo(pMiniMd->GetFieldRid(ridFieldStart, &rid)); + fd = TokenFromRid(rid, mdtFieldDef); + + // Calculate the FieldLayout rid for the current field. + IfFailGo(pMiniMd->FindFieldLayoutHelper(fd, &ridFieldLayout)); + + // Calculate the offset. + if (InvalidRid(ridFieldLayout)) + ulOffset = (ULONG) -1; + else + { + // get the FieldLayout record. + IfFailGo(pMiniMd->GetFieldLayoutRecord(ridFieldLayout, &pLayout2Rec)); + ulOffset = pMiniMd->getOffSetOfFieldLayout(pLayout2Rec); + bLayout = 1; + } + + // fill in the field layout if output buffer still has space. + if (cMax > iFieldOffset && rFieldOffset) + { + rFieldOffset[iFieldOffset].ridOfField = fd; + rFieldOffset[iFieldOffset].ulOffset = ulOffset; + } + + // advance the index to the buffer. + iFieldOffset++; + } + + if (bLayout && pcFieldOffset) + *pcFieldOffset = iFieldOffset; + } + + if (!bLayout) + hr = CLDB_E_RECORD_NOTFOUND; + +ErrExit: + STOP_MD_PERF(GetClassLayout); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetClassLayout() + + + +//***************************************************************************** +// return the native type of a field +//***************************************************************************** +STDMETHODIMP RegMeta::GetFieldMarshal( + mdToken tk, // [IN] given a field's memberdef + PCCOR_SIGNATURE *ppvNativeType, // [OUT] native type of this field + ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + RID rid; + FieldMarshalRec *pFieldMarshalRec; + + + _ASSERTE(ppvNativeType != NULL && pcbNativeType != NULL); + + LOG((LOGMD, "MD RegMeta::GetFieldMarshal(0x%08x, 0x%08x, 0x%08x)\n", + tk, ppvNativeType, pcbNativeType)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(tk) == mdtParamDef || TypeFromToken(tk) == mdtFieldDef); + + // find the row containing the marshal definition for tk + IfFailGo(pMiniMd->FindFieldMarshalHelper(tk, &rid)); + if (InvalidRid(rid)) + { + IfFailGo( CLDB_E_RECORD_NOTFOUND ); + } + IfFailGo(pMiniMd->GetFieldMarshalRecord(rid, &pFieldMarshalRec)); + + // get the native type + IfFailGo(pMiniMd->getNativeTypeOfFieldMarshal(pFieldMarshalRec, ppvNativeType, pcbNativeType)); + +ErrExit: + STOP_MD_PERF(GetFieldMarshal); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetFieldMarshal() + + + +//***************************************************************************** +// return the RVA and implflag for MethodDef or FieldDef token +//***************************************************************************** +STDMETHODIMP +RegMeta::GetRVA( + mdToken tk, // Member for which to set offset + ULONG *pulCodeRVA, // The offset + DWORD *pdwImplFlags) // the implementation flags +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "MD RegMeta::GetRVA(0x%08x, 0x%08x, 0x%08x)\n", + tk, pulCodeRVA, pdwImplFlags)); + + START_MD_PERF(); + LOCKREAD(); + + if (TypeFromToken(tk) == mdtMethodDef) + { + if (tk == mdMethodDefNil) + { // Backward compatibility with CLR 2.0 implementation + if (pulCodeRVA != NULL) + *pulCodeRVA = 0; + if (pdwImplFlags != NULL) + *pdwImplFlags = 0; + + hr = S_OK; + goto ErrExit; + } + + // MethodDef token + MethodRec *pMethodRec; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tk), &pMethodRec)); + + if (pulCodeRVA != NULL) + { + *pulCodeRVA = pMiniMd->getRVAOfMethod(pMethodRec); + } + if (pdwImplFlags != NULL) + { + *pdwImplFlags = pMiniMd->getImplFlagsOfMethod(pMethodRec); + } + } + else + { // FieldDef token or invalid type of token (not mdtMethodDef) + ULONG iRecord; + + IfFailGo(pMiniMd->FindFieldRVAHelper(tk, &iRecord)); + + if (InvalidRid(iRecord)) + { + if (pulCodeRVA != NULL) + *pulCodeRVA = 0; + + IfFailGo(CLDB_E_RECORD_NOTFOUND); + } + + FieldRVARec *pFieldRVARec; + IfFailGo(pMiniMd->GetFieldRVARecord(iRecord, &pFieldRVARec)); + + if (pulCodeRVA != NULL) + { + *pulCodeRVA = pMiniMd->getRVAOfFieldRVA(pFieldRVARec); + } + if (pdwImplFlags != NULL) + { + *pdwImplFlags = 0; + } + } +ErrExit: + STOP_MD_PERF(GetRVA); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetRVA + + + +//***************************************************************************** +// Get the Action and Permissions blob for a given PermissionSet. +//***************************************************************************** +STDMETHODIMP RegMeta::GetPermissionSetProps( + mdPermission pm, // [IN] the permission token. + DWORD *pdwAction, // [OUT] CorDeclSecurity. + void const **ppvPermission, // [OUT] permission blob. + ULONG *pcbPermission) // [OUT] count of bytes of pvPermission. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::GetPermissionSetProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + pm, pdwAction, ppvPermission, pcbPermission)); + + CMiniMdRW *pMiniMd = NULL; + DeclSecurityRec *pRecord = NULL; + + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + IfFailGo(pMiniMd->GetDeclSecurityRecord(RidFromToken(pm), &pRecord)); + + _ASSERTE(TypeFromToken(pm) == mdtPermission && RidFromToken(pm)); + + // If you want the BLOB, better get the BLOB size as well. + _ASSERTE(!ppvPermission || pcbPermission); + + if (pdwAction) + *pdwAction = pMiniMd->getActionOfDeclSecurity(pRecord); + + if (ppvPermission != NULL) + { + IfFailGo(pMiniMd->getPermissionSetOfDeclSecurity(pRecord, (const BYTE **)ppvPermission, pcbPermission)); + } + +ErrExit: + + STOP_MD_PERF(GetPermissionSetProps); + END_ENTRYPOINT_NOTHROW; + return hr; +} // STDMETHODIMP RegMeta::GetPermissionSetProps() + + + +//***************************************************************************** +// Given a signature token, get return a pointer to the signature to the caller. +// +//<TODO>@FUTURE: for short term we have a problem where there is no way to get a +// fixed up address for a blob and do Merge at the same time. So we've created +// this dummy table called StandAloneSig which you hand out a rid for. This +// makes finding the sig an extra indirection that is not required. The +// Model Compression save code needs to map the token into a byte offset in +// the heap. Perhaps we can have another mdt* type to switch on the difference. +// But ultimately it has to simply be "pBlobHeapBase + RidFromToken(mdSig)".</TODO> +//***************************************************************************** +STDMETHODIMP RegMeta::GetSigFromToken( // S_OK or error. + mdSignature mdSig, // [IN] Signature token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token. + ULONG *pcbSig) // [OUT] return size of signature. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + StandAloneSigRec *pRec; + + LOG((LOGMD, "MD RegMeta::GetSigFromToken(0x%08x, 0x%08x, 0x%08x)\n", + mdSig, ppvSig, pcbSig)); + + + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdSig) == mdtSignature); + _ASSERTE(ppvSig && pcbSig); + + IfFailGo(pMiniMd->GetStandAloneSigRecord(RidFromToken(mdSig), &pRec)); + IfFailGo(pMiniMd->getSignatureOfStandAloneSig(pRec, ppvSig, pcbSig)); + + +ErrExit: + + STOP_MD_PERF(GetSigFromToken); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetSigFromToken() + + +//******************************************************************************* +// return the ModuleRef properties +//******************************************************************************* +STDMETHODIMP RegMeta::GetModuleRefProps( // S_OK or error. + mdModuleRef mur, // [IN] moduleref token. + __out_ecount_opt (cchName) LPWSTR szName, // [OUT] buffer to fill with the moduleref name. + ULONG cchName, // [IN] size of szName in wide characters. + ULONG *pchName) // [OUT] actual count of characters in the name. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + ModuleRefRec *pModuleRefRec; + + + + LOG((LOGMD, "MD RegMeta::GetModuleRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mur, szName, cchName, pchName)); + START_MD_PERF(); + LOCKREAD(); + + IfFailGo(pMiniMd->GetModuleRefRecord(RidFromToken(mur), &pModuleRefRec)); + + _ASSERTE(TypeFromToken(mur) == mdtModuleRef); + + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szName || pchName) + { + IfFailGo( pMiniMd->getNameOfModuleRef(pModuleRefRec, szName, cchName, pchName) ); + } + +ErrExit: + + STOP_MD_PERF(GetModuleRefProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetModuleRefProps() + + + +//******************************************************************************* +// enumerating through all of the ModuleRefs +//******************************************************************************* +STDMETHODIMP RegMeta::EnumModuleRefs( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] pointer to the enum. + mdModuleRef rModuleRefs[], // [OUT] put modulerefs here. + ULONG cMax, // [IN] max memberrefs to put. + ULONG *pcModuleRefs) // [OUT] put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "MD RegMeta::EnumModuleRefs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, rModuleRefs, cMax, pcModuleRefs)); + + START_MD_PERF(); + LOCKREAD(); + + if (*ppmdEnum == NULL) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator + IfFailGo(HENUMInternal::CreateSimpleEnum( + mdtModuleRef, + 1, + pMiniMd->getCountModuleRefs() + 1, + &pEnum)); + + // set the output parameter + *ppmdEnum = pEnum; + } + else + { + pEnum = *ppmdEnum; + } + + // we can only fill the minimun of what caller asked for or what we have left + IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rModuleRefs, pcModuleRefs)); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumModuleRefs); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumModuleRefs() + + +//******************************************************************************* +// return properties regarding a TypeSpec +//******************************************************************************* +STDMETHODIMP RegMeta::GetTypeSpecFromToken( // S_OK or error. + mdTypeSpec typespec, // [IN] Signature token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token. + ULONG *pcbSig) // [OUT] return size of signature. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + TypeSpecRec *pRec = NULL; + + LOG((LOGMD, "MD RegMeta::GetTypeSpecFromToken(0x%08x, 0x%08x, 0x%08x)\n", + typespec, ppvSig, pcbSig)); + + + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(typespec) == mdtTypeSpec); + _ASSERTE(ppvSig && pcbSig); + + IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(typespec), &pRec)); + IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, ppvSig, pcbSig)); + +ErrExit: + + STOP_MD_PERF(GetTypeSpecFromToken); + END_ENTRYPOINT_NOTHROW; + return hr; +} // STDMETHODIMP RegMeta::GetTypeSpecFromToken() + + +//***************************************************************************** +// For those items that have a name, retrieve a direct pointer to the name +// off of the heap. This reduces copies made for the caller. +//***************************************************************************** +#define NAME_FROM_TOKEN_TYPE(RecType, TokenType) \ + case mdt ## TokenType: \ + { \ + RecType ## Rec *pRecord; \ + IfFailGo(pMiniMd->Get ## RecType ## Record(RidFromToken(tk), &pRecord)); \ + IfFailGo(pMiniMd->getNameOf ## RecType (pRecord, pszUtf8NamePtr)); \ + } \ + break; +#define NAME_FROM_TOKEN(RecType) NAME_FROM_TOKEN_TYPE(RecType, RecType) + +STDMETHODIMP RegMeta::GetNameFromToken( // S_OK or error. + mdToken tk, // [IN] Token to get name from. Must have a name. + MDUTF8CSTR *pszUtf8NamePtr) // [OUT] Return pointer to UTF8 name in heap. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "MD RegMeta::GetNameFromToken(0x%08x, 0x%08x)\n", + tk, pszUtf8NamePtr)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(pszUtf8NamePtr); + + switch (TypeFromToken(tk)) + { + NAME_FROM_TOKEN(Module); + NAME_FROM_TOKEN(TypeRef); + NAME_FROM_TOKEN(TypeDef); + NAME_FROM_TOKEN_TYPE(Field, FieldDef); + NAME_FROM_TOKEN_TYPE(Method, MethodDef); + NAME_FROM_TOKEN_TYPE(Param, ParamDef); + NAME_FROM_TOKEN(MemberRef); + NAME_FROM_TOKEN(Event); + NAME_FROM_TOKEN(Property); + NAME_FROM_TOKEN(ModuleRef); + + default: + hr = E_INVALIDARG; + } + +ErrExit: + + STOP_MD_PERF(GetNameFromToken); + + END_ENTRYPOINT_NOTHROW; + + return (hr); +} // RegMeta::GetNameFromToken + + +//***************************************************************************** +// Get the symbol binding data back from the module if it is there. It is +// stored as a custom value. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumUnresolvedMethods( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken rMethods[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens) // [OUT] Put # put here. +{ +#ifdef FEATURE_METADATA_EMIT + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal ** ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG iCountTypeDef; // Count of TypeDefs. + ULONG ulStart, ulEnd; // Bounds of methods on a given TypeDef. + ULONG index; // For counting methods on a TypeDef. + ULONG indexTypeDef; // For counting TypeDefs. + bool bIsInterface; // Is a given TypeDef an interface? + HENUMInternal * pEnum = *ppmdEnum; // Enum we're working with. + CMiniMdRW * pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "MD RegMeta::EnumUnresolvedMethods(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, rMethods, cMax, pcTokens)); + + START_MD_PERF(); + + // take the write lock. Because we should have not have two EnumUnresolvedMethods being called at the + // same time. Ref to Def map may be calculated incorrectly. + LOCKWRITE(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + MethodRec *pMethodRec; + TypeDefRec *pTypeDefRec; + + // make sure our ref to def optimization is up to date + IfFailGo( RefToDefOptimization() ); + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( (DWORD) -1, &pEnum) ); + + // Loop through all of the methoddef except global functions. + // If methoddef has RVA 0 and not miRuntime, mdAbstract, mdVirtual, mdNative, + // we will fill it into the enumerator. + // + iCountTypeDef = pMiniMd->getCountTypeDefs(); + + for (indexTypeDef = 2; indexTypeDef <= iCountTypeDef; indexTypeDef ++ ) + { + IfFailGo(pMiniMd->GetTypeDefRecord(indexTypeDef, &pTypeDefRec)); + + // If the type is an interface, check the static methods. + bIsInterface = IsTdInterface(pTypeDefRec->GetFlags()); + + ulStart = pMiniMd->getMethodListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(indexTypeDef, &ulEnd)); + + // always report errors even with any unimplemented methods + for (index = ulStart; index < ulEnd; index++) + { + RID methodRid; + IfFailGo(pMiniMd->GetMethodRid(index, &methodRid)); + IfFailGo(pMiniMd->GetMethodRecord(methodRid, &pMethodRec)); + + // If the type is an interface, and the method is not static, on to next. + if (bIsInterface && !IsMdStatic(pMethodRec->GetFlags())) + continue; + + if ( IsMiForwardRef(pMethodRec->GetImplFlags()) ) + { + if ( IsMdPinvokeImpl(pMethodRec->GetFlags()) ) + { + continue; + } + if ( IsMiRuntime(pMethodRec->GetImplFlags()) || IsMiInternalCall(pMethodRec->GetImplFlags())) + { + continue; + } + + if (IsMdAbstract(pMethodRec->GetFlags())) + continue; + + // If a methoddef has RVA 0 and it is not an abstract or virtual method. + // Nor it is a runtime generated method nore a native method, then we add it + // to the unresolved list. + // + IfFailGo(pMiniMd->GetMethodRid(index, &methodRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(methodRid, mdtMethodDef))); + + LOG((LOGMD, "MD adding unresolved MethodDef: token=%08x, flags=%08x, impl flags=%08x\n", + TokenFromRid(methodRid, mdtMethodDef), + pMethodRec->GetFlags(), pMethodRec->GetImplFlags())); + } + } + } + + MemberRefRec *pMemberRefRec; + ULONG iCount; + + // loop through MemberRef tables and find all of the unsats + iCount = pMiniMd->getCountMemberRefs(); + for (index = 1; index <= iCount; index++ ) + { + mdToken defToken; + mdMemberRef refToken = TokenFromRid(index, mdtMemberRef); + IfFailGo(pMiniMd->GetMemberRefRecord(index, &pMemberRefRec)); + pMiniMd->GetTokenRemapManager()->ResolveRefToDef(refToken, &defToken); + + if ( pMiniMd->getClassOfMemberRef(pMemberRefRec) == m_tdModule && defToken == refToken ) + { + // unresovled externals reference if parent token is not resolved and this ref token does not + // map to any def token (can be MethodDef or FieldDef). + // + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, refToken) ); + + LOG((LOGMD, "MD adding unresolved MemberRef: token=%08x, doesn't have a proper parent\n", + refToken )); + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumUnresolvedMethods); + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT +} // RegMeta::EnumUnresolvedMethods + +//***************************************************************************** +// Return the User string given the token. The offset into the Blob pool where +// the string is stored in Unicode is embedded inside the token. +//***************************************************************************** +STDMETHODIMP RegMeta::GetUserString( // S_OK or error. + mdString stk, // [IN] String token. + __out_ecount_opt(cchStringSize) LPWSTR wszString, // [OUT] Copy of string. + ULONG cchStringSize, // [IN] Max chars of room in szString. + ULONG *pcchStringSize) // [OUT] How many chars in actual string. +{ + HRESULT hr = S_OK; + ULONG cchStringSize_Dummy; + MetaData::DataBlob userString; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::GetUserString(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + stk, wszString, cchStringSize, pcchStringSize)); + + START_MD_PERF(); + LOCKREAD(); + + // Get the string data. + IfFailGo(m_pStgdb->m_MiniMd.GetUserString(RidFromToken(stk), &userString)); + // Want to get whole characters, followed by byte to indicate whether there + // are extended characters (>= 0x80). + if ((userString.GetSize() % sizeof(WCHAR)) == 0) + { + Debug_ReportError("User strings should have 1 byte terminator (either 0x00 or 0x80)."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Strip off the last byte. + if (!userString.TruncateBySize(1)) + { + Debug_ReportInternalError("There's a bug, because previous % 2 check didn't return 0."); + IfFailGo(METADATA_E_INTERNAL_ERROR); + } + + // Convert bytes to characters. + if (pcchStringSize == NULL) + { + pcchStringSize = &cchStringSize_Dummy; + } + *pcchStringSize = userString.GetSize() / sizeof(WCHAR); + + // Copy the string back to the caller. + if ((wszString != NULL) && (cchStringSize > 0)) + { + ULONG cbStringSize = cchStringSize * sizeof(WCHAR); + memcpy( + wszString, + userString.GetDataPointer(), + min(userString.GetSize(), cbStringSize)); + if (cbStringSize < userString.GetSize()) + { + if ((wszString != NULL) && (cchStringSize > 0)) + { // null-terminate the truncated output string + wszString[cchStringSize - 1] = W('\0'); + } + + hr = CLDB_S_TRUNCATION; + } + } + + ErrExit: + STOP_MD_PERF(GetUserString); + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetUserString + +//***************************************************************************** +// Return contents of Pinvoke given the forwarded member token. +//***************************************************************************** +STDMETHODIMP RegMeta::GetPinvokeMap( // S_OK or error. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD *pdwMappingFlags, // [OUT] Flags used for mapping. + __out_ecount_opt (cchImportName) LPWSTR szImportName, // [OUT] Import name. + ULONG cchImportName, // [IN] Size of the name buffer. + ULONG *pchImportName, // [OUT] Actual number of characters stored. + mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ImplMapRec * pRecord; + ULONG iRecord; + + LOG((LOGMD, "MD RegMeta::GetPinvokeMap(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + tk, pdwMappingFlags, szImportName, cchImportName, pchImportName, pmrImportDLL)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || + TypeFromToken(tk) == mdtMethodDef); + + IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord)); + if (InvalidRid(iRecord)) + { + IfFailGo( CLDB_E_RECORD_NOTFOUND ); + } + else + IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord)); + + if (pdwMappingFlags) + *pdwMappingFlags = m_pStgdb->m_MiniMd.getMappingFlagsOfImplMap(pRecord); + if (pmrImportDLL) + *pmrImportDLL = m_pStgdb->m_MiniMd.getImportScopeOfImplMap(pRecord); + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szImportName || pchImportName) + IfFailGo(m_pStgdb->m_MiniMd.getImportNameOfImplMap(pRecord, szImportName, cchImportName, pchImportName)); +ErrExit: + STOP_MD_PERF(GetPinvokeMap); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetPinvokeMap() + +//***************************************************************************** +// Enumerate through all the local sigs. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumSignatures( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] pointer to the enum. + mdModuleRef rSignatures[], // [OUT] put signatures here. + ULONG cmax, // [IN] max signatures to put. + ULONG *pcSignatures) // [OUT] put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppsigEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "MD RegMeta::EnumSignatures(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, rSignatures, cmax, pcSignatures)); + + START_MD_PERF(); + LOCKREAD(); + + if (*ppsigEnum == NULL) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator. + IfFailGo(HENUMInternal::CreateSimpleEnum( + mdtSignature, + 1, + pMiniMd->getCountStandAloneSigs() + 1, + &pEnum)); + + // set the output parameter + *ppsigEnum = pEnum; + } + else + { + pEnum = *ppsigEnum; + } + + // we can only fill the minimum of what caller asked for or what we have left. + IfFailGo(HENUMInternal::EnumWithCount(pEnum, cmax, rSignatures, pcSignatures)); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppsigEnum); + + STOP_MD_PERF(EnumSignatures); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumSignatures + + +//***************************************************************************** +// Enumerate through all the TypeSpec +//***************************************************************************** +STDMETHODIMP RegMeta::EnumTypeSpecs( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] pointer to the enum. + mdTypeSpec rTypeSpecs[], // [OUT] put TypeSpecs here. + ULONG cmax, // [IN] max TypeSpecs to put. + ULONG *pcTypeSpecs) // [OUT] put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "MD RegMeta::EnumTypeSpecs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, rTypeSpecs, cmax, pcTypeSpecs)); + + START_MD_PERF(); + LOCKREAD(); + + if (*ppEnum == NULL) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + // create the enumerator. + IfFailGo(HENUMInternal::CreateSimpleEnum( + mdtTypeSpec, + 1, + pMiniMd->getCountTypeSpecs() + 1, + &pEnum)); + + // set the output parameter + *ppEnum = pEnum; + } + else + { + pEnum = *ppEnum; + } + + // we can only fill the minimum of what caller asked for or what we have left. + IfFailGo(HENUMInternal::EnumWithCount(pEnum, cmax, rTypeSpecs, pcTypeSpecs)); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppEnum); + + STOP_MD_PERF(EnumTypeSpecs); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumTypeSpecs + + +//***************************************************************************** +// Enumerate through all the User Strings. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumUserStrings( // S_OK or error. + HCORENUM *phEnum, // [IN/OUT] pointer to the enum. + mdString rStrings[], // [OUT] put Strings here. + ULONG cmax, // [IN] max Strings to put. + ULONG *pcStrings) // [OUT] put # put here. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum = *ppEnum; + + LOG((LOGMD, "MD RegMeta::EnumUserStrings(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, rStrings, cmax, pcStrings)); + + START_MD_PERF(); + LOCKREAD(); + + if (pEnum == NULL) + { + // instantiating a new ENUM. + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + IfFailGo(HENUMInternal::CreateDynamicArrayEnum(mdtString, &pEnum)); + + // Add all strings to the dynamic array + for (UINT32 nIndex = 0; ;) + { + MetaData::DataBlob userString; + UINT32 nNextIndex; + hr = pMiniMd->GetUserStringAndNextIndex( + nIndex, + &userString, + &nNextIndex); + IfFailGo(hr); + if (hr == S_FALSE) + { // We reached the last user string + hr = S_OK; + break; + } + _ASSERTE(hr == S_OK); + + // Skip empty strings + if (userString.IsEmpty()) + { + nIndex = nNextIndex; + continue; + } + // Add the user string into dynamic array + IfFailGo(HENUMInternal::AddElementToEnum( + pEnum, + TokenFromRid(nIndex, mdtString))); + + // Process next user string in the heap + nIndex = nNextIndex; + } + + // set the output parameter. + *ppEnum = pEnum; + } + + // fill the output token buffer. + hr = HENUMInternal::EnumWithCount(pEnum, cmax, rStrings, pcStrings); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppEnum); + + + STOP_MD_PERF(EnumUserStrings); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumUserStrings + + +//***************************************************************************** +// This routine gets the param token given a method and index of the parameter. +//***************************************************************************** +STDMETHODIMP RegMeta::GetParamForMethodIndex( // S_OK or error. + mdMethodDef md, // [IN] Method token. + ULONG ulParamSeq, // [IN] Parameter sequence. + mdParamDef *ppd) // [IN] Put Param token here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + LOG((LOGMD, "MD RegMeta::GetParamForMethodIndex(0x%08x, 0x%08x, 0x%08x)\n", + md, ulParamSeq, ppd)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE((TypeFromToken(md) == mdtMethodDef) && (ulParamSeq != UINT32_MAX) && (ppd != NULL)); + + IfFailGo(_FindParamOfMethod(md, ulParamSeq, ppd)); +ErrExit: + + STOP_MD_PERF(GetParamForMethodIndex); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetParamForMethodIndex() + +//***************************************************************************** +// Return the property of a MethodDef or a FieldDef +//***************************************************************************** +HRESULT RegMeta::GetMemberProps( + mdToken mb, // The member for which to get props. + mdTypeDef *pClass, // Put member's class here. + __out_ecount_opt (cchMember) LPWSTR szMember, // Put member's name here. + ULONG cchMember, // Size of szMember buffer in wide chars. + ULONG *pchMember, // Put actual size here + DWORD *pdwAttr, // Put flags here. + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob, // [OUT] actual size of signature blob + ULONG *pulCodeRVA, // [OUT] codeRVA + DWORD *pdwImplFlags, // [OUT] Impl. Flags + DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_* + UVCP_CONSTANT *ppValue, // [OUT] constant value + ULONG *pchValue) // [OUT] size of constant value, string only, wide chars +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::GetMemberProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mb, pClass, szMember, cchMember, pchMember, pdwAttr, ppvSigBlob, pcbSigBlob, + pulCodeRVA, pdwImplFlags, pdwCPlusTypeFlag, ppValue, pchValue)); + + + + START_MD_PERF(); + + _ASSERTE(TypeFromToken(mb) == mdtMethodDef || TypeFromToken(mb) == mdtFieldDef); + + // No need to lock this function. It is calling public APIs. Keep it that way. + + if (TypeFromToken(mb) == mdtMethodDef) + { + // It is a Method + IfFailGo( GetMethodProps( + mb, + pClass, + szMember, + cchMember, + pchMember, + pdwAttr, + ppvSigBlob, + pcbSigBlob, + pulCodeRVA, + pdwImplFlags) ); + } + else + { + // It is a Field + IfFailGo( GetFieldProps( + mb, + pClass, + szMember, + cchMember, + pchMember, + pdwAttr, + ppvSigBlob, + pcbSigBlob, + pdwCPlusTypeFlag, + ppValue, + pchValue) ); + } +ErrExit: + STOP_MD_PERF(GetMemberProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetMemberProps() + +//***************************************************************************** +// Return the property of a FieldDef +//***************************************************************************** +HRESULT RegMeta::GetFieldProps( + mdFieldDef fd, // The field for which to get props. + mdTypeDef *pClass, // Put field's class here. + __out_ecount_opt (cchField) LPWSTR szField, // Put field's name here. + ULONG cchField, // Size of szField buffer in wide chars. + ULONG *pchField, // Put actual size here + DWORD *pdwAttr, // Put flags here. + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob, // [OUT] actual size of signature blob + DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_* + UVCP_CONSTANT *ppValue, // [OUT] constant value + ULONG *pchValue) // [OUT] size of constant value, string only, wide chars +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + FieldRec *pFieldRec; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "MD RegMeta::GetFieldProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + fd, pClass, szField, cchField, pchField, pdwAttr, ppvSigBlob, pcbSigBlob, pdwCPlusTypeFlag, + ppValue, pchValue)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(fd) == mdtFieldDef); + + IfFailGo(pMiniMd->GetFieldRecord(RidFromToken(fd), &pFieldRec)); + + if (pClass) + { + // caller wants parent typedef + IfFailGo( pMiniMd->FindParentOfFieldHelper(fd, pClass) ); + + if ( IsGlobalMethodParentToken(*pClass) ) + { + // If the parent of Field is the <Module>, return mdTypeDefNil instead. + *pClass = mdTypeDefNil; + } + } + if (ppvSigBlob || pcbSigBlob) + { + // caller wants signature information + PCCOR_SIGNATURE pvSigTmp; + ULONG cbSig; + IfFailGo(pMiniMd->getSignatureOfField(pFieldRec, &pvSigTmp, &cbSig)); + if ( ppvSigBlob ) + *ppvSigBlob = pvSigTmp; + if ( pcbSigBlob) + *pcbSigBlob = cbSig; + } + if ( pdwAttr ) + { + *pdwAttr = pMiniMd->getFlagsOfField(pFieldRec); + } + if ( pdwCPlusTypeFlag || ppValue || pchValue) + { + // get the constant value + ULONG cbValue; + RID rid; + IfFailGo(pMiniMd->FindConstantHelper(fd, &rid)); + + if (pchValue) + *pchValue = 0; + + if (InvalidRid(rid)) + { + // There is no constant value associate with it + if (pdwCPlusTypeFlag) + *pdwCPlusTypeFlag = ELEMENT_TYPE_VOID; + + if ( ppValue ) + *ppValue = NULL; + } + else + { + ConstantRec *pConstantRec; + IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec)); + DWORD dwType; + + // get the type of constant value + dwType = pMiniMd->getTypeOfConstant(pConstantRec); + if ( pdwCPlusTypeFlag ) + *pdwCPlusTypeFlag = dwType; + + // get the value blob + if (ppValue != NULL) + { + IfFailGo(pMiniMd->getValueOfConstant(pConstantRec, (const BYTE **)ppValue, &cbValue)); + if (pchValue && dwType == ELEMENT_TYPE_STRING) + *pchValue = cbValue / sizeof(WCHAR); + } + } + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szField || pchField) + { + IfFailGo( pMiniMd->getNameOfField(pFieldRec, szField, cchField, pchField) ); + } + +ErrExit: + STOP_MD_PERF(GetFieldProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetFieldProps() + +//***************************************************************************** +// return the properties of a property token +//***************************************************************************** +HRESULT RegMeta::GetPropertyProps( // S_OK, S_FALSE, or error. + mdProperty prop, // [IN] property token + mdTypeDef *pClass, // [OUT] typedef containing the property declarion. + LPCWSTR szProperty, // [OUT] Property name + ULONG cchProperty, // [IN] the count of wchar of szProperty + ULONG *pchProperty, // [OUT] actual count of wchar for property name + DWORD *pdwPropFlags, // [OUT] property flags. + PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob + ULONG *pbSig, // [OUT] count of bytes in *ppvSig + DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_* + UVCP_CONSTANT *ppDefaultValue, // [OUT] constant value + ULONG *pchDefaultValue, // [OUT] size of constant value, string only, wide chars + mdMethodDef *pmdSetter, // [OUT] setter method of the property + mdMethodDef *pmdGetter, // [OUT] getter method of the property + mdMethodDef rmdOtherMethod[], // [OUT] other method of the property + ULONG cMax, // [IN] size of rmdOtherMethod + ULONG *pcOtherMethod) // [OUT] total number of other method of this property +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd; + PropertyRec *pRec; + HENUMInternal hEnum; + + LOG((LOGMD, "MD RegMeta::GetPropertyProps(0x%08x, 0x%08x, %S, 0x%08x, 0x%08x, " + "0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, " + "0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, " + "0x%08x)\n", + prop, pClass, MDSTR(szProperty), cchProperty, pchProperty, + pdwPropFlags, ppvSig, pbSig, pdwCPlusTypeFlag, ppDefaultValue, + pchDefaultValue, pmdSetter, pmdGetter, rmdOtherMethod, cMax, + pcOtherMethod)); + + + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(prop) == mdtProperty); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + HENUMInternal::ZeroEnum(&hEnum); + IfFailGo(pMiniMd->GetPropertyRecord(RidFromToken(prop), &pRec)); + + if ( pClass ) + { + // find the property map entry corresponding to this property + IfFailGo( pMiniMd->FindParentOfPropertyHelper( prop, pClass) ); + } + if ( pdwPropFlags ) + { + *pdwPropFlags = pMiniMd->getPropFlagsOfProperty(pRec); + } + if ( ppvSig || pbSig ) + { + // caller wants the signature + // + ULONG cbSig; + PCCOR_SIGNATURE pvSig; + IfFailGo(pMiniMd->getTypeOfProperty(pRec, &pvSig, &cbSig)); + if ( ppvSig ) + { + *ppvSig = pvSig; + } + if ( pbSig ) + { + *pbSig = cbSig; + } + } + if ( pdwCPlusTypeFlag || ppDefaultValue || pchDefaultValue) + { + // get the constant value + ULONG cbValue; + RID rid; + IfFailGo(pMiniMd->FindConstantHelper(prop, &rid)); + + if (pchDefaultValue) + *pchDefaultValue = 0; + + if (InvalidRid(rid)) + { + // There is no constant value associate with it + if (pdwCPlusTypeFlag) + *pdwCPlusTypeFlag = ELEMENT_TYPE_VOID; + + if ( ppDefaultValue ) + *ppDefaultValue = NULL; + } + else + { + ConstantRec *pConstantRec; + IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec)); + DWORD dwType; + + // get the type of constant value + dwType = pMiniMd->getTypeOfConstant(pConstantRec); + if ( pdwCPlusTypeFlag ) + *pdwCPlusTypeFlag = dwType; + + // get the value blob + if (ppDefaultValue != NULL) + { + IfFailGo(pMiniMd->getValueOfConstant(pConstantRec, (const BYTE **)ppDefaultValue, &cbValue)); + if (pchDefaultValue && dwType == ELEMENT_TYPE_STRING) + *pchDefaultValue = cbValue / sizeof(WCHAR); + } + } + } + { + MethodSemanticsRec *pSemantics; + RID ridCur; + ULONG cCurOtherMethod = 0; + ULONG ulSemantics; + mdMethodDef tkMethod; + + // initialize output parameters + if (pmdSetter) + *pmdSetter = mdMethodDefNil; + if (pmdGetter) + *pmdGetter = mdMethodDefNil; + + IfFailGo( pMiniMd->FindMethodSemanticsHelper(prop, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur)) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics)); + ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics); + tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef ); + switch (ulSemantics) + { + case msSetter: + if (pmdSetter) *pmdSetter = tkMethod; + break; + case msGetter: + if (pmdGetter) *pmdGetter = tkMethod; + break; + case msOther: + if (cCurOtherMethod < cMax) + rmdOtherMethod[cCurOtherMethod] = tkMethod; + cCurOtherMethod ++; + break; + default: + _ASSERTE(!"BadKind!"); + } + } + + // set the output parameter + if (pcOtherMethod) + *pcOtherMethod = cCurOtherMethod; + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szProperty || pchProperty) + { + IfFailGo( pMiniMd->getNameOfProperty(pRec, (LPWSTR) szProperty, cchProperty, pchProperty) ); + } + +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + STOP_MD_PERF(GetPropertyProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetPropertyProps() + + +//***************************************************************************** +// This routine gets the properties for the given Param token. +//***************************************************************************** +HRESULT RegMeta::GetParamProps( // S_OK or error. + mdParamDef pd, // [IN]The Parameter. + mdMethodDef *pmd, // [OUT] Parent Method token. + ULONG *pulSequence, // [OUT] Parameter sequence. + __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Put name here. + ULONG cchName, // [OUT] Size of name buffer. + ULONG *pchName, // [OUT] Put actual size of name here. + DWORD *pdwAttr, // [OUT] Put flags here. + DWORD *pdwCPlusTypeFlag, // [OUT] Flag for value type. selected ELEMENT_TYPE_*. + UVCP_CONSTANT *ppValue, // [OUT] Constant value. + ULONG *pchValue) // [OUT] size of constant value, string only, wide chars +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + ParamRec *pParamRec; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + LOG((LOGMD, "MD RegMeta::GetParamProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + pd, pmd, pulSequence, szName, cchName, pchName, pdwAttr, pdwCPlusTypeFlag, ppValue, pchValue)); + + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(pd) == mdtParamDef); + + IfFailGo(pMiniMd->GetParamRecord(RidFromToken(pd), &pParamRec)); + + if (pmd) + { + IfFailGo(pMiniMd->FindParentOfParamHelper(pd, pmd)); + _ASSERTE(TypeFromToken(*pmd) == mdtMethodDef); + } + if (pulSequence) + *pulSequence = pMiniMd->getSequenceOfParam(pParamRec); + if (pdwAttr) + { + *pdwAttr = pMiniMd->getFlagsOfParam(pParamRec); + } + if ( pdwCPlusTypeFlag || ppValue || pchValue) + { + // get the constant value + ULONG cbValue; + RID rid; + IfFailGo(pMiniMd->FindConstantHelper(pd, &rid)); + + if (pchValue) + *pchValue = 0; + + if (InvalidRid(rid)) + { + // There is no constant value associate with it + if (pdwCPlusTypeFlag) + *pdwCPlusTypeFlag = ELEMENT_TYPE_VOID; + + if ( ppValue ) + *ppValue = NULL; + } + else + { + ConstantRec *pConstantRec; + IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec)); + DWORD dwType; + + // get the type of constant value + dwType = pMiniMd->getTypeOfConstant(pConstantRec); + if ( pdwCPlusTypeFlag ) + *pdwCPlusTypeFlag = dwType; + + // get the value blob + if (ppValue != NULL) + { + IfFailGo(pMiniMd->getValueOfConstant(pConstantRec, (const BYTE **)ppValue, &cbValue)); + if (pchValue && dwType == ELEMENT_TYPE_STRING) + *pchValue = cbValue / sizeof(WCHAR); + } + } + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szName || pchName) + IfFailGo( pMiniMd->getNameOfParam(pParamRec, szName, cchName, pchName) ); + +ErrExit: + STOP_MD_PERF(GetParamProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetParamProps() + +//***************************************************************************** +// This routine gets the properties for the given GenericParam token. +//***************************************************************************** +HRESULT RegMeta::GetGenericParamProps( // S_OK or error. + mdGenericParam rd, // [IN] The type parameter + ULONG* pulSequence, // [OUT] Parameter sequence number + DWORD* pdwAttr, // [OUT] Type parameter flags (for future use) + mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef) + DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use) + __out_ecount_opt (cchName) LPWSTR szName, // [OUT] The name + ULONG cchName, // [IN] Size of name buffer + ULONG *pchName) // [OUT] Actual size of name +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + GenericParamRec *pGenericParamRec; + CMiniMdRW *pMiniMd = NULL; + RID ridRD = RidFromToken(rd); + + + LOG((LOGMD, "MD RegMeta::GetGenericParamProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + rd, pulSequence, pdwAttr, ptOwner, reserved, szName, cchName, pchName)); + + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + // See if this version of the metadata can do Generics + if (!pMiniMd->SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + + if((TypeFromToken(rd) == mdtGenericParam) && (ridRD != 0)) + { + IfFailGo(pMiniMd->GetGenericParamRecord(RidFromToken(rd), &pGenericParamRec)); + + if (pulSequence) + *pulSequence = pMiniMd->getNumberOfGenericParam(pGenericParamRec); + if (pdwAttr) + *pdwAttr = pMiniMd->getFlagsOfGenericParam(pGenericParamRec); + if (ptOwner) + *ptOwner = pMiniMd->getOwnerOfGenericParam(pGenericParamRec); + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (pchName || szName) + IfFailGo(pMiniMd->getNameOfGenericParam(pGenericParamRec, szName, cchName, pchName)); + } + else + hr = META_E_BAD_INPUT_PARAMETER; + +ErrExit: + STOP_MD_PERF(GetGenericParamProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetGenericParamProps() + +//***************************************************************************** +// This routine gets the properties for the given GenericParamConstraint token. +//***************************************************************************** +HRESULT RegMeta::GetGenericParamConstraintProps( // S_OK or error. + mdGenericParamConstraint rd, // [IN] The constraint token + mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained + mdToken *ptkConstraintType) // [OUT] TypeDef/Ref/Spec constraint +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + GenericParamConstraintRec *pGPCRec; + CMiniMdRW *pMiniMd = NULL; + RID ridRD = RidFromToken(rd); + + LOG((LOGMD, "MD RegMeta::GetGenericParamConstraintProps(0x%08x, 0x%08x, 0x%08x)\n", + rd, ptGenericParam, ptkConstraintType)); + + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + // See if this version of the metadata can do Generics + if (!pMiniMd->SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + + if((TypeFromToken(rd) == mdtGenericParamConstraint) && (ridRD != 0)) + { + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(ridRD, &pGPCRec)); + + if (ptGenericParam) + *ptGenericParam = TokenFromRid(pMiniMd->getOwnerOfGenericParamConstraint(pGPCRec),mdtGenericParam); + if (ptkConstraintType) + *ptkConstraintType = pMiniMd->getConstraintOfGenericParamConstraint(pGPCRec); + } + else + hr = META_E_BAD_INPUT_PARAMETER; + +ErrExit: + STOP_MD_PERF(GetGenericParamConstraintProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetGenericParamConstraintProps() + +//***************************************************************************** +// This routine gets the properties for the given MethodSpec token. +//***************************************************************************** +HRESULT RegMeta::GetMethodSpecProps( // S_OK or error. + mdMethodSpec mi, // [IN] The method instantiation + mdToken *tkParent, // [OUT] MethodDef or MemberRef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob) // [OUT] actual size of signature blob +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + MethodSpecRec *pMethodSpecRec; + CMiniMdRW *pMiniMd = NULL; + + LOG((LOGMD, "MD RegMeta::GetMethodSpecProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mi, tkParent, ppvSigBlob, pcbSigBlob)); + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + + // See if this version of the metadata can do Generics + if (!pMiniMd->SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + _ASSERTE(TypeFromToken(mi) == mdtMethodSpec && RidFromToken(mi)); + + IfFailGo(pMiniMd->GetMethodSpecRecord(RidFromToken(mi), &pMethodSpecRec)); + + if (tkParent) + *tkParent = pMiniMd->getMethodOfMethodSpec(pMethodSpecRec); + + if (ppvSigBlob || pcbSigBlob) + { + // caller wants signature information + PCCOR_SIGNATURE pvSigTmp; + ULONG cbSig; + IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pMethodSpecRec, &pvSigTmp, &cbSig)); + if ( ppvSigBlob ) + *ppvSigBlob = pvSigTmp; + if ( pcbSigBlob) + *pcbSigBlob = cbSig; + } + + +ErrExit: + + STOP_MD_PERF(GetMethodSpecProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetMethodSpecProps() + +//***************************************************************************** +// This routine gets the type and machine of the PE file the scope is opened on. +//***************************************************************************** +HRESULT RegMeta::GetPEKind( // S_OK or error. + DWORD *pdwPEKind, // [OUT] The kind of PE (0 - not a PE) + DWORD *pdwMachine) // [OUT] Machine as defined in NT header +{ + HRESULT hr = NOERROR; + MAPPINGTYPE mt = MTYPE_NOMAPPING; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "MD RegMeta::GetPEKind(0x%08x, 0x%08x)\n",pdwPEKind,pdwMachine)); + + START_MD_PERF(); + LOCKREAD(); + + + if (m_pStgdb->m_pStgIO != NULL) + mt = m_pStgdb->m_pStgIO->GetMemoryMappedType(); + + hr = m_pStgdb->GetPEKind(mt, pdwPEKind, pdwMachine); + + ErrExit: + + STOP_MD_PERF(GetPEKind); + END_ENTRYPOINT_NOTHROW; + return hr; +} // HRESULT RegMeta::GetPEKind() + +//***************************************************************************** +// This function gets the "built for" version of a metadata scope. +// NOTE: if the scope has never been saved, it will not have a built-for +// version, and an empty string will be returned. +//***************************************************************************** +HRESULT RegMeta::GetVersionString( // S_OK or error. + __out_ecount_opt (cchBufSize) LPWSTR pwzBuf, // [OUT] Put version string here. + DWORD cchBufSize, // [in] size of the buffer, in wide chars + DWORD *pchBufSize) // [out] Size of the version string, wide chars, including terminating nul. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + DWORD cch; // Length of WideChar string. + LPCSTR pVer; // Pointer to version string. + + LOG((LOGMD, "MD RegMeta::GetVersionString(0x%08x, 0x%08x, 0x%08x)\n",pwzBuf,cchBufSize,pchBufSize)); + + START_MD_PERF(); + LOCKREAD(); + + if (m_pStgdb->m_pvMd != NULL) + { + // For convenience, get a pointer to the version string. + // @todo: get from alternate locations when there is no STOREAGESIGNATURE. + pVer = reinterpret_cast<const char*>(reinterpret_cast<const STORAGESIGNATURE*>(m_pStgdb->m_pvMd)->pVersion); + // Attempt to convert into caller's buffer. + cch = WszMultiByteToWideChar(CP_UTF8,0, pVer,-1, pwzBuf,cchBufSize); + // Did the string fit? + if (cch == 0) + { // No, didn't fit. Find out space required. + cch = WszMultiByteToWideChar(CP_UTF8,0, pVer,-1, pwzBuf,0); + // NUL terminate string. + if (cchBufSize > 0) + pwzBuf[cchBufSize-1] = W('\0'); + // Truncation return code. + hr = CLDB_S_TRUNCATION; + } + } + else + { // No string. + if (cchBufSize > 0) + *pwzBuf = W('\0'); + cch = 0; + } + + if (pchBufSize) + *pchBufSize = cch; + +ErrExit: + + STOP_MD_PERF(GetVersionString); + END_ENTRYPOINT_NOTHROW; + return hr; +} // HRESULT RegMeta::GetVersionString() + +//***************************************************************************** +// This routine gets the parent class for the nested class. +//***************************************************************************** +HRESULT RegMeta::GetNestedClassProps( // S_OK or error. + mdTypeDef tdNestedClass, // [IN] NestedClass token. + mdTypeDef *ptdEnclosingClass) // [OUT] EnclosingClass token. +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + NestedClassRec *pRecord; + ULONG iRecord; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + + LOG((LOGMD, "MD RegMeta::GetNestedClassProps(0x%08x, 0x%08x)\n", + tdNestedClass, ptdEnclosingClass)); + + START_MD_PERF(); + LOCKREAD(); + + // If not a typedef -- return error. + if (TypeFromToken(tdNestedClass) != mdtTypeDef) + { + IfFailGo(META_E_INVALID_TOKEN_TYPE); // PostError(META_E_INVALID_TOKEN_TYPE, tdNestedClass)); + } + + _ASSERTE(TypeFromToken(tdNestedClass) && !IsNilToken(tdNestedClass) && ptdEnclosingClass); + + IfFailGo(pMiniMd->FindNestedClassHelper(tdNestedClass, &iRecord)); + + if (InvalidRid(iRecord)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + + IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pRecord)); + + _ASSERTE(tdNestedClass == pMiniMd->getNestedClassOfNestedClass(pRecord)); + *ptdEnclosingClass = pMiniMd->getEnclosingClassOfNestedClass(pRecord); + +ErrExit: + STOP_MD_PERF(GetNestedClassProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetNestedClassProps() + +//***************************************************************************** +// Given a signature, parse it for custom modifier with calling convention. +//***************************************************************************** +HRESULT RegMeta::GetNativeCallConvFromSig( // S_OK or error. + void const *pvSig, // [IN] Pointer to signature. + ULONG cbSig, // [IN] Count of signature bytes. + ULONG *pCallConv) // [OUT] Put calling conv here (see CorPinvokemap). +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + PCCOR_SIGNATURE pvSigBlob = reinterpret_cast<PCCOR_SIGNATURE>(pvSig); + ULONG cbTotal = 0; // total of number bytes for return type + all fixed arguments + ULONG cbCur = 0; // index through the pvSigBlob + ULONG cb; + ULONG cArg; + ULONG cTyArg = 0; + ULONG callingconv; + ULONG cArgsIndex; + ULONG callConv = pmCallConvWinapi; // The calling convention. + + + + + *pCallConv = pmCallConvWinapi; + + // remember the number of bytes to represent the calling convention + cb = CorSigUncompressData (pvSigBlob, &callingconv); + if (cb == ((ULONG)(-1))) + { + hr = CORSEC_E_INVALID_IMAGE_FORMAT; + goto ErrExit; + } + cbCur += cb; + + // remember the number of bytes to represent the type parameter count + if (callingconv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + cb= CorSigUncompressData (&pvSigBlob[cbCur], &cTyArg); + if (cb == ((ULONG)(-1))) + { + hr = CORSEC_E_INVALID_IMAGE_FORMAT; + goto ErrExit; + } + cbCur += cb; + } + + + // remember number of bytes to represent the arg counts + cb= CorSigUncompressData (&pvSigBlob[cbCur], &cArg); + if (cb == ((ULONG)(-1))) + { + hr = CORSEC_E_INVALID_IMAGE_FORMAT; + goto ErrExit; + } + + cbCur += cb; + + // Look at the return type. + hr = _SearchOneArgForCallConv( &pvSigBlob[cbCur], &cb, &callConv); + if (hr == (HRESULT)-1) + { + *pCallConv = callConv; + hr = S_OK; + goto ErrExit; + } + IfFailGo(hr); + cbCur += cb; + cbTotal += cb; + + // loop through argument until we found ELEMENT_TYPE_SENTINEL or run + // out of arguments + for (cArgsIndex = 0; cArgsIndex < cArg; cArgsIndex++) + { + _ASSERTE(cbCur < cbSig); + hr = _SearchOneArgForCallConv( &pvSigBlob[cbCur], &cb, &callConv); + if (hr == (HRESULT)-1) + { + *pCallConv = callConv; + hr = S_OK; + goto ErrExit; + } + IfFailGo(hr); + cbTotal += cb; + cbCur += cb; + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::GetNativeCallConvFromSig() + +//***************************************************************************** +// Helper used by GetNativeCallingConvFromSig. +//***************************************************************************** +HRESULT RegMeta::_CheckCmodForCallConv( // S_OK, -1 if found, or error. + PCCOR_SIGNATURE pbSig, // [IN] Signature to check. + ULONG *pcbTotal, // [OUT] Put bytes consumed here. + ULONG *pCallConv) // [OUT] If found, put calling convention here. +{ + ULONG cbTotal = 0; // Bytes consumed. + mdToken tk; // Token for callconv. + HRESULT hr = NOERROR; // A result. + LPCUTF8 szName=0; // Callconv name. + LPCUTF8 szNamespace=0; // Callconv namespace. + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + _ASSERTE(pcbTotal); + + + + + // count the bytes for the token compression + cbTotal += CorSigUncompressToken(&pbSig[cbTotal], &tk); + + // workaround to skip nil tokens and TypeSpec tokens. + if (IsNilToken(tk) || TypeFromToken(tk) == mdtTypeSpec) + { + *pcbTotal = cbTotal; + goto ErrExit; + } + + // See if this token is a calling convention. + if (TypeFromToken(tk) == mdtTypeRef) + { + TypeRefRec *pTypeRefRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tk), &pTypeRefRec)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace)); + } + else + if (TypeFromToken(tk) == mdtTypeDef) + { + TypeDefRec *pTypeDefRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tk), &pTypeDefRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace)); + } + + if ((szNamespace && szName) && + (strcmp(szNamespace, CMOD_CALLCONV_NAMESPACE) == 0 || + strcmp(szNamespace, CMOD_CALLCONV_NAMESPACE_OLD) == 0) ) + { + // Set the hr to -1, which is an unspecified 'error'. This will percolate + // back up to the caller, where the 'error' should be recognized. + hr=(HRESULT)-1; + if (strcmp(szName, CMOD_CALLCONV_NAME_CDECL) == 0) + *pCallConv = pmCallConvCdecl; + else + if (strcmp(szName, CMOD_CALLCONV_NAME_STDCALL) == 0) + *pCallConv = pmCallConvStdcall; + else + if (strcmp(szName, CMOD_CALLCONV_NAME_THISCALL) == 0) + *pCallConv = pmCallConvThiscall; + else + if (strcmp(szName, CMOD_CALLCONV_NAME_FASTCALL) == 0) + *pCallConv = pmCallConvFastcall; + else + hr = S_OK; // keep looking + IfFailGo(hr); + } + *pcbTotal = cbTotal; + +ErrExit: + + return hr; +} // HRESULT RegMeta::_CheckCmodForCallConv() + +//***************************************************************************** +// Helper used by GetNativeCallingConvFromSig. +//***************************************************************************** +HRESULT RegMeta::_SearchOneArgForCallConv(// S_OK, -1 if found, or error. + PCCOR_SIGNATURE pbSig, // [IN] Signature to check. + ULONG *pcbTotal, // [OUT] Put bytes consumed here. + ULONG *pCallConv) // [OUT] If found, put calling convention here. +{ + ULONG cb; + ULONG cbTotal = 0; + CorElementType ulElementType; + ULONG ulData; + ULONG ulTemp; + int iData; + mdToken tk; + ULONG cArg; + ULONG callingconv; + ULONG cArgsIndex; + HRESULT hr = NOERROR; + + _ASSERTE(pcbTotal); + + cbTotal += CorSigUncompressElementType(&pbSig[cbTotal], &ulElementType); + while (CorIsModifierElementType(ulElementType) || ulElementType == ELEMENT_TYPE_SENTINEL) + { + cbTotal += CorSigUncompressElementType(&pbSig[cbTotal], &ulElementType); + } + switch (ulElementType) + { + case ELEMENT_TYPE_SZARRAY: + // skip over base type + IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) ); + cbTotal += cb; + break; + + case ELEMENT_TYPE_VAR : + case ELEMENT_TYPE_MVAR : + // skip over index + cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulData); + break; + + case ELEMENT_TYPE_GENERICINST : + // skip over generic type + IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) ); + cbTotal += cb; + + // skip over number of parameters + cbTotal += CorSigUncompressData(&pbSig[cbTotal], &cArg); + + // loop through type parameters + for (cArgsIndex = 0; cArgsIndex < cArg; cArgsIndex++) + { + IfFailGo( _SearchOneArgForCallConv( &pbSig[cbTotal], &cb, pCallConv) ); + cbTotal += cb; + } + + break; + + case ELEMENT_TYPE_FNPTR: + cbTotal += CorSigUncompressData (&pbSig[cbTotal], &callingconv); + + // remember number of bytes to represent the arg counts + cbTotal += CorSigUncompressData (&pbSig[cbTotal], &cArg); + + // how many bytes to represent the return type + IfFailGo( _SearchOneArgForCallConv( &pbSig[cbTotal], &cb, pCallConv) ); + cbTotal += cb; + + // loop through argument + for (cArgsIndex = 0; cArgsIndex < cArg; cArgsIndex++) + { + IfFailGo( _SearchOneArgForCallConv( &pbSig[cbTotal], &cb, pCallConv) ); + cbTotal += cb; + } + + break; + + case ELEMENT_TYPE_ARRAY: + // syntax : ARRAY BaseType <rank> [i size_1... size_i] [j lowerbound_1 ... lowerbound_j] + + // skip over base type + IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) ); + cbTotal += cb; + + // Parse for the rank + cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulData); + + // if rank == 0, we are done + if (ulData == 0) + break; + + // any size of dimension specified? + cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulData); + while (ulData--) + { + cbTotal += CorSigUncompressData(&pbSig[cbTotal], &ulTemp); + } + + // any lower bound specified? + cbTotal = CorSigUncompressData(&pbSig[cbTotal], &ulData); + + while (ulData--) + { + cbTotal += CorSigUncompressSignedInt(&pbSig[cbTotal], &iData); + } + + break; + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + // count the bytes for the token compression + cbTotal += CorSigUncompressToken(&pbSig[cbTotal], &tk); + break; + case ELEMENT_TYPE_CMOD_REQD: + case ELEMENT_TYPE_CMOD_OPT: + // Check for the calling convention. + IfFailGo(_CheckCmodForCallConv(&pbSig[cbTotal], &cb, pCallConv)); + cbTotal += cb; + // skip over base type + IfFailGo( _SearchOneArgForCallConv(&pbSig[cbTotal], &cb, pCallConv) ); + cbTotal += cb; + break; + default: + break; + } + *pcbTotal = cbTotal; + +ErrExit: + + return hr; +} // HRESULT RegMeta::_SearchOneArgForCallConv() diff --git a/src/coreclr/md/compiler/importhelper.cpp b/src/coreclr/md/compiler/importhelper.cpp new file mode 100644 index 00000000000..5b5907c8e03 --- /dev/null +++ b/src/coreclr/md/compiler/importhelper.cpp @@ -0,0 +1,3406 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// ImportHelper.cpp +// + +// +// contains utility code to MD directory +// +//***************************************************************************** +#include "stdafx.h" +#include "importhelper.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "strongnameinternal.h" +#include "sstring.h" + +#define COM_RUNTIME_LIBRARY "ComRuntimeLibrary" + + +//******************************************************************************* +// Find the MethodSpec by Method and Instantiation +//******************************************************************************* +//@GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary +HRESULT ImportHelper::FindMethodSpecByMethodAndInstantiation( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + /*mdMethodDefOrRef*/ mdToken tkMethod, // [IN] MethodSpec method field + PCCOR_SIGNATURE pInstantiation, // [IN] MethodSpec instantiation (a signature) + ULONG cbInstantiation, // [IN] Size of instantiation. + mdMethodSpec *pMethodSpec, // [OUT] Put the MethodSpec token here. + RID rid /* = 0*/) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + MethodSpecRec *pRecord; + /*mdMethodDefOrRef*/ mdToken tkMethodTmp; + PCCOR_SIGNATURE pInstantiationTmp; + ULONG cbInstantiationTmp; + ULONG cMethodSpecs; + ULONG i; + + _ASSERTE(pMethodSpec); + + cMethodSpecs = pMiniMd->getCountMethodSpecs(); + + // linear scan through the MethodSpec table + for (i=1; i <= cMethodSpecs; ++i) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetMethodSpecRecord(i, &pRecord)); + + tkMethodTmp = pMiniMd->getMethodOfMethodSpec(pRecord); + if ((tkMethodTmp != tkMethod)) + continue; + + //@GENERICS: not sure what is meant by duplicate here: identical sig pointers or sig pointer contents? + IfFailRet(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiationTmp, &cbInstantiationTmp)); + if (cbInstantiationTmp != cbInstantiation || memcmp(pInstantiation, pInstantiationTmp, cbInstantiation)) + continue; + + // Matching record found. + *pMethodSpec = TokenFromRid(i, mdtMethodSpec); + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindMethodSpecByMethodAndInstantiation() + + +//******************************************************************************* +// Find the GenericParam by owner and constraint +//******************************************************************************* +//@GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary +HRESULT ImportHelper::FindGenericParamConstraintByOwnerAndConstraint( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdGenericParam tkOwner, // [IN] GenericParamConstraint Owner + mdToken tkConstraint, // [IN] GenericParamConstraint Constraint + mdGenericParamConstraint *pGenericParamConstraint,// [OUT] Put the GenericParam token here. + RID rid /* = 0*/) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + GenericParamConstraintRec *pRecord; + mdGenericParam tkOwnerTmp; + mdToken tkConstraintTmp; + ULONG cGenericParamConstraints; + + ULONG i; + + _ASSERTE(pGenericParamConstraint); + + cGenericParamConstraints = pMiniMd->getCountGenericParamConstraints(); + + // linear scan through the GenericParam table + for (i=1; i <= cGenericParamConstraints; ++i) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetGenericParamConstraintRecord(i, &pRecord)); + + tkOwnerTmp = pMiniMd->getOwnerOfGenericParamConstraint(pRecord); + tkConstraintTmp = pMiniMd->getConstraintOfGenericParamConstraint(pRecord); + + if ((tkOwnerTmp != tkOwner) || (tkConstraintTmp != tkConstraint)) + continue; + + // Matching record found. + *pGenericParamConstraint = TokenFromRid(i, mdtGenericParamConstraint); + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindGenericParamConstraintByOwnerAndConstraint() + +//******************************************************************************* +// Find the GenericParam by owner and name or number +//******************************************************************************* +//<REVISIT_TODO> @GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary </REVISIT_TODO> +HRESULT ImportHelper::FindGenericParamByOwner( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkOwner, // [IN] GenericParam Owner + LPCUTF8 szUTF8Name, // [IN] GeneriParam Name, may be NULL if not used for search + ULONG *pNumber, // [IN] GeneriParam Number, may be NULL if not used for search + mdGenericParam *pGenericParam, // [OUT] Put the GenericParam token here. + RID rid /* = 0*/) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + GenericParamRec *pRecord; + mdToken tkOwnerTmp; + ULONG cGenericParams; + LPCUTF8 szCurName; + ULONG curNumber; + ULONG i; + + _ASSERTE(pGenericParam); + + cGenericParams = pMiniMd->getCountGenericParams(); + + // linear scan through the GenericParam table + for (i=1; i <= cGenericParams; ++i) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetGenericParamRecord(i, &pRecord)); + + tkOwnerTmp = pMiniMd->getOwnerOfGenericParam(pRecord); + if ( tkOwnerTmp != tkOwner) + continue; + + // if the name is significant, try to match it + if (szUTF8Name) + { + IfFailRet(pMiniMd->getNameOfGenericParam(pRecord, &szCurName)); + if (strcmp(szCurName, szUTF8Name)) + continue; + } + + // if the number is significant, try to match it + if (pNumber) + { curNumber = pMiniMd->getNumberOfGenericParam(pRecord); + if (*pNumber != curNumber) + continue; + } + + // Matching record found. + *pGenericParam = TokenFromRid(i, mdtGenericParam); + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindGenericParamByOwner() + +//******************************************************************************* +// Find a Method given a parent, name and signature. +//******************************************************************************* +HRESULT ImportHelper::FindMethod( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + mdTypeDef td, // [IN] parent. + LPCUTF8 szName, // [IN] MethodDef name. + PCCOR_SIGNATURE pSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdMethodDef * pmb, // [OUT] Put the MethodDef token here. + RID rid, // = 0 // [IN] Optional rid to be ignored. + PSIGCOMPARE pSignatureCompare, // = NULL // [IN] Optional Routine to compare signatures + void * pCompareContext) // = NULL // [IN] Optional context for the compare function +{ + HRESULT hr = S_OK; + ULONG ridStart; // Start of td's methods. + ULONG ridEnd; // End of td's methods. + ULONG index; // Loop control. + TypeDefRec *pRec; // A TypeDef Record. + MethodRec *pMethod; // A MethodDef Record. + LPCUTF8 szNameUtf8Tmp; // A found MethodDef's name. + PCCOR_SIGNATURE pSigTmp; // A found MethodDef's signature. + ULONG cbSigTmp; // Size of a found MethodDef's signature. + PCCOR_SIGNATURE pvSigTemp = pSig; // For use in parsing a signature. + CQuickBytes qbSig; // Struct to build a non-varargs signature. + CMiniMdRW::HashSearchResult rtn; + + if (cbSig) + { // check to see if this is a vararg signature + if (isCallConv(CorSigUncompressCallingConv(pvSigTemp), IMAGE_CEE_CS_CALLCONV_VARARG)) + { // Get the fix part of VARARG signature + IfFailGo(_GetFixedSigOfVarArg(pSig, cbSig, &qbSig, &cbSig)); + pSig = (PCCOR_SIGNATURE) qbSig.Ptr(); + } + } + + *pmb = TokenFromRid(rid, mdtMethodDef); // to know what to ignore + rtn = pMiniMd->FindMemberDefFromHash(td, szName, pSig, cbSig, pmb); + if (rtn == CMiniMdRW::Found) + { + goto ErrExit; + } + else if (rtn == CMiniMdRW::NotFound) + { + IfFailGo(CLDB_E_RECORD_NOTFOUND); + } + _ASSERTE(rtn == CMiniMdRW::NoTable); + + *pmb = mdMethodDefNil; + + // get the range of method rids given a typedef + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec)); + ridStart = pMiniMd->getMethodListOfTypeDef(pRec); + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(td), &ridEnd)); + // Iterate over the methods. + for (index = ridStart; index < ridEnd; index ++ ) + { + RID methodRID; + IfFailGo(pMiniMd->GetMethodRid(index, &methodRID)); + // For the call from Validator ignore the rid passed in. + if (methodRID != rid) + { + // Get the method and its name. + IfFailGo(pMiniMd->GetMethodRecord(methodRID, &pMethod)); + IfFailGo(pMiniMd->getNameOfMethod(pMethod, &szNameUtf8Tmp)); + + // If name matches what was requested... + if ( strcmp(szNameUtf8Tmp, szName) == 0 ) + { + if (cbSig && pSig) + { + IfFailGo(pMiniMd->getSignatureOfMethod(pMethod, &pSigTmp, &cbSigTmp)); + + // If the caller did not provide a custom compare routine + // then we use memcmp to match the signatures + // + if (pSignatureCompare == NULL) + { + if (cbSigTmp != cbSig || memcmp(pSig, pSigTmp, cbSig)) + continue; + } + else + { + // Call the custom compare routine + // + if (!pSignatureCompare(pSigTmp, cbSigTmp, pSig, cbSig, pCompareContext)) + continue; + } + } + // Ignore PrivateScope methods. + if (IsMdPrivateScope(pMiniMd->getFlagsOfMethod(pMethod))) + continue; + + // Found method. + *pmb = TokenFromRid(methodRID, mdtMethodDef); + goto ErrExit; + } + } + } + + // record not found + *pmb = mdMethodDefNil; + hr = CLDB_E_RECORD_NOTFOUND; + +ErrExit: + return hr; +} // ImportHelper::FindMethod + +//******************************************************************************* +// Find a Field given a parent, name and signature. +//******************************************************************************* +HRESULT ImportHelper::FindField( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + mdTypeDef td, // [IN] parent. + LPCUTF8 szName, // [IN] FieldDef name. + PCCOR_SIGNATURE pSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdFieldDef * pfd, // [OUT] Put the FieldDef token here. + RID rid) // = 0 // [IN] Optional rid to be ignored. +{ + HRESULT hr = S_OK; // A result. + ULONG ridStart; // Start of td's methods. + ULONG ridEnd; // End of td's methods. + ULONG index; // Loop control. + TypeDefRec *pRec; // A TypeDef Record. + FieldRec *pField; // A FieldDef Record. + LPCUTF8 szNameUtf8Tmp; // A found FieldDef's name. + PCCOR_SIGNATURE pSigTmp; // A found FieldDef's signature. + ULONG cbSigTmp; // Size of a found FieldDef's signature. + CMiniMdRW::HashSearchResult rtn; + + *pfd = TokenFromRid(rid,mdtFieldDef); // to know what to ignore + rtn = pMiniMd->FindMemberDefFromHash(td, szName, pSig, cbSig, pfd); + if (rtn == CMiniMdRW::Found) + { + goto ErrExit; + } + else if (rtn == CMiniMdRW::NotFound) + { + IfFailGo(CLDB_E_RECORD_NOTFOUND); + } + _ASSERTE(rtn == CMiniMdRW::NoTable); + + *pfd = mdFieldDefNil; + + // get the range of method rids given a typedef + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec)); + ridStart = pMiniMd->getFieldListOfTypeDef(pRec); + IfFailGo(pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd)); + + // Iterate over the methods. + for (index = ridStart; index < ridEnd; index ++ ) + { + RID fieldRID; + IfFailGo(pMiniMd->GetFieldRid(index, &fieldRID)); + // For the call from Validator ignore the rid passed in. + if (fieldRID != rid) + { + // Get the field and its name. + IfFailGo(pMiniMd->GetFieldRecord(fieldRID, &pField)); + IfFailGo(pMiniMd->getNameOfField(pField, &szNameUtf8Tmp)); + + // If name matches what was requested... + if ( strcmp(szNameUtf8Tmp, szName) == 0 ) + { + // Check signature if specified. + if (cbSig && pSig) + { + IfFailGo(pMiniMd->getSignatureOfField(pField, &pSigTmp, &cbSigTmp)); + if (cbSigTmp != cbSig || memcmp(pSig, pSigTmp, cbSig)) + continue; + } + // Ignore PrivateScope fields. + if (IsFdPrivateScope(pMiniMd->getFlagsOfField(pField))) + continue; + // Field found. + *pfd = TokenFromRid(fieldRID, mdtFieldDef); + goto ErrExit; + } + } + } + + // record not found + *pfd = mdFieldDefNil; + hr = CLDB_E_RECORD_NOTFOUND; + +ErrExit: + return hr; +} // ImportHelper::FindField + +//******************************************************************************* +// Find a Member given a parent, name and signature. +//******************************************************************************* +HRESULT ImportHelper::FindMember( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + mdTypeDef td, // [IN] parent. + LPCUTF8 szName, // [IN] Member name. + PCCOR_SIGNATURE pSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdToken * ptk) // [OUT] Put the token here. +{ + HRESULT hr; + + if (cbSig == 0) + { + Debug_ReportError("Invalid signature size 0."); + return CLDB_E_INDEX_NOTFOUND; + } + + // determine if it is ref to MethodDef or FieldDef + if ((pSig[0] & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_FIELD) + { + hr = FindMethod(pMiniMd, td, szName, pSig, cbSig, ptk); + } + else + { + hr = FindField(pMiniMd, td, szName, pSig, cbSig, ptk); + } + + if (hr == CLDB_E_RECORD_NOTFOUND) + *ptk = mdTokenNil; + + return hr; +} // ImportHelper::FindMember + + +//******************************************************************************* +// Find the memberref given name, sig, and parent +//******************************************************************************* +HRESULT ImportHelper::FindMemberRef( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + mdToken tkParent, // [IN] the parent token + LPCUTF8 szName, // [IN] memberref name + const COR_SIGNATURE * pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdMemberRef * pmr, // [OUT] Put the MemberRef token found + RID rid, // = 0 // [IN] Optional rid to be ignored. + HashSearchOption fCreateHash) // = DoNotCreateHash // [IN] Should we create hash first? (Optimize for multiple calls vs. single isolated call) +{ + ULONG cMemberRefRecs; + MemberRefRec * pMemberRefRec; + LPCUTF8 szNameTmp = 0; + const COR_SIGNATURE * pbSigTmp; // Signature. + ULONG cbSigTmp; // Size of signature. + mdToken tkParentTmp; // the parent token + HRESULT hr = NOERROR; + CMiniMdRW::HashSearchResult rtn; + + if ((szName == NULL) || (pmr == NULL)) + { + IfFailGo(CLDB_E_RECORD_NOTFOUND); + } + + if (fCreateHash == CreateHash) + { // Caller asked for creating hash to optimize for multiple calls + IfFailGo(pMiniMd->CreateMemberRefHash()); + } + + *pmr = TokenFromRid(rid, mdtMemberRef); // to know what to ignore + rtn = pMiniMd->FindMemberRefFromHash(tkParent, szName, pbSig, cbSig, pmr); + if (rtn == CMiniMdRW::Found) + { + goto ErrExit; + } + else if (rtn == CMiniMdRW::NotFound) + { + IfFailGo(CLDB_E_RECORD_NOTFOUND); + } + _ASSERTE(rtn == CMiniMdRW::NoTable); + + *pmr = mdMemberRefNil; + + cMemberRefRecs = pMiniMd->getCountMemberRefs(); + + // Search for the MemberRef + for (ULONG i = 1; i <= cMemberRefRecs; i++) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailGo(pMiniMd->GetMemberRefRecord(i, &pMemberRefRec)); + if (!IsNilToken(tkParent)) + { + // given a valid parent + tkParentTmp = pMiniMd->getClassOfMemberRef(pMemberRefRec); + if (tkParentTmp != tkParent) + { + // if parent is specified and not equal to the current row, + // try the next row. + continue; + } + } + if ((szName != NULL) && (*szName != 0)) + { + // name is specified + IfFailGo(pMiniMd->getNameOfMemberRef(pMemberRefRec, &szNameTmp)); + if (strcmp(szName, szNameTmp) != 0) + { + // Name is not equal. Try next row. + continue; + } + } + if ((cbSig != 0) && (pbSig != NULL)) + { + // signature is specifed + IfFailGo(pMiniMd->getSignatureOfMemberRef(pMemberRefRec, &pbSigTmp, &cbSigTmp)); + if (cbSigTmp != cbSig) + continue; + if (memcmp( pbSig, pbSigTmp, cbSig ) != 0) + continue; + } + + // we found a match + *pmr = TokenFromRid(i, mdtMemberRef); + return S_OK; + } + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; +} // ImportHelper::FindMemberRef + + + +//******************************************************************************* +// Find duplicate StandAloneSig +//******************************************************************************* +HRESULT ImportHelper::FindStandAloneSig( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + const COR_SIGNATURE *pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdSignature *psa) // [OUT] Put the StandAloneSig token found +{ + HRESULT hr; + ULONG cRecs; + StandAloneSigRec *pRec; + const COR_SIGNATURE *pbSigTmp; // Signature. + ULONG cbSigTmp; // Size of signature. + + + _ASSERTE(cbSig && psa); + *psa = mdSignatureNil; + + cRecs = pMiniMd->getCountStandAloneSigs(); + + // Search for the StandAloneSignature + for (ULONG i = 1; i <= cRecs; i++) + { + IfFailRet(pMiniMd->GetStandAloneSigRecord(i, &pRec)); + IfFailRet(pMiniMd->getSignatureOfStandAloneSig(pRec, &pbSigTmp, &cbSigTmp)); + if (cbSigTmp != cbSig) + continue; + if (memcmp( pbSig, pbSigTmp, cbSig ) != 0) + continue; + + // we found a match + *psa = TokenFromRid(i, mdtSignature); + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindStandAloneSig() + +//******************************************************************************* +// Find duplicate TypeSpec +//******************************************************************************* +HRESULT +ImportHelper::FindTypeSpec( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + const COR_SIGNATURE * pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdTypeSpec * pTypeSpec) // [OUT] Put the TypeSpec token found +{ + HRESULT hr; + ULONG cRecs; + TypeSpecRec * pRec; + const COR_SIGNATURE * pbSigTmp; // Signature. + ULONG cbSigTmp; // Size of signature. + + // cbSig can be 0 + _ASSERTE(pTypeSpec != NULL); + *pTypeSpec = mdSignatureNil; + + cRecs = pMiniMd->getCountTypeSpecs(); + + // Search for the TypeSpec + for (ULONG i = 1; i <= cRecs; i++) + { + IfFailRet(pMiniMd->GetTypeSpecRecord(i, &pRec)); + IfFailRet(pMiniMd->getSignatureOfTypeSpec(pRec, &pbSigTmp, &cbSigTmp)); + if (cbSigTmp != cbSig) + continue; + if (memcmp(pbSig, pbSigTmp, cbSig) != 0) + continue; + + // we found a match + *pTypeSpec = TokenFromRid(i, mdtTypeSpec); + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; +} // ImportHelper::FindTypeSpec + + +//******************************************************************************* +// Find the MethodImpl +//******************************************************************************* +HRESULT ImportHelper::FindMethodImpl( + CMiniMdRW *pMiniMd, // [IN] The MiniMd to lookup. + mdTypeDef tkClass, // [IN] The parent TypeDef token. + mdMethodDef tkBody, // [IN] Method body token. + mdMethodDef tkDecl, // [IN] Method declaration token. + RID *pRid) // [OUT] Put the MethodImpl rid here +{ + HRESULT hr; + MethodImplRec *pMethodImplRec; // MethodImpl record. + ULONG cMethodImplRecs; // Count of MethodImpl records. + mdTypeDef tkClassTmp; // Parent TypeDef token. + mdToken tkBodyTmp; // Method body token. + mdToken tkDeclTmp; // Method declaration token. + + _ASSERTE(TypeFromToken(tkClass) == mdtTypeDef); + _ASSERTE(TypeFromToken(tkBody) == mdtMemberRef || TypeFromToken(tkBody) == mdtMethodDef); + _ASSERTE(TypeFromToken(tkDecl) == mdtMemberRef || TypeFromToken(tkDecl) == mdtMethodDef); + _ASSERTE(!IsNilToken(tkClass) && !IsNilToken(tkBody) && !IsNilToken(tkDecl)); + + if (pRid) + *pRid = 0; + + cMethodImplRecs = pMiniMd->getCountMethodImpls(); + + // Search for the MethodImpl. + for (ULONG i = 1; i <= cMethodImplRecs; i++) + { + IfFailRet(pMiniMd->GetMethodImplRecord(i, &pMethodImplRec)); + + // match the parent column + tkClassTmp = pMiniMd->getClassOfMethodImpl(pMethodImplRec); + if (tkClassTmp != tkClass) + continue; + + // match the method body column + tkBodyTmp = pMiniMd->getMethodBodyOfMethodImpl(pMethodImplRec); + if (tkBodyTmp != tkBody) + continue; + + // match the method declaration column + tkDeclTmp = pMiniMd->getMethodDeclarationOfMethodImpl(pMethodImplRec); + if (tkDeclTmp != tkDecl) + continue; + + // we found a match + if (pRid) + *pRid = i; + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindMethodImpl() + +//******************************************************************************* +// Find the TypeRef given the fully qualified name and the assembly name +//******************************************************************************* +HRESULT ImportHelper::FindCustomAttributeCtorByName( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + LPCUTF8 szAssemblyName, // [IN] Assembly Name. + LPCUTF8 szNamespace, // [IN] TypeRef Namespace. + LPCUTF8 szName, // [IN] TypeRef Name. + mdTypeDef *ptk, // [OUT] Put the TypeRef token here. + RID rid /* = 0*/) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + ULONG cRecs; // Count of records. + AssemblyRefRec *pRec; // Current record being looked at. + LPCUTF8 szTmp; // Temp string. + mdTypeRef tkCAType; + + cRecs = pMiniMd->getCountAssemblyRefs(); + // Search for the AssemblyRef record. + for (ULONG i = 1; i <= cRecs; i++) + { + IfFailRet(pMiniMd->GetAssemblyRefRecord(i, &pRec)); + + IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szTmp)); + if (!strcmp(szTmp, szAssemblyName) && + (SUCCEEDED(FindTypeRefByName(pMiniMd, TokenFromRid(i, mdtAssemblyRef), szNamespace, szName, &tkCAType, rid))) && + (SUCCEEDED(FindMemberRef(pMiniMd, tkCAType, COR_CTOR_METHOD_NAME, NULL, 0 ,ptk)))) + { + return S_OK; + } + } + + return CLDB_E_RECORD_NOTFOUND; +} + +//******************************************************************************* +// Find the TypeRef given the fully qualified name. +//******************************************************************************* +HRESULT ImportHelper::FindTypeRefByName( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkResolutionScope, // [IN] Resolution scope for the TypeRef. + LPCUTF8 szNamespace, // [IN] TypeRef Namespace. + LPCUTF8 szName, // [IN] TypeRef Name. + mdTypeRef *ptk, // [OUT] Put the TypeRef token here. + RID rid /* = 0*/) // [IN] Optional rid to be ignored. +{ + HRESULT hr=S_OK; // A result. + ULONG cTypeRefRecs; // Count of TypeRefs to scan. + TypeRefRec *pTypeRefRec; // A TypeRef record. + LPCUTF8 szNameTmp; // A TypeRef's Name. + LPCUTF8 szNamespaceTmp; // A TypeRef's Namespace. + mdToken tkResTmp; // TypeRef's resolution scope. + ULONG i; // Loop control. + + _ASSERTE(szName && ptk); + *ptk = mdTypeRefNil; + + // Treat no namespace as empty string. + if (!szNamespace) + szNamespace = ""; + + if (pMiniMd->m_pNamedItemHash) + { + // If hash is build, go through the hash table + TOKENHASHENTRY *p; // Hash entry from chain. + ULONG iHash; // Item's hash value. + int pos; // Position in hash chain. + + // Hash the data. + iHash = pMiniMd->HashNamedItem(0, szName); + + // Go through every entry in the hash chain looking for ours. + for (p = pMiniMd->m_pNamedItemHash->FindFirst(iHash, pos); + p; + p = pMiniMd->m_pNamedItemHash->FindNext(pos)) + { + + // name hash can hold more than one kind of token + if (TypeFromToken(p->tok) != (ULONG)mdtTypeRef) + { + continue; + } + + // skip this one if asked + if (RidFromToken(p->tok) == rid) + continue; + + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(p->tok), &pTypeRefRec)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szNameTmp)); + if (strcmp(szName, szNameTmp) || strcmp(szNamespace, szNamespaceTmp)) + { + // if the name space is not equal, then check the next one. + continue; + } + tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); + + if (tkResTmp == tkResolutionScope || + (IsNilToken(tkResTmp) && IsNilToken(tkResolutionScope))) + { + // we found a match + *ptk = p->tok; + return S_OK; + } + } + hr = CLDB_E_RECORD_NOTFOUND; + } + else + { + cTypeRefRecs = pMiniMd->getCountTypeRefs(); + + // Search for the TypeRef. + for (i = 1; i <= cTypeRefRecs; i++) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailGo(pMiniMd->GetTypeRefRecord(i, &pTypeRefRec)); + + // See if the Resolution scopes match. + tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); + if (IsNilToken(tkResTmp)) + { + if (!IsNilToken(tkResolutionScope)) + continue; + } + else if (tkResTmp != tkResolutionScope) + continue; + + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp)); + if (strcmp(szNamespace, szNamespaceTmp)) + continue; + + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szNameTmp)); + if (! strcmp(szName, szNameTmp)) + { + *ptk = TokenFromRid(i, mdtTypeRef); + return S_OK; + } + } + hr = CLDB_E_RECORD_NOTFOUND; + } +ErrExit: + return hr; +} // HRESULT ImportHelper::FindTypeRefByName() + + +//******************************************************************************* +// Find the ModuleRef given the name, guid and mvid. +//******************************************************************************* +HRESULT ImportHelper::FindModuleRef( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + LPCUTF8 szUTF8Name, // [IN] ModuleRef name. + mdModuleRef *pmur, // [OUT] Put the ModuleRef token here. + RID rid /* = 0*/) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + ModuleRefRec *pModuleRef; + ULONG cModuleRefs; + LPCUTF8 szCurName; + ULONG i; + + _ASSERTE(pmur); + _ASSERTE(szUTF8Name); + + cModuleRefs = pMiniMd->getCountModuleRefs(); + + // linear scan through the ModuleRef table + for (i=1; i <= cModuleRefs; ++i) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetModuleRefRecord(i, &pModuleRef)); + + if (szUTF8Name != NULL) + { + IfFailRet(pMiniMd->getNameOfModuleRef(pModuleRef, &szCurName)); + if (strcmp(szCurName, szUTF8Name)) + continue; + } + // Matching record found. + *pmur = TokenFromRid(i, mdtModuleRef); + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindModuleRef() + + + +//******************************************************************************* +// Find the TypeDef given the type and namespace name +//******************************************************************************* +HRESULT +ImportHelper::FindTypeDefByName( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + LPCUTF8 szTypeDefNamespace, // [IN] Full qualified TypeRef name. + LPCUTF8 szTypeDefName, // [IN] Full qualified TypeRef name. + mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef/Module for Enclosing class. + mdTypeDef * ptkTypeDef, // [OUT] Put the TypeRef token here. + RID ridIgnore) // =0 // [IN] Optional rid to be ignored. +{ + ULONG cTypeDefRecs; + TypeDefRec * pTypeDefRec; + LPCUTF8 szName; + LPCUTF8 szNamespace; + DWORD dwFlags; + HRESULT hr = S_OK; + + _ASSERTE((szTypeDefName != NULL) && (ptkTypeDef != NULL)); + _ASSERTE((TypeFromToken(tkEnclosingClass) == mdtTypeDef) || + (TypeFromToken(tkEnclosingClass) == mdtTypeRef) || + (tkEnclosingClass == TokenFromRid(1, mdtModule)) || + IsNilToken(tkEnclosingClass)); + + *ptkTypeDef = mdTypeDefNil; + + cTypeDefRecs = pMiniMd->getCountTypeDefs(); + + // Treat no namespace as empty string. + if (szTypeDefNamespace == NULL) + szTypeDefNamespace = ""; + + if (tkEnclosingClass == TokenFromRid(1, mdtModule)) + { // Module scope is the same as no scope + tkEnclosingClass = mdTokenNil; + } + + // Get TypeDef of the tkEnclosingClass passed in + if (TypeFromToken(tkEnclosingClass) == mdtTypeRef) + { + // Resolve the TypeRef to a TypeDef + TypeRefRec * pTypeRefRec; + mdToken tkResolutionScope; + LPCUTF8 szTypeRefName; + LPCUTF8 szTypeRefNamespace; + + IfFailRet(pMiniMd->GetTypeRefRecord(RidFromToken(tkEnclosingClass), &pTypeRefRec)); + tkResolutionScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); + IfFailRet(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szTypeRefName)); + IfFailRet(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szTypeRefNamespace)); + + if (tkEnclosingClass == tkResolutionScope && !strcmp(szTypeDefName, szTypeRefName) && + ((szTypeDefNamespace == nullptr && szTypeRefNamespace == nullptr) || + (szTypeDefNamespace != nullptr && szTypeRefNamespace != nullptr && !strcmp(szTypeDefNamespace, szTypeRefNamespace)))) + { + // + // This defensive workaround works around a feature of DotFuscator that adds a bad type-ref + // which causes tools like ILDASM to crash. The type-ref's parent is set to itself + // which causes this function to recurse infinitely. A side-effect is that during Ngen we + // parse all the type-refs in an assembly and Ngen also hangs infinitely. + // This workaround is necessary because several popular gaming libraries experience hangs + // and we need binary compatibility in Apollo. + // + return CLDB_E_FILE_CORRUPT; + } + + // Update tkEnclosingClass to TypeDef + IfFailRet(FindTypeDefByName( + pMiniMd, + szTypeRefNamespace, + szTypeRefName, + (TypeFromToken(tkResolutionScope) == mdtTypeRef) ? tkResolutionScope : mdTokenNil, + &tkEnclosingClass)); + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef); + } + + // Search for the TypeDef + for (ULONG i = 1; i <= cTypeDefRecs; i++) + { + // For the call from Validator ignore the rid passed in. + if (i == ridIgnore) + continue; + + IfFailRet(pMiniMd->GetTypeDefRecord(i, &pTypeDefRec)); + + dwFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec); + + if (!IsTdNested(dwFlags) && !IsNilToken(tkEnclosingClass)) + { + // If the class is not Nested and EnclosingClass passed in is not nil + continue; + } + else if (IsTdNested(dwFlags) && IsNilToken(tkEnclosingClass)) + { + // If the class is nested and EnclosingClass passed is nil + continue; + } + else if (!IsNilToken(tkEnclosingClass)) + { + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef); + + RID iNestedClassRec; + NestedClassRec * pNestedClassRec; + mdTypeDef tkEnclosingClassTmp; + + IfFailRet(pMiniMd->FindNestedClassHelper(TokenFromRid(i, mdtTypeDef), &iNestedClassRec)); + if (InvalidRid(iNestedClassRec)) + continue; + IfFailRet(pMiniMd->GetNestedClassRecord(iNestedClassRec, &pNestedClassRec)); + tkEnclosingClassTmp = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec); + if (tkEnclosingClass != tkEnclosingClassTmp) + continue; + } + + IfFailRet(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName)); + if (strcmp(szTypeDefName, szName) == 0) + { + IfFailRet(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace)); + if (strcmp(szTypeDefNamespace, szNamespace) == 0) + { + *ptkTypeDef = TokenFromRid(i, mdtTypeDef); + return S_OK; + } + } + } + return CLDB_E_RECORD_NOTFOUND; +} // ImportHelper::FindTypeDefByName + +//******************************************************************************* +// Find the InterfaceImpl given the typedef and implemented interface +//******************************************************************************* +HRESULT ImportHelper::FindInterfaceImpl( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkClass, // [IN] TypeDef of the type + mdToken tkInterface, // [IN] could be typedef/typeref + mdInterfaceImpl *ptk, // [OUT] Put the interface token here. + RID rid /* = 0*/) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + ULONG ridStart, ridEnd; + ULONG i; + InterfaceImplRec *pInterfaceImplRec; + + _ASSERTE(ptk); + *ptk = mdInterfaceImplNil; + if ( pMiniMd->IsSorted(TBL_InterfaceImpl) ) + { + IfFailRet(pMiniMd->getInterfaceImplsForTypeDef(RidFromToken(tkClass), &ridEnd, &ridStart)); + } + else + { + ridStart = 1; + ridEnd = pMiniMd->getCountInterfaceImpls() + 1; + } + + // Search for the interfaceimpl + for (i = ridStart; i < ridEnd; i++) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetInterfaceImplRecord(i, &pInterfaceImplRec)); + if ( tkClass != pMiniMd->getClassOfInterfaceImpl(pInterfaceImplRec) ) + continue; + if ( tkInterface == pMiniMd->getInterfaceOfInterfaceImpl(pInterfaceImplRec) ) + { + *ptk = TokenFromRid(i, mdtInterfaceImpl); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindInterfaceImpl() + + + +//******************************************************************************* +// Find the Permission by parent and action +//******************************************************************************* +HRESULT ImportHelper::FindPermission( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkParent, // [IN] Token with the Permission + USHORT usAction, // [IN] The action of the permission + mdPermission *ppm) // [OUT] Put permission token here +{ + HRESULT hr; + DeclSecurityRec *pRec; + ULONG ridStart, ridEnd; + ULONG i; + mdToken tkParentTmp; + + _ASSERTE(ppm); + + if ( pMiniMd->IsSorted(TBL_DeclSecurity) ) + { + + IfFailRet(pMiniMd->getDeclSecurityForToken(tkParent, &ridEnd, &ridStart)); + } + else + { + ridStart = 1; + ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + } + // loop through all permission + for (i = ridStart; i < ridEnd; i++) + { + IfFailRet(pMiniMd->GetDeclSecurityRecord(i, &pRec)); + tkParentTmp = pMiniMd->getParentOfDeclSecurity(pRec); + if ( tkParentTmp != tkParent ) + continue; + if (pRec->GetAction() == usAction) + { + *ppm = TokenFromRid(i, mdtPermission); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindPermission() + + +//***************************************************************************** +// find a property record +//***************************************************************************** +HRESULT ImportHelper::FindProperty( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkTypeDef, // [IN] typedef token + LPCUTF8 szName, // [IN] name of the property + const COR_SIGNATURE *pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdProperty *ppr) // [OUT] Property token +{ + HRESULT hr; + RID ridPropertyMap; + PropertyMapRec *pPropertyMapRec; + PropertyRec *pRec; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + LPCUTF8 szNameTmp; + PCCOR_SIGNATURE pbSigTmp; + ULONG cbSigTmp; + ULONG pr; + + IfFailRet(pMiniMd->FindPropertyMapFor(RidFromToken(tkTypeDef), &ridPropertyMap)); + if ( !InvalidRid(ridPropertyMap) ) + { + IfFailRet(pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailRet(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + + for (i = ridStart; i < ridEnd; i++) + { + // get the property rid + IfFailRet(pMiniMd->GetPropertyRid(i, &pr)); + IfFailRet(pMiniMd->GetPropertyRecord(pr, &pRec)); + IfFailRet(pMiniMd->getNameOfProperty(pRec, &szNameTmp)); + IfFailRet(pMiniMd->getTypeOfProperty(pRec, &pbSigTmp, &cbSigTmp)); + if ( strcmp (szName, szNameTmp) != 0 ) + continue; + if ( cbSig != 0 && (cbSigTmp != cbSig || memcmp(pbSig, pbSigTmp, cbSig) != 0 ) ) + continue; + *ppr = TokenFromRid( i, mdtProperty ); + return S_OK; + } + return CLDB_E_RECORD_NOTFOUND; + } + else + { + return CLDB_E_RECORD_NOTFOUND; + } +} // HRESULT ImportHelper::FindProperty() + + + + +//***************************************************************************** +// find an Event record +//***************************************************************************** +HRESULT ImportHelper::FindEvent( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkTypeDef, // [IN] typedef token + LPCUTF8 szName, // [IN] name of the event + mdProperty *pev) // [OUT] Event token +{ + HRESULT hr; + RID ridEventMap; + EventMapRec *pEventMapRec; + EventRec *pRec; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + LPCUTF8 szNameTmp; + ULONG ev; + + IfFailRet(pMiniMd->FindEventMapFor(RidFromToken(tkTypeDef), &ridEventMap)); + if ( !InvalidRid(ridEventMap) ) + { + IfFailRet(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec)); + ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec); + IfFailRet(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd)); + + for (i = ridStart; i < ridEnd; i++) + { + // get the Event rid + IfFailRet(pMiniMd->GetEventRid(i, &ev)); + + // get the event row + IfFailRet(pMiniMd->GetEventRecord(ev, &pRec)); + IfFailRet(pMiniMd->getNameOfEvent(pRec, &szNameTmp)); + if ( strcmp (szName, szNameTmp) == 0) + { + *pev = TokenFromRid( ev, mdtEvent ); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; + } + else + { + return CLDB_E_RECORD_NOTFOUND; + } +} // HRESULT ImportHelper::FindEvent() + + + +//***************************************************************************** +// find an custom value record given by parent and type token. This will always return +// the first one that is found regardless duplicated. +//***************************************************************************** +HRESULT ImportHelper::FindCustomAttributeByToken( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkParent, // [IN] the parent that custom value is associated with + mdToken tkType, // [IN] type of the CustomAttribute + const void *pCustBlob, // [IN] custom attribute blob + ULONG cbCustBlob, // [IN] size of the blob. + mdCustomAttribute *pcv) // [OUT] CustomAttribute token +{ + HRESULT hr; + CustomAttributeRec *pRec; + ULONG ridStart, ridEnd; + ULONG i; + mdToken tkParentTmp; + mdToken tkTypeTmp; + const void *pCustBlobTmp; + ULONG cbCustBlobTmp; + + _ASSERTE(pcv); + *pcv = mdCustomAttributeNil; + if ( pMiniMd->IsSorted(TBL_CustomAttribute) ) + { + IfFailRet(pMiniMd->FindCustomAttributeFor( + RidFromToken(tkParent), + TypeFromToken(tkParent), + tkType, + (RID *)pcv)); + if (InvalidRid(*pcv)) + { + return S_FALSE; + } + else if (pCustBlob) + { + IfFailRet(pMiniMd->GetCustomAttributeRecord(RidFromToken(*pcv), &pRec)); + IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp)); + if (cbCustBlob == cbCustBlobTmp && + !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob)) + { + return S_OK; + } + } + else + { + return S_OK; + } + } + else + { + CLookUpHash *pHashTable = pMiniMd->m_pLookUpHashs[TBL_CustomAttribute]; + + if (pHashTable) + { + // table is not sorted but hash is built + // We want to create dynmaic array to hold the dynamic enumerator. + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + iHash = pMiniMd->HashCustomAttribute(tkParent); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailRet(pMiniMd->GetCustomAttributeRecord(RidFromToken(p->tok), &pRec)); + + tkParentTmp = pMiniMd->getParentOfCustomAttribute(pRec); + if (tkParentTmp != tkParent) + continue; + + tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pRec); + if (tkType != tkTypeTmp) + continue; + if (pCustBlob != NULL) + { + IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp)); + if (cbCustBlob == cbCustBlobTmp && + !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob)) + { + *pcv = TokenFromRid(p->tok, mdtCustomAttribute); + return S_OK; + } + } + else + return S_OK; + } + } + else + { + // linear scan + ridStart = 1; + ridEnd = pMiniMd->getCountCustomAttributes() + 1; + + // loop through all custom values + for (i = ridStart; i < ridEnd; i++) + { + IfFailRet(pMiniMd->GetCustomAttributeRecord(i, &pRec)); + + tkParentTmp = pMiniMd->getParentOfCustomAttribute(pRec); + if ( tkParentTmp != tkParent ) + continue; + + tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pRec); + if (tkType != tkTypeTmp) + continue; + + if (pCustBlob != NULL) + { + IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp)); + if (cbCustBlob == cbCustBlobTmp && + !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob)) + { + *pcv = TokenFromRid(i, mdtCustomAttribute); + return S_OK; + } + } + else + return S_OK; + } + } + // fall through + } + return S_FALSE; +} // ImportHelper::FindCustomAttributeByToken + +//***************************************************************************** +// Helper function to lookup and retrieve a CustomAttribute. +//***************************************************************************** +HRESULT ImportHelper::GetCustomAttributeByName( // S_OK or error. + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData) // [OUT] Put size of data here. +{ + return pMiniMd->CommonGetCustomAttributeByName(tkObj, szName, ppData, pcbData); +} // ImportHelper::GetCustomAttributeByName + +#ifdef FEATURE_METADATA_EMIT + +//******************************************************************************* +// Find an AssemblyRef record given the name. +//******************************************************************************* +HRESULT ImportHelper::FindAssemblyRef( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szName, // [IN] Name. + LPCUTF8 szLocale, // [IN] Locale. + const void *pbPublicKeyOrToken, // [IN] Public key or token (based on flags). + ULONG cbPublicKeyOrToken, // [IN] Byte count of public key or token. + USHORT usMajorVersion, // [IN] Major version. + USHORT usMinorVersion, // [IN] Minor version. + USHORT usBuildNumber, // [IN] Build number. + USHORT usRevisionNumber, // [IN] Revision number. + DWORD dwFlags, // [IN] Flags. + mdAssemblyRef *pmar) // [OUT] returned AssemblyRef token. +{ + HRESULT hr; + ULONG cRecs; // Count of records. + AssemblyRefRec *pRec; // Current record being looked at. + LPCUTF8 szTmp; // Temp string. + const void *pbTmp; // Temp blob. + ULONG cbTmp; // Temp byte count. + DWORD dwTmp; // Temp flags. + const void *pbToken = NULL; // Token version of public key. + ULONG cbToken = 0; // Count of bytes in token. +#if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE) + const void *pbTmpToken; // Token version of public key. + ULONG cbTmpToken; // Count of bytes in token. + bool fMatch; // Did public key or tokens match? +#endif // !FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE + + // Handle special cases upfront. + if (!szLocale) + szLocale = ""; + if (!pbPublicKeyOrToken) + cbPublicKeyOrToken = 0; + + if (!IsAfPublicKey(dwFlags)) + { + pbToken = pbPublicKeyOrToken; + cbToken = cbPublicKeyOrToken; + } + + _ASSERTE(pMiniMd && szName && pmar); + *pmar = 0; + + cRecs = pMiniMd->getCountAssemblyRefs(); + + // Search for the AssemblyRef record. + for (ULONG i = 1; i <= cRecs; i++) + { + IfFailRet(pMiniMd->GetAssemblyRefRecord(i, &pRec)); + + IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szTmp)); + if (strcmp(szTmp, szName)) + continue; + + IfFailRet(pMiniMd->getLocaleOfAssemblyRef(pRec, &szTmp)); + if (strcmp(szTmp, szLocale)) + continue; + + if (pRec->GetMajorVersion() != usMajorVersion) + continue; + if (pRec->GetMinorVersion() != usMinorVersion) + continue; + + // We'll "unify" all versions of mscorlib and Microsoft.VisualC... so if this + // is one of those, we won't do the version check beyond the major/minor + + LPCUTF8 szAssemblyRefName; + IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szAssemblyRefName)); + if (SString::_stricmp(szAssemblyRefName, "mscorlib") && + SString::_stricmp(szAssemblyRefName, "microsoft.visualc")) + { + if (pRec->GetBuildNumber() != usBuildNumber) + continue; + if (pRec->GetRevisionNumber() != usRevisionNumber) + continue; + } + + IfFailRet(pMiniMd->getPublicKeyOrTokenOfAssemblyRef(pRec, (const BYTE **)&pbTmp, &cbTmp)); + + if ((cbPublicKeyOrToken && !cbTmp) || + (!cbPublicKeyOrToken && cbTmp)) + continue; + + if (cbTmp) + { + // Either ref may be using either a full public key or a token + // (determined by the ref flags). Must cope with all variations. + dwTmp = pMiniMd->getFlagsOfAssemblyRef(pRec); + if (IsAfPublicKey(dwTmp) == IsAfPublicKey(dwFlags)) + { + // Easy case, they're both in the same form. + if (cbTmp != cbPublicKeyOrToken || memcmp(pbTmp, pbPublicKeyOrToken, cbTmp)) + continue; + } + else if (IsAfPublicKey(dwTmp)) + { +#if defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) && !defined(DACCESS_COMPILE) + return E_FAIL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE + // Need to compress target public key to see if it matches. + IfFailRet(StrongNameTokenFromPublicKey((BYTE*)pbTmp, + cbTmp, + (BYTE**)&pbTmpToken, + &cbTmpToken)); + fMatch = cbTmpToken == cbPublicKeyOrToken && !memcmp(pbTmpToken, pbPublicKeyOrToken, cbTmpToken); + StrongNameFreeBuffer((BYTE*)pbTmpToken); + if (!fMatch) + continue; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE + } + else + { + // Need to compress out public key to see if it matches. We + // cache the result of this for further iterations. + if (!pbToken) + { +#if defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) && !defined(DACCESS_COMPILE) + return E_FAIL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE + IfFailRet(StrongNameTokenFromPublicKey((BYTE*)pbPublicKeyOrToken, + cbPublicKeyOrToken, + (BYTE**)&pbToken, + &cbToken)); +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE + } + if (cbTmp != cbToken || memcmp(pbTmp, pbToken, cbToken)) + continue; + } + } + + if (pbToken && IsAfPublicKey(dwFlags)) + { +#if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE) + StrongNameFreeBuffer((BYTE*)pbToken); +#endif + } + *pmar = TokenFromRid(i, mdtAssemblyRef); + return S_OK; + } + if (pbToken && IsAfPublicKey(dwFlags)) + { +#if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE) + StrongNameFreeBuffer((BYTE*)pbToken); +#endif + } + return CLDB_E_RECORD_NOTFOUND; +} // ImportHelper::FindAssemblyRef + +//******************************************************************************* +// Find a File record given the name. +//******************************************************************************* +HRESULT ImportHelper::FindFile( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szName, // [IN] name for the File. + mdFile *pmf, // [OUT] returned File token. + RID rid /* = 0 */) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + ULONG cRecs; // Count of records. + FileRec *pRec; // Current record being looked at. + + LPCUTF8 szNameTmp; + + _ASSERTE(pMiniMd && szName && pmf); + *pmf = 0; + + cRecs = pMiniMd->getCountFiles(); + + // Search for the File record. + for (ULONG i = 1; i <= cRecs; i++) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetFileRecord(i, &pRec)); + + IfFailRet(pMiniMd->getNameOfFile(pRec, &szNameTmp)); + if (!strcmp(szNameTmp, szName)) + { + *pmf = TokenFromRid(i, mdtFile); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // ImportHelper::FindFile + +#endif //FEATURE_METADATA_EMIT + +//******************************************************************************* +// Find a ExportedType record given the name. +//******************************************************************************* +HRESULT ImportHelper::FindExportedType( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szNamespace, // [IN] namespace for the ExportedType. + LPCUTF8 szName, // [IN] name for the ExportedType. + mdExportedType tkEnclosingType, // [IN] token for the enclosing type. + mdExportedType *pmct, // [OUT] returned ExportedType token. + RID rid /* = 0 */) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + ULONG cRecs; // Count of records. + ExportedTypeRec *pRec; // Current record being looked at. + mdToken tkImpl; + LPCUTF8 szNamespaceTmp; + LPCUTF8 szNameTmp; + + _ASSERTE(pMiniMd && szName && pmct); + *pmct = 0; + + // Treat no namespace as empty string. + if (!szNamespace) + szNamespace = ""; + + cRecs = pMiniMd->getCountExportedTypes(); + + // Search for the ExportedType record. + for (ULONG i = 1; i <= cRecs; i++) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetExportedTypeRecord(i, &pRec)); + + // Handle the case of nested vs. non-nested classes. + tkImpl = pMiniMd->getImplementationOfExportedType(pRec); + if (TypeFromToken(tkImpl) == mdtExportedType && !IsNilToken(tkImpl)) + { + // Current ExportedType being looked at is a nested type, so + // comparing the implementation token. + if (tkImpl != tkEnclosingType) + continue; + } + else if (TypeFromToken(tkEnclosingType) == mdtExportedType && + !IsNilToken(tkEnclosingType)) + { + // ExportedType passed in is nested but the current ExportedType is not. + continue; + } + + IfFailRet(pMiniMd->getTypeNamespaceOfExportedType(pRec, &szNamespaceTmp)); + if (strcmp(szNamespaceTmp, szNamespace)) + continue; + + IfFailRet(pMiniMd->getTypeNameOfExportedType(pRec, &szNameTmp)); + if (!strcmp(szNameTmp, szName)) + { + *pmct = TokenFromRid(i, mdtExportedType); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindExportedType() + +//******************************************************************************* +// Find a ManifestResource record given the name. +//******************************************************************************* +HRESULT ImportHelper::FindManifestResource( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szName, // [IN] name for the ManifestResource. + mdManifestResource *pmmr, // [OUT] returned ManifestResource token. + RID rid /* = 0 */) // [IN] Optional rid to be ignored. +{ + HRESULT hr; + ULONG cRecs; // Count of records. + ManifestResourceRec *pRec; // Current record being looked at. + + LPCUTF8 szNameTmp; + + _ASSERTE(pMiniMd && szName && pmmr); + *pmmr = 0; + + cRecs = pMiniMd->getCountManifestResources(); + + // Search for the ManifestResource record. + for (ULONG i = 1; i <= cRecs; i++) + { + // For the call from Validator ignore the rid passed in. + if (i == rid) + continue; + + IfFailRet(pMiniMd->GetManifestResourceRecord(i, &pRec)); + + IfFailRet(pMiniMd->getNameOfManifestResource(pRec, &szNameTmp)); + if (!strcmp(szNameTmp, szName)) + { + *pmmr = TokenFromRid(i, mdtManifestResource); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT ImportHelper::FindManifestResource() + +#ifdef FEATURE_METADATA_EMIT + +//**************************************************************************** +// Convert tokens contained in an element type +//**************************************************************************** +HRESULT +ImportHelper::MergeUpdateTokenInFieldSig( + CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope. + CMiniMdRW *pMiniMdEmit, // [IN] The emit scope. + IMetaModelCommon *pCommonAssemImport,// [IN] Assembly scope where the signature is from. + const void *pbHashValue, // [IN] Hash value for the import assembly. + ULONG cbHashValue, // [IN] Size in bytes for the hash value. + IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope. + PCCOR_SIGNATURE pbSigImp, // signature from the imported scope + MDTOKENMAP *ptkMap, // Internal OID mapping structure. + CQuickBytes *pqkSigEmit, // [OUT] buffer for translated signature + ULONG cbStartEmit, // [IN] start point of buffer to write to + ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp + ULONG *pcbEmit) // [OUT] total number of bytes write to pqkSigEmit +{ + + HRESULT hr; // A result. + ULONG cb; // count of bytes + ULONG cb1; // count of bytes + ULONG cb2; // count of bytes + ULONG cbSubTotal; + ULONG cbImp; + ULONG cbEmit; + ULONG cbSrcTotal = 0; // count of bytes consumed in the imported signature + ULONG cbDestTotal = 0; // count of bytes for the new signature + ULONG ulElementType = 0; // place holder for expanded data + ULONG ulData; + ULONG ulTemp; + mdToken tkRidFrom; // Original rid + mdToken tkRidTo; // new rid + int iData; + CQuickArray<mdToken> cqaNesters; // Array of Nester tokens. + CQuickArray<LPCUTF8> cqaNesterNamespaces; // Array of Nester Namespaces. + CQuickArray<LPCUTF8> cqaNesterNames; // Array of Nester names. + + _ASSERTE(pcbEmit); + + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulElementType); + cbSrcTotal += cb; + + // count numbers of modifiers + while (CorIsModifierElementType((CorElementType) ulElementType)) + { + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulElementType); + cbSrcTotal += cb; + } + + // copy ELEMENT_TYPE_* over + cbDestTotal = cbSrcTotal; + IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal)); + memcpy(((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit, pbSigImp, cbDestTotal); + switch (ulElementType) + { + case ELEMENT_TYPE_SZARRAY: + // syntax : SZARRAY <BaseType> + + // conver the base type for the SZARRAY or GENERICARRAY + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // The assembly scope where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes for the hash value. + pCommonImport, // scope to merge into the emit scope. + &pbSigImp[cbSrcTotal], // from the imported scope + ptkMap, // OID mapping structure. + pqkSigEmit, // [OUT] buffer for translated signature + cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to + &cbImp, // [OUT] total number of bytes consumed from pbSigImp + &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + break; + + case ELEMENT_TYPE_GENERICINST: + { + // syntax : WITH (ELEMENT_TYPE_CLASS | ELEMENT_TYPE_VALUECLASS) <BaseType> + + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // The assembly scope where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes for the hash value. + pCommonImport, // scope to merge into the emit scope. + &pbSigImp[cbSrcTotal], // from the imported scope + ptkMap, // OID mapping structure. + pqkSigEmit, // [OUT] buffer for translated signature + cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to + &cbImp, // [OUT] total number of bytes consumed from pbSigImp + &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + + // copy over the number of arguments + ULONG nargs; + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &nargs); + + IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb)); + cb1 = CorSigCompressData(nargs, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal); + _ASSERTE(cb == cb1); + + cbSrcTotal += cb; + cbDestTotal += cb1; + + for (ULONG narg = 0; narg < nargs; narg++) { + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // The assembly scope where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes for the hash value. + pCommonImport, // The scope to merge into the emit scope. + &pbSigImp[cbSrcTotal], // signature from the imported scope + ptkMap, // Internal OID mapping structure. + pqkSigEmit, // [OUT] buffer for translated signature + cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to + &cbImp, // [OUT] total number of bytes consumed from pbSigImp + &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + } + } + + break; + + case ELEMENT_TYPE_MVAR: + case ELEMENT_TYPE_VAR: + // syntax : VAR <n> + // syntax : MVAR <n> + + // after the VAR or MVAR there is an integer indicating which type variable + // + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulData); + + IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb)); + cb1 = CorSigCompressData(ulData, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal); + _ASSERTE(cb == cb1); + + cbSrcTotal += cb; + cbDestTotal += cb1; + + break; + + case ELEMENT_TYPE_ARRAY: + // syntax : ARRAY BaseType <rank> [i size_1... size_i] [j lowerbound_1 ... lowerbound_j] + + // conver the base type for the MDARRAY + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // The assembly scope where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes for the hash value. + pCommonImport, // The scope to merge into the emit scope. + &pbSigImp[cbSrcTotal], // signature from the imported scope + ptkMap, // Internal OID mapping structure. + pqkSigEmit, // [OUT] buffer for translated signature + cbStartEmit + cbSrcTotal, // [IN] start point of buffer to write to + &cbImp, // [OUT] total number of bytes consumed from pbSigImp + &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + + // Parse for the rank + cbSubTotal = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulData); + + // if rank == 0, we are done + if (ulData != 0) + { + // any size of dimension specified? + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulData); + cbSubTotal += cb; + + while (ulData--) + { + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulTemp); + cbSubTotal += cb; + } + + // any lower bound specified? + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulData); + cbSubTotal += cb; + + while (ulData--) + { + cb = CorSigUncompressSignedInt(&pbSigImp[cbSrcTotal + cbSubTotal], &iData); + cbSubTotal += cb; + } + } + + // cbSubTotal is now the number of bytes still left to move over + // cbSrcTotal is where bytes start on the pbSigImp to be copied over + // cbStartEmit + cbDestTotal is where the destination of copy + + IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cbSubTotal)); + memcpy(((BYTE *)pqkSigEmit->Ptr())+cbStartEmit + cbDestTotal, &pbSigImp[cbSrcTotal], cbSubTotal); + + cbSrcTotal = cbSrcTotal + cbSubTotal; + cbDestTotal = cbDestTotal + cbSubTotal; + + break; + case ELEMENT_TYPE_FNPTR: + // function pointer is followed by another complete signature + IfFailGo(MergeUpdateTokenInSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // The assembly scope where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes for the hash value. + pCommonImport, // The scope to merge into the emit scope. + &pbSigImp[cbSrcTotal], // signature from the imported scope + ptkMap, // Internal OID mapping structure. + pqkSigEmit, // [OUT] buffer for translated signature + cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to + &cbImp, // [OUT] total number of bytes consumed from pbSigImp + &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + break; + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_CMOD_REQD: + case ELEMENT_TYPE_CMOD_OPT: + + // syntax for CLASS = ELEMENT_TYPE_CLASS <rid> + // syntax for VALUE_CLASS = ELEMENT_TYPE_VALUECLASS <rid> + + // now get the embedded typeref token + cb = CorSigUncompressToken(&pbSigImp[cbSrcTotal], &tkRidFrom); + + // Map the ulRidFrom to ulRidTo + if (ptkMap) + { + // mdtBaseType does not record in the map. It is unique across modules + if ( TypeFromToken(tkRidFrom) == mdtBaseType ) + { + tkRidTo = tkRidFrom; + } + else + { + IfFailGo( ptkMap->Remap(tkRidFrom, &tkRidTo) ); + } + } + else + { + // If the token is a TypeDef or a TypeRef, get/create the + // ResolutionScope for the outermost TypeRef. + if (TypeFromToken(tkRidFrom) == mdtTypeDef) + { + IfFailGo(ImportTypeDef(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + pCommonImport, + tkRidFrom, + true, // Optimize to TypeDef if emit and import scopes are identical. + &tkRidTo)); + } + else if (TypeFromToken(tkRidFrom) == mdtTypeRef) + { + IfFailGo(ImportTypeRef(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + pCommonImport, + tkRidFrom, + &tkRidTo)); + } + else if ( TypeFromToken(tkRidFrom) == mdtTypeSpec ) + { + // copy over the TypeSpec + PCCOR_SIGNATURE pvTypeSpecSig; + ULONG cbTypeSpecSig; + CQuickBytes qkTypeSpecSigEmit; + ULONG cbTypeSpecEmit; + + IfFailGo(pCommonImport->CommonGetTypeSpecProps( + tkRidFrom, + &pvTypeSpecSig, + &cbTypeSpecSig)); + + // Translate the typespec signature before look up + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // The assembly scope where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes for the hash value. + pCommonImport, // The scope to merge into the emit scope. + pvTypeSpecSig, // signature from the imported scope + ptkMap, // Internal OID mapping structure. + &qkTypeSpecSigEmit, // [OUT] buffer for translated signature + 0, // start from first byte of TypeSpec signature + 0, // don't care how many bytes are consumed + &cbTypeSpecEmit) ); // [OUT] total number of bytes write to pqkSigEmit + + hr = FindTypeSpec(pMiniMdEmit, + (PCCOR_SIGNATURE) (qkTypeSpecSigEmit.Ptr()), + cbTypeSpecEmit, + &tkRidTo); + + if ( hr == CLDB_E_RECORD_NOTFOUND ) + { + // Create TypeSpec record. + TypeSpecRec *pRecEmit; + + IfFailGo(pMiniMdEmit->AddTypeSpecRecord(&pRecEmit, (RID *)&tkRidTo)); + + IfFailGo(pMiniMdEmit->PutBlob( + TBL_TypeSpec, + TypeSpecRec::COL_Signature, + pRecEmit, + (PCCOR_SIGNATURE) (qkTypeSpecSigEmit.Ptr()), + cbTypeSpecEmit)); + tkRidTo = TokenFromRid( tkRidTo, mdtTypeSpec ); + IfFailGo(pMiniMdEmit->UpdateENCLog(tkRidTo)); + } + IfFailGo( hr ); + } + else + { + _ASSERTE( TypeFromToken(tkRidFrom) == mdtBaseType ); + + // base type is unique across module + tkRidTo = tkRidFrom; + } + } + + // How many bytes the new rid will consume? + cb1 = CorSigCompressToken(tkRidTo, &ulData); + + // ensure buffer is big enough + IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb1)); + + // store the new token + cb2 = CorSigCompressToken( + tkRidTo, + (ULONG *)( ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal) ); + + // inconsistency on CorSigCompressToken and CorSigUncompressToken + _ASSERTE(cb1 == cb2); + + cbSrcTotal = cbSrcTotal + cb; + cbDestTotal = cbDestTotal + cb1; + + if ( ulElementType == ELEMENT_TYPE_CMOD_REQD || + ulElementType == ELEMENT_TYPE_CMOD_OPT) + { + // need to skip over the base type + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // The assembly scope where the signature is from. + pbHashValue, // Hash value for the import assembly. + cbHashValue, // Size in bytes for the hash value. + pCommonImport, // The scope to merge into the emit scope. + &pbSigImp[cbSrcTotal], // signature from the imported scope + ptkMap, // Internal OID mapping structure. + pqkSigEmit, // [OUT] buffer for translated signature + cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to + &cbImp, // [OUT] total number of bytes consumed from pbSigImp + &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + } + + break; + default: + _ASSERTE(cbSrcTotal == cbDestTotal); + + if ((ulElementType >= ELEMENT_TYPE_MAX) || + (ulElementType == ELEMENT_TYPE_PTR) || + (ulElementType == ELEMENT_TYPE_BYREF) || + (ulElementType == ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED)) + { + IfFailGo(META_E_BAD_SIGNATURE); + } + break; + } + if (pcbImp) + *pcbImp = cbSrcTotal; + *pcbEmit = cbDestTotal; + +ErrExit: + return hr; +} // ImportHelper::MergeUpdateTokenInFieldSig + +#endif //FEATURE_METADATA_EMIT + +//**************************************************************************** +// convert tokens contained in a COM+ signature +//**************************************************************************** +HRESULT ImportHelper::MergeUpdateTokenInSig(// S_OK or error. + CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope. + CMiniMdRW *pMiniMdEmit, // [IN] The emit scope. + IMetaModelCommon *pCommonAssemImport,// [IN] Assembly scope where the signature is from. + const void *pbHashValue, // [IN] Hash value for the import assembly. + ULONG cbHashValue, // [IN] Size in bytes for the hash value. + IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope. + PCCOR_SIGNATURE pbSigImp, // signature from the imported scope + MDTOKENMAP *ptkMap, // Internal OID mapping structure. + CQuickBytes *pqkSigEmit, // [OUT] translated signature + ULONG cbStartEmit, // [IN] start point of buffer to write to + ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp + ULONG *pcbEmit) // [OUT] total number of bytes write to pqkSigEmit +{ +#ifdef FEATURE_METADATA_EMIT + HRESULT hr = NOERROR; // A result. + ULONG cb; // count of bytes + ULONG cb1; + ULONG cbSrcTotal = 0; // count of bytes consumed in the imported signature + ULONG cbDestTotal = 0; // count of bytes for the new signature + ULONG cbEmit; // count of bytes consumed in the imported signature + ULONG cbImp; // count of bytes for the new signature + ULONG cArg = 0; // count of arguments in the signature + ULONG cTyArg = 0; + ULONG callingconv = 0; // calling convention from signature + + _ASSERTE(pcbEmit && pqkSigEmit && pbSigImp); + + // calling convention + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &callingconv); + _ASSERTE((callingconv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX); + + // skip over calling convention + cbSrcTotal += cb; + + if (isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + // It is a FieldRef + cb1 = CorSigCompressData(callingconv, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit); + + // compression and uncompression better match + _ASSERTE(cb == cb1); + + cbDestTotal = cbSrcTotal = cb; + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + pCommonImport, + &pbSigImp[cbSrcTotal], + ptkMap, + pqkSigEmit, // output buffer to hold the new sig for the field + cbStartEmit + cbDestTotal, // number of bytes already in pqkSigDest + &cbImp, // number of bytes consumed from imported signature + &cbEmit)); // number of bytes write to the new signature + *pcbEmit = cbDestTotal + cbEmit; + } + else + { + + // It is a MethodRef + // count of type arguments + if (callingconv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &cTyArg); + cbSrcTotal += cb; + } + + // count of argument + cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &cArg); + cbSrcTotal += cb; + + // move over the calling convention and the count of arguments + IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbSrcTotal)); + memcpy(((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit, pbSigImp, cbSrcTotal); + cbDestTotal = cbSrcTotal; + + if ( !( isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) || isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_GENERICINST)) ) + { + // LocalVar sig does not have return type + // process the return type + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + pCommonImport, + &pbSigImp[cbSrcTotal], + ptkMap, + pqkSigEmit, // output buffer to hold the new sig for the field + cbStartEmit + cbDestTotal, // number of bytes already in pqkSigDest + &cbImp, // number of bytes consumed from imported signature + &cbEmit)); // number of bytes write to the new signature + + // advance the count + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + } + + + while (cArg) + { + // process every argument + IfFailGo(MergeUpdateTokenInFieldSig( + pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + pCommonImport, + &pbSigImp[cbSrcTotal], + ptkMap, + pqkSigEmit, // output buffer to hold the new sig for the field + cbStartEmit + cbDestTotal, + &cbImp, // number of bytes consumed from imported signature + &cbEmit)); // number of bytes write to the new signature + cbSrcTotal += cbImp; + cbDestTotal += cbEmit; + cArg--; + } + + // total of number of bytes consumed from imported signature + if (pcbImp) + *pcbImp = cbSrcTotal; + + // total number of bytes emitted by this function call to the emitting signature + *pcbEmit = cbDestTotal; + } + +ErrExit: + return hr; +#else //!FEATURE_METADATA_EMIT + // This code should be called only with public emit APIs + _ASSERTE_MSG(FALSE, "This method should not be reachable"); + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT +} // ImportHelper::MergeUpdateTokenInSig + +//**************************************************************************** +// Given a TypeDef or a TypeRef, return the Nesting hierarchy. The first +// element in the returned array always refers to the class token passed and +// the nesting hierarchy expands outwards from there. +//**************************************************************************** +HRESULT ImportHelper::GetNesterHierarchy( + IMetaModelCommon *pCommon, // Scope in which to find the hierarchy. + mdToken tk, // TypeDef/TypeRef whose hierarchy is needed. + CQuickArray<mdToken> &cqaNesters, // Array of Nesters. + CQuickArray<LPCUTF8> &cqaNamespaces, // Names of the nesters. + CQuickArray<LPCUTF8> &cqaNames) // Namespaces of the nesters. +{ + _ASSERTE(pCommon && + (TypeFromToken(tk) == mdtTypeDef || + TypeFromToken(tk) == mdtTypeRef) && + !IsNilToken(tk)); + + if (TypeFromToken(tk) == mdtTypeDef) + { + return GetTDNesterHierarchy(pCommon, + tk, + cqaNesters, + cqaNamespaces, + cqaNames); + } + else + { + return GetTRNesterHierarchy(pCommon, + tk, + cqaNesters, + cqaNamespaces, + cqaNames); + } +} // HRESULT ImportHelper::GetNesterHierarchy() + +//**************************************************************************** +// Get Nesting hierarchy given a TypeDef. +//**************************************************************************** +HRESULT ImportHelper::GetTDNesterHierarchy( + IMetaModelCommon *pCommon, // Scope in which to find the hierarchy. + mdTypeDef td, // TypeDef whose hierarchy is needed. + CQuickArray<mdTypeDef> &cqaTdNesters,// Array of Nesters. + CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters. + CQuickArray<LPCUTF8> &cqaNames) // Names of the nesters. +{ + LPCUTF8 szName, szNamespace; + DWORD dwFlags; + mdTypeDef tdNester; + ULONG ulNesters; + HRESULT hr = NOERROR; + + _ASSERTE(pCommon && + TypeFromToken(td) == mdtTypeDef && + !IsNilToken(td)); + + // Set current Nester index to 0. + ulNesters = 0; + // The first element in the hierarchy is the TypeDef itself. + tdNester = td; + // Bogus initialization to kick off the while loop. + dwFlags = tdNestedPublic; + // Loop as long as the TypeDef is a Nested TypeDef. + while (IsTdNested(dwFlags)) + { + if (InvalidRid(tdNester)) + IfFailGo(CLDB_E_RECORD_NOTFOUND); + // Get the name and namespace for the TypeDef. + IfFailGo(pCommon->CommonGetTypeDefProps( + tdNester, + &szNamespace, + &szName, + &dwFlags, + NULL, + NULL)); + + // Update the dynamic arrays. + ulNesters++; + + IfFailGo(cqaTdNesters.ReSizeNoThrow(ulNesters)); + cqaTdNesters[ulNesters-1] = tdNester; + + IfFailGo(cqaNamespaces.ReSizeNoThrow(ulNesters)); + cqaNamespaces[ulNesters-1] = szNamespace; + + IfFailGo(cqaNames.ReSizeNoThrow(ulNesters)); + cqaNames[ulNesters-1] = szName; + + IfFailGo(pCommon->CommonGetEnclosingClassOfTypeDef(tdNester, &tdNester)); + } + // Outermost class must have enclosing of Nil. + _ASSERTE(IsNilToken(tdNester)); +ErrExit: + return hr; +} // HRESULT ImportHelper::GetTDNesterHierarchy() + + +//**************************************************************************** +// Get Nesting hierarchy given a TypeRef. +//**************************************************************************** +HRESULT ImportHelper::GetTRNesterHierarchy( + IMetaModelCommon *pCommon, // [IN] Scope in which to find the hierarchy. + mdTypeRef tr, // [IN] TypeRef whose hierarchy is needed. + CQuickArray<mdTypeRef> &cqaTrNesters,// [OUT] Array of Nesters. + CQuickArray<LPCUTF8> &cqaNamespaces, // [OUT] Namespaces of the nesters. + CQuickArray<LPCUTF8> &cqaNames) // [OUT] Names of the nesters. +{ + LPCUTF8 szNamespace; + LPCUTF8 szName; + mdTypeRef trNester; + mdToken tkResolutionScope; + ULONG ulNesters; + HRESULT hr = S_OK; + + _ASSERTE(pCommon && + TypeFromToken(tr) == mdtTypeRef && + !IsNilToken(tr)); + + // Set current Nester index to 0. + ulNesters = 0; + // The first element in the hierarchy is the TypeRef itself. + trNester = tr; + // Loop as long as the TypeRef is a Nested TypeRef. + while (TypeFromToken(trNester) == mdtTypeRef && !IsNilToken(trNester)) + { + // Get the name and namespace for the TypeDef. + IfFailGo(pCommon->CommonGetTypeRefProps( + trNester, + &szNamespace, + &szName, + &tkResolutionScope)); + + // Update the dynamic arrays. + ulNesters++; + + IfFailGo(cqaTrNesters.ReSizeNoThrow(ulNesters)); + cqaTrNesters[ulNesters-1] = trNester; + + IfFailGo(cqaNamespaces.ReSizeNoThrow(ulNesters)); + cqaNamespaces[ulNesters-1] = szNamespace; + + IfFailGo(cqaNames.ReSizeNoThrow(ulNesters)); + cqaNames[ulNesters-1] = szName; + + trNester = tkResolutionScope; + } +ErrExit: + return hr; +} // HRESULT ImportHelper::GetTRNesterHierarchy() + +//**************************************************************************** +// Create the Nesting hierarchy given the array of TypeRef names. The first +// TypeRef in the array is the innermost TypeRef. +//**************************************************************************** +HRESULT ImportHelper::CreateNesterHierarchy( + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope to create the Nesters in. + CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Nester namespaces. + CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Nester names. + mdToken tkResolutionScope, // [IN] ResolutionScope for the innermost TypeRef. + mdTypeRef *ptr) // [OUT] Token for the innermost TypeRef. +{ + TypeRefRec *pRecEmit; + ULONG iRecord; + LPCUTF8 szName; + LPCUTF8 szNamespace; + mdTypeRef trNester; + mdTypeRef trCur; + ULONG ulNesters; + HRESULT hr = S_OK; + + _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() && + cqaNesterNames.Size()); + + // Initialize the output parameter. + *ptr = mdTypeRefNil; + + // Get count of Nesters in the hierarchy. + ulNesters = (ULONG)cqaNesterNames.Size(); + + // For each nester try to find the corresponding TypeRef in the emit scope. + // For the outermost TypeRef, ResolutionScope is what's passed in. + if (tkResolutionScope == mdTokenNil) + trNester = mdTypeRefNil; + else + trNester = tkResolutionScope; + ULONG ulCurNester; + for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--) + { + hr = FindTypeRefByName(pMiniMdEmit, + trNester, + cqaNesterNamespaces[ulCurNester], + cqaNesterNames[ulCurNester], + &trCur); + if (hr == CLDB_E_RECORD_NOTFOUND) + break; + else + IfFailGo(hr); + trNester = trCur; + } + if (SUCCEEDED(hr)) + *ptr = trNester; + else if ( hr == CLDB_E_RECORD_NOTFOUND ) + { + // Create TypeRef records for the part of the hierarchy for which + // TypeRefs are not already present. + for (;ulCurNester != (ULONG) -1; ulCurNester--) + { + szName = cqaNesterNames[ulCurNester]; + szNamespace = cqaNesterNamespaces[ulCurNester]; + + IfFailGo(pMiniMdEmit->AddTypeRefRecord(&pRecEmit, &iRecord)); + if (szNamespace && szNamespace[0] != '\0') + { + // only put the namespace if it is not an empty string and not NULL + IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Namespace, + pRecEmit, szNamespace)); + } + IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Name, + pRecEmit, szName)); + IfFailGo(pMiniMdEmit->PutToken(TBL_TypeRef, + TypeRefRec::COL_ResolutionScope, pRecEmit, trNester)); + + trNester = TokenFromRid(iRecord, mdtTypeRef); + IfFailGo(pMiniMdEmit->UpdateENCLog(trNester)); + + // Hash the name. + IfFailGo(pMiniMdEmit->AddNamedItemToHash(TBL_TypeRef, trNester, szName, 0)); + } + *ptr = trNester; + } + else + IfFailGo(hr); +ErrExit: + return hr; +} // ImportHelper::CreateNesterHierarchy + +//**************************************************************************** +// Given the arrays of names and namespaces for the Nested Type hierarchy, +// find the innermost TypeRef token. The arrays start with the innermost +// TypeRefs and go outwards. +//**************************************************************************** +HRESULT ImportHelper::FindNestedTypeRef( + CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef. + CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Names. + CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Namespaces. + mdToken tkResolutionScope, // [IN] Resolution scope for the outermost TypeRef. + mdTypeRef *ptr) // [OUT] Inner most TypeRef token. +{ + ULONG ulNesters; + ULONG ulCurNester; + HRESULT hr = S_OK; + + _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() && + cqaNesterNames.Size()); + + // Set the output parameter to Nil token. + *ptr = mdTokenNil; + + // Get count in the hierarchy, the give TypeDef included. + ulNesters = (ULONG)cqaNesterNames.Size(); + + // For each nester try to find the corresponding TypeRef in + // the emit scope. For the outermost TypeDef enclosing class is Nil. + for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--) + { + IfFailGo(FindTypeRefByName(pMiniMd, + tkResolutionScope, + cqaNesterNamespaces[ulCurNester], + cqaNesterNames[ulCurNester], + &tkResolutionScope)); + } + *ptr = tkResolutionScope; +ErrExit: + return hr; +} // HRESULT ImportHelper::FindNestedTypeRef() + + +//**************************************************************************** +// Given the arrays of names and namespaces for the Nested Type hierarchy, +// find the innermost TypeDef token. The arrays start with the innermost +// TypeDef and go outwards. +//**************************************************************************** +HRESULT ImportHelper::FindNestedTypeDef( + CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef. + CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Namespaces. + CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Names. + mdTypeDef tdNester, // [IN] Enclosing class for the Outermost TypeDef. + mdTypeDef *ptd) // [OUT] Inner most TypeRef token. +{ + ULONG ulNesters; + ULONG ulCurNester; + HRESULT hr = S_OK; + + _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() && + cqaNesterNames.Size()); + + // Set the output parameter to Nil token. + *ptd = mdTokenNil; + + // Get count in the hierarchy, the give TypeDef included. + ulNesters = (ULONG)cqaNesterNames.Size(); + + // For each nester try to find the corresponding TypeRef in + // the emit scope. For the outermost TypeDef enclosing class is Nil. + for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--) + { + IfFailGo(FindTypeDefByName(pMiniMd, + cqaNesterNamespaces[ulCurNester], + cqaNesterNames[ulCurNester], + tdNester, + &tdNester)); + } + *ptd = tdNester; +ErrExit: + return hr; +} // ImportHelper::FindNestedTypeDef + +#ifdef FEATURE_METADATA_EMIT + +//**************************************************************************** +// Given the TypeDef and the corresponding assembly and module import scopes, +// create a corresponding TypeRef in the given emit scope. +//**************************************************************************** +HRESULT +ImportHelper::ImportTypeDef( + CMiniMdRW * pMiniMdAssemEmit, // [IN] Assembly emit scope. + CMiniMdRW * pMiniMdEmit, // [IN] Module emit scope. + IMetaModelCommon * pCommonAssemImport, // [IN] Assembly import scope. + const void * pbHashValue, // [IN] Hash value for import assembly. + ULONG cbHashValue, // [IN] Size in bytes of hash value. + IMetaModelCommon * pCommonImport, // [IN] Module import scope. + mdTypeDef tdImport, // [IN] Imported TypeDef. + bool bReturnTd, // [IN] If the import and emit scopes are identical, return the TypeDef. + mdToken * ptkType) // [OUT] Output token for the imported type in the emit scope. +{ + CQuickArray<mdTypeDef> cqaNesters; + CQuickArray<LPCUTF8> cqaNesterNames; + CQuickArray<LPCUTF8> cqaNesterNamespaces; + GUID nullguid = GUID_NULL; + GUID MvidAssemImport = nullguid; + GUID MvidAssemEmit = nullguid; + GUID MvidImport = nullguid; + GUID MvidEmit = nullguid; + GUID GuidImport = GUID_NULL; + LPCUTF8 szModuleImport; + mdToken tkOuterRes = mdTokenNil; + HRESULT hr = S_OK; + BOOL bBCL = false; + + _ASSERTE(pMiniMdEmit && pCommonImport && ptkType); + _ASSERTE(TypeFromToken(tdImport) == mdtTypeDef && tdImport != mdTypeDefNil); + + // Get MVIDs for import and emit, assembly and module scopes. + if (pCommonAssemImport != NULL) + { + IfFailGo(pCommonAssemImport->CommonGetScopeProps(0, &MvidAssemImport)); + } + IfFailGo(pCommonImport->CommonGetScopeProps(&szModuleImport, &MvidImport)); + if (pMiniMdAssemEmit != NULL) + { + IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdAssemEmit)->CommonGetScopeProps(0, &MvidAssemEmit)); + } + IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdEmit)->CommonGetScopeProps(0, &MvidEmit)); + + if (pCommonAssemImport == NULL && strcmp(szModuleImport, COM_RUNTIME_LIBRARY) == 0) + { + const BYTE *pBlob; // Blob with dispid. + ULONG cbBlob; // Length of blob. + WCHAR wzBlob[40]; // Wide char format of guid. + int ix; // Loop control. + + hr = pCommonImport->CommonGetCustomAttributeByName(1, INTEROP_GUID_TYPE, (const void **)&pBlob, &cbBlob); + if (hr != S_FALSE) + { + // Should be in format. Total length == 41 + // <0x0001><0x24>01234567-0123-0123-0123-001122334455<0x0000> + if ((cbBlob == 41) || (GET_UNALIGNED_VAL16(pBlob) == 1)) + { + for (ix=1; ix<=36; ++ix) + wzBlob[ix] = pBlob[ix+2]; + wzBlob[0] = '{'; + wzBlob[37] = '}'; + wzBlob[38] = 0; + // It's ok that we ignore the hr here. It's not needed, but I + // don't want to remove it in case a code analysis tool will complain + // about not capturing return codes. + hr = IIDFromString(wzBlob, &GuidImport); + } + } + bBCL = (GuidImport == LIBID_ComPlusRuntime); + } + + // Compute the ResolutionScope for the imported type. + if (bBCL) + { + // This is the case that we are referring to mscorlib.dll but client does not provide the manifest for + // mscorlib.dll!! Do not generate ModuleRef to the mscorlib.dll. But instead we should just leave the + // ResolutionScope empty + tkOuterRes = mdTokenNil; + } + else if (MvidAssemImport == MvidAssemEmit && MvidImport == MvidEmit) + { + // The TypeDef is in the same Assembly and the Same scope. + if (bReturnTd) + { + *ptkType = tdImport; + goto ErrExit; + } + else + tkOuterRes = TokenFromRid(1, mdtModule); + } + else if (MvidAssemImport == MvidAssemEmit && MvidImport != MvidEmit) + { + // The TypeDef is in the same Assembly but a different module. + + // Create a ModuleRef corresponding to the import scope. + IfFailGo(CreateModuleRefFromScope(pMiniMdEmit, pCommonImport, &tkOuterRes)); + } + else if (MvidAssemImport != MvidAssemEmit) + { + if (pCommonAssemImport) + { + // The TypeDef is from a different Assembly. + + // Import and Emit scopes can't be identical and be from different + // Assemblies at the same time. + _ASSERTE(MvidImport != MvidEmit && + "Import scope can't be identical to the Emit scope and be from a different Assembly at the same time."); + + _ASSERTE(pCommonAssemImport); + + // Create an AssemblyRef corresponding to the import scope. + IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + &tkOuterRes)); + } + else + { + // <REVISIT_TODO>@FUTURE: review this fix! We may want to return error in the future. + // This is to enable smc to reference mscorlib.dll while it does not have the manifest for mscorlib.dll opened.</REVISIT_TODO> + // Create a Nil ResolutionScope to the TypeRef. + tkOuterRes = mdTokenNil; + } + } + + // Get the nesting hierarchy for the Type from the import scope and create + // the corresponding Type hierarchy in the emit scope. Note that the non- + // nested class case simply folds into this scheme. + + IfFailGo(GetNesterHierarchy(pCommonImport, + tdImport, + cqaNesters, + cqaNesterNamespaces, + cqaNesterNames)); + + IfFailGo(CreateNesterHierarchy(pMiniMdEmit, + cqaNesterNamespaces, + cqaNesterNames, + tkOuterRes, + ptkType)); +ErrExit: + return hr; +} // ImportHelper::ImportTypeDef + +//**************************************************************************** +// Given the TypeRef and the corresponding assembly and module import scopes, +// return the corresponding token in the given emit scope. +// <REVISIT_TODO>@FUTURE: Should we look at visibility flags on ExportedTypes and TypeDefs when +// handling references across Assemblies?</REVISIT_TODO> +//**************************************************************************** +HRESULT ImportHelper::ImportTypeRef( + CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope. + CMiniMdRW *pMiniMdEmit, // [IN] Module emit scope. + IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope. + const void *pbHashValue, // [IN] Hash value for import assembly. + ULONG cbHashValue, // [IN] Size in bytes of hash value. + IMetaModelCommon *pCommonImport, // [IN] Module import scope. + mdTypeRef trImport, // [IN] Imported TypeRef. + mdToken *ptkType) // [OUT] Output token for the imported type in the emit scope. +{ + CQuickArray<mdTypeDef> cqaNesters; + CQuickArray<LPCUTF8> cqaNesterNames; + CQuickArray<LPCUTF8> cqaNesterNamespaces; + LPCUTF8 szScopeNameEmit; + GUID nullguid = GUID_NULL; + GUID MvidAssemImport = nullguid; + GUID MvidAssemEmit = nullguid; + GUID MvidImport = nullguid; + GUID MvidEmit = nullguid; + mdToken tkOuterImportRes; // ResolutionScope for the outermost TypeRef in import scope. + mdToken tkOuterEmitRes = mdTokenNil; // ResolutionScope for outermost TypeRef in emit scope. + HRESULT hr = S_OK; + bool bAssemblyRefFromAssemScope = false; + + _ASSERTE(pMiniMdEmit && pCommonImport && ptkType); + _ASSERTE(TypeFromToken(trImport) == mdtTypeRef); + + // Get MVIDs for import and emit, assembly and module scopes. + if (pCommonAssemImport != NULL) + { + IfFailGo(pCommonAssemImport->CommonGetScopeProps(0, &MvidAssemImport)); + } + IfFailGo(pCommonImport->CommonGetScopeProps(0, &MvidImport)); + if (pMiniMdAssemEmit != NULL) + { + IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdAssemEmit)->CommonGetScopeProps( + 0, + &MvidAssemEmit)); + } + IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdEmit)->CommonGetScopeProps( + &szScopeNameEmit, + &MvidEmit)); + + // Get the outermost resolution scope for the TypeRef being imported. + IfFailGo(GetNesterHierarchy(pCommonImport, + trImport, + cqaNesters, + cqaNesterNamespaces, + cqaNesterNames)); + IfFailGo(pCommonImport->CommonGetTypeRefProps( + cqaNesters[cqaNesters.Size() - 1], + 0, + 0, + &tkOuterImportRes)); + + // Compute the ResolutionScope for the imported type. + if (MvidAssemImport == MvidAssemEmit && MvidImport == MvidEmit) + { + *ptkType = trImport; + goto ErrExit; + } + else if (MvidAssemImport == MvidAssemEmit && MvidImport != MvidEmit) + { + // The TypeRef is in the same Assembly but a different module. + + if (IsNilToken(tkOuterImportRes)) + { + tkOuterEmitRes = tkOuterImportRes; + } + else if (TypeFromToken(tkOuterImportRes) == mdtModule) + { + // TypeRef resolved to the import module in which its defined. + + // + if (pMiniMdAssemEmit == NULL && pCommonAssemImport == NULL) + { + tkOuterEmitRes = TokenFromRid(1, mdtModule); + } + else + { + // Create a ModuleRef corresponding to the import scope. + IfFailGo(CreateModuleRefFromScope(pMiniMdEmit, + pCommonImport, + &tkOuterEmitRes)); + } + } + else if (TypeFromToken(tkOuterImportRes) == mdtAssemblyRef) + { + // TypeRef is from a different Assembly. + + // Create a corresponding AssemblyRef in the emit scope. + IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonImport, + tkOuterImportRes, + &tkOuterEmitRes)); + } + else if (TypeFromToken(tkOuterImportRes) == mdtModuleRef) + { + // Get Name of the ModuleRef. + LPCUTF8 szMRName; + IfFailGo(pCommonImport->CommonGetModuleRefProps(tkOuterImportRes, &szMRName)); + + if (!strcmp(szMRName, szScopeNameEmit)) + { + // ModuleRef from import scope resolves to the emit scope. + tkOuterEmitRes = TokenFromRid(1, mdtModule); + } + else + { + // ModuleRef does not correspond to the emit scope. + // Create a corresponding ModuleRef. + IfFailGo(CreateModuleRefFromModuleRef(pMiniMdEmit, + pCommonImport, + tkOuterImportRes, + &tkOuterEmitRes)); + } + } + } + else if (MvidAssemImport != MvidAssemEmit) + { + // The TypeDef is from a different Assembly. + + // Import and Emit scopes can't be identical and be from different + // Assemblies at the same time. + _ASSERTE(MvidImport != MvidEmit && + "Import scope can't be identical to the Emit scope and be from a different Assembly at the same time."); + + mdToken tkImplementation; // Implementation token for ExportedType. + if (IsNilToken(tkOuterImportRes)) + { + // <REVISIT_TODO>BUG FIX:: URT 13626 + // Well, before all of the clients generate AR for mscorlib.dll reference, it is not true + // that tkOuterImportRes == nil will imply that we have to find such an entry in the import manifest!!</REVISIT_TODO> + + // Look for a ExportedType entry in the import Assembly. Its an error + // if we don't find a ExportedType entry. + mdExportedType tkExportedType; + hr = pCommonAssemImport->CommonFindExportedType( + cqaNesterNamespaces[cqaNesters.Size() - 1], + cqaNesterNames[cqaNesters.Size() - 1], + mdTokenNil, + &tkExportedType); + if (SUCCEEDED(hr)) + { + IfFailGo(pCommonAssemImport->CommonGetExportedTypeProps( + tkExportedType, + NULL, + NULL, + &tkImplementation)); + if (TypeFromToken(tkImplementation) == mdtFile) + { + // Type is from a different Assembly. + IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + &tkOuterEmitRes)); + } + else if (TypeFromToken(tkImplementation) == mdtAssemblyRef) + { + // This folds into the case where the Type is AssemblyRef. So + // let it fall through to that case. + + // Remember that this AssemblyRef token is actually from the Manifest scope not + // the module scope!!! + bAssemblyRefFromAssemScope = true; + tkOuterImportRes = tkImplementation; + } + else + _ASSERTE(!"Unexpected ExportedType implementation token."); + } + else + { + // In this case, we will just move over the TypeRef with Nil ResolutionScope. + hr = NOERROR; + tkOuterEmitRes = mdTokenNil; + } + } + else if (TypeFromToken(tkOuterImportRes) == mdtModule) + { + // Type is from a different Assembly. + IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + &tkOuterEmitRes)); + } + // Not else if, because mdtModule case above could change + // tkOuterImportRes to an AssemblyRef. + if (TypeFromToken(tkOuterImportRes) == mdtAssemblyRef) + { + // If there is an emit assembly, see if the import assembly ref points to + // it. If there is no emit assembly, the import assembly, by definition, + // does not point to this one. + if (pMiniMdAssemEmit == NULL || !pMiniMdAssemEmit->getCountAssemblys()) + hr = S_FALSE; + else + { + if (bAssemblyRefFromAssemScope) + { + // Check to see if the AssemblyRef resolves to the emit assembly. + IfFailGo(CompareAssemblyRefToAssembly(pCommonAssemImport, + tkOuterImportRes, + static_cast<IMetaModelCommon*>(pMiniMdAssemEmit))); + + } + else + { + // Check to see if the AssemblyRef resolves to the emit assembly. + IfFailGo(CompareAssemblyRefToAssembly(pCommonImport, + tkOuterImportRes, + static_cast<IMetaModelCommon*>(pMiniMdAssemEmit))); + } + } + if (hr == S_OK) + { + // The TypeRef being imported is defined in the current Assembly. + + // Find the ExportedType for the outermost TypeRef in the Emit assembly. + mdExportedType tkExportedType; + + hr = FindExportedType(pMiniMdAssemEmit, + cqaNesterNamespaces[cqaNesters.Size() - 1], + cqaNesterNames[cqaNesters.Size() - 1], + mdTokenNil, // Enclosing ExportedType. + &tkExportedType); + if (hr == S_OK) + { + // Create a ModuleRef based on the File name for the ExportedType. + // If the ModuleRef corresponds to pMiniMdEmit, the function + // will return S_FALSE, in which case set tkOuterEmitRes to + // the Module token. + hr = CreateModuleRefFromExportedType(pMiniMdAssemEmit, + pMiniMdEmit, + tkExportedType, + &tkOuterEmitRes); + if (hr == S_FALSE) + tkOuterEmitRes = TokenFromRid(1, mdtModule); + else + IfFailGo(hr); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + { + // Find the Type in the Assembly emit scope to cover the + // case where ExportedTypes may be implicitly defined. Its an + // error if we can't find the Type at this point. + IfFailGo(FindTypeDefByName(pMiniMdAssemEmit, + cqaNesterNamespaces[cqaNesters.Size() - 1], + cqaNesterNames[cqaNesters.Size() - 1], + mdTokenNil, // Enclosing Type. + &tkOuterEmitRes)); + tkOuterEmitRes = TokenFromRid(1, mdtModule); + } + else + { + _ASSERTE(FAILED(hr)); + IfFailGo(hr); + } + } + else if (hr == S_FALSE) + { + // The TypeRef being imported is from a different Assembly. + + if (bAssemblyRefFromAssemScope) + { + // Create a corresponding AssemblyRef. + IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + tkOuterImportRes, + &tkOuterEmitRes)); + } + else + { + // Create a corresponding AssemblyRef. + IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonImport, + tkOuterImportRes, + &tkOuterEmitRes)); + } + } + else + { + _ASSERTE(FAILED(hr)); + IfFailGo(hr); + } + } + else if (TypeFromToken(tkOuterImportRes) == mdtModuleRef) + { + // Type is from a different Assembly. + IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, + pMiniMdEmit, + pCommonAssemImport, + pbHashValue, + cbHashValue, + &tkOuterEmitRes)); + } + } + + // Try to find the TypeDef in the emit scope. If we cannot find the + // typedef, we need to introduce a typeref. + + // See if the Nested TypeDef is present in the Emit scope. + hr = CLDB_E_RECORD_NOTFOUND; + if (TypeFromToken(tkOuterEmitRes) == mdtModule && !IsNilToken(tkOuterEmitRes)) + { + hr = FindNestedTypeDef(pMiniMdEmit, + cqaNesterNamespaces, + cqaNesterNames, + mdTokenNil, + ptkType); + + // <REVISIT_TODO>cannot assert now!! Due to the IJW workaround! + // _ASSERTE(SUCCEEDED(hr));</REVISIT_TODO> + } + + if (hr == CLDB_E_RECORD_NOTFOUND) + { + IfFailGo(CreateNesterHierarchy(pMiniMdEmit, + cqaNesterNamespaces, + cqaNesterNames, + tkOuterEmitRes, + ptkType)); + } + else + IfFailGo(hr); +ErrExit: + return hr; +} // ImportHelper::ImportTypeRef + +//****************************************************************************** +// Given import scope, create a corresponding ModuleRef. +//****************************************************************************** +HRESULT ImportHelper::CreateModuleRefFromScope( // S_OK or error. + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope in which the ModuleRef is to be created. + IMetaModelCommon *pCommonImport, // [IN] Import scope. + mdModuleRef *ptkModuleRef) // [OUT] Output token for ModuleRef. +{ + HRESULT hr = S_OK; + LPCSTR szName; + ModuleRefRec *pRecordEmit; + RID iRecordEmit; + + // Set output to nil. + *ptkModuleRef = mdTokenNil; + + // Get name of import scope. + IfFailGo(pCommonImport->CommonGetScopeProps(&szName, 0)); + + // See if the ModuleRef exists in the Emit scope. + hr = FindModuleRef(pMiniMdEmit, szName, ptkModuleRef); + + if (hr == CLDB_E_RECORD_NOTFOUND) + { + if (szName[0] == '\0') + { + // It the referenced Module does not have a proper name, use the nil token instead. + LOG((LOGMD, "WARNING!!! MD ImportHelper::CreatemoduleRefFromScope but scope does not have a proper name!!!!")); + + // clear the error + hr = NOERROR; + + // It is a bug to create an ModuleRef to an empty name!!! + *ptkModuleRef = mdTokenNil; + } + else + { + // Create ModuleRef record and set the output parameter. + IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecordEmit, &iRecordEmit)); + *ptkModuleRef = TokenFromRid(iRecordEmit, mdtModuleRef); + IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef)); + + // It is a bug to create an ModuleRef to mscorlib.dll + _ASSERTE(strcmp(szName, COM_RUNTIME_LIBRARY) != 0); + + // Set the name of ModuleRef. + IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, + pRecordEmit, szName)); + } + } + else + IfFailGo(hr); +ErrExit: + return hr; +} // ImportHelper::CreateModuleRefFromScope + + +//****************************************************************************** +// Given an import scope and a ModuleRef, create a corresponding ModuleRef in +// the given emit scope. +//****************************************************************************** +HRESULT ImportHelper::CreateModuleRefFromModuleRef( // S_OK or error. + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope. + IMetaModelCommon *pCommon, // [IN] Import scope. + mdModuleRef tkModuleRef, // [IN] ModuleRef token. + mdModuleRef *ptkModuleRef) // [OUT] ModuleRef token in the emit scope. +{ + HRESULT hr = S_OK; + LPCSTR szName; + ModuleRefRec *pRecord; + RID iRecord; + + // Set output to Nil. + *ptkModuleRef = mdTokenNil; + + // Get name of the ModuleRef being imported. + IfFailGo(pCommon->CommonGetModuleRefProps(tkModuleRef, &szName)); + + // See if the ModuleRef exist in the Emit scope. + hr = FindModuleRef(pMiniMdEmit, szName, ptkModuleRef); + + if (hr == CLDB_E_RECORD_NOTFOUND) + { + // Create ModuleRef record and set the output parameter. + IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecord, &iRecord)); + *ptkModuleRef = TokenFromRid(iRecord, mdtModuleRef); + IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef)); + + // Set the name of ModuleRef. + IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, + pRecord, szName)); + } + else + { + IfFailGo(hr); + } +ErrExit: + return hr; +} // ImportHelper::CreateModuleRefFromModuleRef + + +//****************************************************************************** +// Given a ExportedType and the Assembly emit scope, create a corresponding ModuleRef +// in the give emit scope. The ExportedType being passed in must belong to the +// Assembly passed in. Function returns S_FALSE if the ExportedType is implemented +// by the emit scope passed in. +//****************************************************************************** +HRESULT ImportHelper::CreateModuleRefFromExportedType( // S_OK or error. + CMiniMdRW *pAssemEmit, // [IN] Import assembly scope. + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope. + mdExportedType tkExportedType, // [IN] ExportedType token in Assembly emit scope. + mdModuleRef *ptkModuleRef) // [OUT] ModuleRef token in the emit scope. +{ + mdFile tkFile; + LPCUTF8 szFile; + LPCUTF8 szScope; + FileRec *pFileRec; + HRESULT hr = S_OK; + + // Set output to nil. + *ptkModuleRef = mdTokenNil; + + // Get the implementation token for the ExportedType. It must be a File token + // since the caller should call this function only on ExportedTypes that resolve + // to the same Assembly. + IfFailGo(static_cast<IMetaModelCommon*>(pAssemEmit)->CommonGetExportedTypeProps( + tkExportedType, + NULL, + NULL, + &tkFile)); + _ASSERTE(TypeFromToken(tkFile) == mdtFile); + + // Get the name of the file. + IfFailGo(pAssemEmit->GetFileRecord(RidFromToken(tkFile), &pFileRec)); + IfFailGo(pAssemEmit->getNameOfFile(pFileRec, &szFile)); + + // Get the name of the emit scope. + IfFailGo(static_cast<IMetaModelCommon*>(pMiniMdEmit)->CommonGetScopeProps( + &szScope, + 0)); + + // If the file corresponds to the emit scope, return S_FALSE; + if (!strcmp(szFile, szScope)) + return S_FALSE; + + // See if a ModuleRef exists with this name. + hr = FindModuleRef(pMiniMdEmit, szFile, ptkModuleRef); + + if (hr == CLDB_E_RECORD_NOTFOUND) + { + // Create ModuleRef record and set the output parameter. + + ModuleRefRec *pRecord; + RID iRecord; + + IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecord, &iRecord)); + *ptkModuleRef = TokenFromRid(iRecord, mdtModuleRef); + IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef)); + + // Set the name of ModuleRef. + IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, + pRecord, szFile)); + } + else + IfFailGo(hr); +ErrExit: + return hr; +} // ImportHelper::CreateModuleRefFromExportedType + +//****************************************************************************** +// Given an AssemblyRef and the corresponding scope, create an AssemblyRef in +// the given Module scope and Assembly scope. +//****************************************************************************** +HRESULT ImportHelper::CreateAssemblyRefFromAssemblyRef( + CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope. + CMiniMdRW *pMiniMdModuleEmit, // [IN] Module emit scope + IMetaModelCommon *pCommonImport, // [IN] Scope to import the assembly ref from. + mdAssemblyRef tkAssemRef, // [IN] Assembly ref to be imported. + mdAssemblyRef *ptkAssemblyRef) // [OUT] AssemblyRef in the emit scope. +{ + AssemblyRefRec *pRecordEmit; + CMiniMdRW *rMiniMdRW[2]; + CMiniMdRW *pMiniMdEmit; + RID iRecordEmit; + USHORT usMajorVersion; + USHORT usMinorVersion; + USHORT usBuildNumber; + USHORT usRevisionNumber; + DWORD dwFlags; + const void *pbPublicKeyOrToken; + ULONG cbPublicKeyOrToken; + LPCUTF8 szName; + LPCUTF8 szLocale; + const void *pbHashValue; + ULONG cbHashValue; + HRESULT hr = S_OK; + + // Set output to Nil. + *ptkAssemblyRef = mdTokenNil; + + // Get import AssemblyRef props. + IfFailGo(pCommonImport->CommonGetAssemblyRefProps( + tkAssemRef, + &usMajorVersion, &usMinorVersion, &usBuildNumber, &usRevisionNumber, + &dwFlags, &pbPublicKeyOrToken, &cbPublicKeyOrToken, + &szName, &szLocale, + &pbHashValue, &cbHashValue)); + + // Create the AssemblyRef in both the Assembly and Module emit scopes. + rMiniMdRW[0] = pMiniMdAssemEmit; + rMiniMdRW[1] = pMiniMdModuleEmit; + + for (ULONG i = 0; i < 2; i++) + { + pMiniMdEmit = rMiniMdRW[i]; + + if (!pMiniMdEmit) + continue; + + // See if the AssemblyRef already exists in the emit scope. + hr = FindAssemblyRef(pMiniMdEmit, szName, szLocale, pbPublicKeyOrToken, + cbPublicKeyOrToken, usMajorVersion, usMinorVersion, + usBuildNumber, usRevisionNumber, dwFlags, &tkAssemRef); + if (hr == CLDB_E_RECORD_NOTFOUND) + { + // Create the AssemblyRef record and set the output parameter. + IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecordEmit, &iRecordEmit)); + tkAssemRef = TokenFromRid(iRecordEmit, mdtAssemblyRef); + IfFailGo(pMiniMdEmit->UpdateENCLog(tkAssemRef)); + + // Set parameters derived from the import Assembly. + pRecordEmit->SetMajorVersion(usMajorVersion); + pRecordEmit->SetMinorVersion(usMinorVersion); + pRecordEmit->SetBuildNumber(usBuildNumber); + pRecordEmit->SetRevisionNumber(usRevisionNumber); + pRecordEmit->SetFlags(dwFlags); + + IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken, + pRecordEmit, pbPublicKeyOrToken, cbPublicKeyOrToken)); + IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name, + pRecordEmit, szName)); + IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale, + pRecordEmit, szLocale)); + + // Set the parameters passed in for the AssemblyRef. + IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue, + pRecordEmit, pbHashValue, cbHashValue)); + } + else + IfFailGo(hr); + + // Set the output parameter for the AssemblyRef emitted in Module emit scope. + if (i) + *ptkAssemblyRef = tkAssemRef; + } +ErrExit: + return hr; +} // ImportHelper::CreateAssemblyRefFromAssemblyRef + +//****************************************************************************** +// Given the Assembly Import scope, hash value and execution location, create +// a corresponding AssemblyRef in the given assembly and module emit scope. +// Set the output parameter to the AssemblyRef token emitted in the module emit +// scope. +//****************************************************************************** +HRESULT +ImportHelper::CreateAssemblyRefFromAssembly( + CMiniMdRW * pMiniMdAssemEmit, // [IN] Emit assembly scope. + CMiniMdRW * pMiniMdModuleEmit, // [IN] Emit module scope. + IMetaModelCommon * pCommonAssemImport, // [IN] Assembly import scope. + const void * pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + mdAssemblyRef * ptkAssemblyRef) // [OUT] AssemblyRef token. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + AssemblyRefRec *pRecordEmit; + CMiniMdRW *rMiniMdRW[2]; + CMiniMdRW *pMiniMdEmit; + RID iRecordEmit; + USHORT usMajorVersion; + USHORT usMinorVersion; + USHORT usBuildNumber; + USHORT usRevisionNumber; + DWORD dwFlags; + const void *pbPublicKey; + ULONG cbPublicKey; + LPCUTF8 szName; + LPCUTF8 szLocale; + mdAssemblyRef tkAssemRef; + HRESULT hr = S_OK; + const void *pbToken = NULL; + ULONG cbToken = 0; + ULONG i; + + // Set output to Nil. + *ptkAssemblyRef = mdTokenNil; + + // Get the Assembly props. + IfFailGo(pCommonAssemImport->CommonGetAssemblyProps( + &usMajorVersion, &usMinorVersion, &usBuildNumber, &usRevisionNumber, + &dwFlags, &pbPublicKey, &cbPublicKey, + &szName, &szLocale)); + + // Compress the public key into a token. + if ((pbPublicKey != NULL) && (cbPublicKey != 0)) + { + _ASSERTE(IsAfPublicKey(dwFlags)); + dwFlags &= ~afPublicKey; + IfFailGo(StrongNameTokenFromPublicKey((BYTE*)pbPublicKey, + cbPublicKey, + (BYTE**)&pbToken, + &cbToken)); + } + else + _ASSERTE(!IsAfPublicKey(dwFlags)); + + // Create the AssemblyRef in both the Assembly and Module emit scopes. + rMiniMdRW[0] = pMiniMdAssemEmit; + rMiniMdRW[1] = pMiniMdModuleEmit; + + for (i = 0; i < 2; i++) + { + pMiniMdEmit = rMiniMdRW[i]; + + if (!pMiniMdEmit) + continue; + + // See if the AssemblyRef already exists in the emit scope. + hr = FindAssemblyRef(pMiniMdEmit, szName, szLocale, pbToken, + cbToken, usMajorVersion, usMinorVersion, + usBuildNumber, usRevisionNumber, dwFlags, + &tkAssemRef); + if (hr == CLDB_E_RECORD_NOTFOUND) + { + // Create the AssemblyRef record and set the output parameter. + IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecordEmit, &iRecordEmit)); + tkAssemRef = TokenFromRid(iRecordEmit, mdtAssemblyRef); + IfFailGo(pMiniMdEmit->UpdateENCLog(tkAssemRef)); + + // Set parameters derived from the import Assembly. + pRecordEmit->SetMajorVersion(usMajorVersion); + pRecordEmit->SetMinorVersion(usMinorVersion); + pRecordEmit->SetBuildNumber(usBuildNumber); + pRecordEmit->SetRevisionNumber(usRevisionNumber); + pRecordEmit->SetFlags(dwFlags); + + IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken, + pRecordEmit, pbToken, cbToken)); + IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name, + pRecordEmit, szName)); + IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale, + pRecordEmit, szLocale)); + + // Set the parameters passed in for the AssemblyRef. + IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue, + pRecordEmit, pbHashValue, cbHashValue)); + } + else + IfFailGo(hr); + + // Set the output parameter for the AssemblyRef emitted in Module emit scope. + if (i) + *ptkAssemblyRef = tkAssemRef; + } +ErrExit: + if (pbToken) + StrongNameFreeBuffer((BYTE*)pbToken); + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // ImportHelper::CreateAssemblyRefFromAssembly + +//****************************************************************************** +// Given an AssemblyRef and the corresponding scope, compare it to see if it +// refers to the given Assembly. +//****************************************************************************** +HRESULT ImportHelper::CompareAssemblyRefToAssembly( // S_OK, S_FALSE or error. + IMetaModelCommon *pCommonAssem1, // [IN] Scope that defines the AssemblyRef. + mdAssemblyRef tkAssemRef, // [IN] AssemblyRef. + IMetaModelCommon *pCommonAssem2) // [IN] Assembly against which the Ref is compared. +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + HRESULT hr; + + USHORT usMajorVersion1; + USHORT usMinorVersion1; + USHORT usBuildNumber1; + USHORT usRevisionNumber1; + const void *pbPublicKeyOrToken1; + ULONG cbPublicKeyOrToken1; + LPCUTF8 szName1; + LPCUTF8 szLocale1; + DWORD dwFlags1; + + USHORT usMajorVersion2; + USHORT usMinorVersion2; + USHORT usBuildNumber2; + USHORT usRevisionNumber2; + const void *pbPublicKey2; + ULONG cbPublicKey2; + LPCUTF8 szName2; + LPCUTF8 szLocale2; + const void *pbToken = NULL; + ULONG cbToken = 0; + bool fMatch; + + // Get the AssemblyRef props. + IfFailRet(pCommonAssem1->CommonGetAssemblyRefProps( + tkAssemRef, + &usMajorVersion1, &usMinorVersion1, &usBuildNumber1, &usRevisionNumber1, + &dwFlags1, &pbPublicKeyOrToken1, &cbPublicKeyOrToken1, + &szName1, &szLocale1, + NULL, NULL)); + // Get the Assembly props. + IfFailRet(pCommonAssem2->CommonGetAssemblyProps( + &usMajorVersion2, &usMinorVersion2, &usBuildNumber2, &usRevisionNumber2, + 0, &pbPublicKey2, &cbPublicKey2, + &szName2, &szLocale2)); + + // Compare. + if (usMajorVersion1 != usMajorVersion2 || + usMinorVersion1 != usMinorVersion2 || + usBuildNumber1 != usBuildNumber2 || + usRevisionNumber1 != usRevisionNumber2 || + strcmp(szName1, szName2) || + strcmp(szLocale1, szLocale2)) + { + return S_FALSE; + } + + // Defs always contain a full public key (or no key at all). Refs may have + // no key, a full public key or a tokenized key. + if ((cbPublicKeyOrToken1 && !cbPublicKey2) || + (!cbPublicKeyOrToken1 && cbPublicKey2)) + return S_FALSE; + + if (cbPublicKeyOrToken1) + { + // If ref contains a full public key we can just directly compare. + if (IsAfPublicKey(dwFlags1) && + (cbPublicKeyOrToken1 != cbPublicKey2 || + memcmp(pbPublicKeyOrToken1, pbPublicKey2, cbPublicKeyOrToken1))) + return S_FALSE; + + // Otherwise we need to compress the def public key into a token. + IfFailRet(StrongNameTokenFromPublicKey((BYTE*)pbPublicKey2, + cbPublicKey2, + (BYTE**)&pbToken, + &cbToken)); + + fMatch = cbPublicKeyOrToken1 == cbToken && + !memcmp(pbPublicKeyOrToken1, pbToken, cbPublicKeyOrToken1); + + StrongNameFreeBuffer((BYTE*)pbToken); + + if (!fMatch) + return S_FALSE; + } + + return S_OK; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // ImportHelper::CompareAssemblyRefToAssembly + +#endif //FEATURE_METADATA_EMIT diff --git a/src/coreclr/md/compiler/importhelper.h b/src/coreclr/md/compiler/importhelper.h new file mode 100644 index 00000000000..febcd7b5ae1 --- /dev/null +++ b/src/coreclr/md/compiler/importhelper.h @@ -0,0 +1,367 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// ImportHelper.h +// + +// +// contains utility code to MD directory +// +//***************************************************************************** +#ifndef __IMPORTHELPER__h__ +#define __IMPORTHELPER__h__ + +class CMiniMdRW; +class MDTOKENMAP; + +//********************************************************************* +// Class to handle merge +//********************************************************************* +class ImportHelper +{ +public: + // Options for code:FindMemberRef. + enum HashSearchOption + { + DoNotCreateHash, // Do not create hash if it does not exist (faster for isolated calls) + CreateHash // Create hash if it does not exist (faster for multiple calls) + }; + + + static HRESULT FindMethodSpecByMethodAndInstantiation( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + /*mdMethodDefOrRef*/ mdToken tkMethod, // [IN] MethodSpec method field + PCCOR_SIGNATURE pInstantiation, // [IN] MethodSpec instantiation (a signature) + ULONG cbInstantiation, // [IN] Size of instantiation. + mdMethodSpec *pMethodSpec, // [OUT] Put the MethodSpec token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + + static HRESULT FindGenericParamConstraintByOwnerAndConstraint( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdGenericParam tkOwner, // [IN] GenericParamConstraint Owner + mdToken tkConstraint, // [IN] GenericParamConstraint Constraint + mdGenericParamConstraint *pGenericParamConstraint, // [OUT] Put the GenericParamConstraint token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + + static HRESULT FindGenericParamByOwner( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkOwner, // [IN] GenericParam Owner + LPCUTF8 szUTF8Name, // [IN] GeneriParam Name, may be NULL if not used for search + ULONG *pNumber, // [IN] GeneriParam Number, may be NULL if not used for search + mdGenericParam *pGenericParam, // [OUT] Put the GenericParam token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindMethod( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + mdTypeDef td, // [IN] parent. + LPCUTF8 szName, // [IN] MethodDef name. + PCCOR_SIGNATURE pSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdMethodDef * pmb, // [OUT] Put the MethodDef token here. + RID rid = 0, // [IN] Optional rid to be ignored. + PSIGCOMPARE pSignatureCompare = NULL, // [IN] Optional Routine to compare signatures + void * pCompareContext = NULL); // [IN] Optional context for the compare function + + static HRESULT FindField( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + mdTypeDef td, // [IN] parent. + LPCUTF8 szName, // [IN] FieldDef name. + PCCOR_SIGNATURE pSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdFieldDef * pfd, // [OUT] Put the FieldDef token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindMember( + CMiniMdRW * pMiniMd, // [IN] the minimd to lookup + mdTypeDef td, // [IN] parent. + LPCUTF8 szName, // [IN] Member name. + PCCOR_SIGNATURE pSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdToken * ptk); // [OUT] Put the token here. + + static HRESULT FindMemberRef( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkParent, // [IN] the parent token + LPCUTF8 szName, // [IN] memberref name + const COR_SIGNATURE *pSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdMemberRef *pmr, // [OUT] Put the MemberRef token found + RID rid = 0, // [IN] Optional rid to be ignored. + HashSearchOption fCreateHash = DoNotCreateHash); // [IN] Should we create hash first? (Optimize for multiple calls vs. single isolated call) + + static HRESULT FindStandAloneSig( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + const COR_SIGNATURE *pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdSignature *psa); // [OUT] Put the StandAloneSig token found + + static HRESULT FindTypeSpec( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + const COR_SIGNATURE *pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdTypeSpec *ptypespec); // [OUT] Put the TypeSpec token found + + static HRESULT FindMethodImpl( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdTypeDef tkClass, // [IN] The parent TypeDef token. + mdToken tkBody, // [IN] Method body token. + mdToken tkDecl, // [IN] Method declaration token. + RID *pRid); // [OUT] Put the MethodImpl rid here + + static HRESULT FindCustomAttributeCtorByName( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + LPCUTF8 szAssemblyName, // [IN] Assembly Name. + LPCUTF8 szNamespace, // [IN] TypeRef Namespace. + LPCUTF8 szName, // [IN] TypeRef Name. + mdTypeDef *ptk, // [OUT] Put the TypeRef token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindTypeRefByName( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkResolutionScope, // [IN] ResolutionScope, mdAssemblyRef or mdModuleRef. + LPCUTF8 szNamespace, // [IN] TypeRef Namespace. + LPCUTF8 szName, // [IN] TypeRef Name. + mdTypeDef *ptk, // [OUT] Put the TypeRef token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindModuleRef( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + LPCUTF8 szUTF8Name, // [IN] ModuleRef name. + mdModuleRef *pmur, // [OUT] Put the ModuleRef token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindTypeDefByName( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + LPCUTF8 szNamespace, // [IN] Namespace of the TypeDef. + LPCUTF8 szName, // [IN] Name of the TypeDef. + mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef enclosing class. + mdTypeDef *ptk, // [OUT] Put the TypeDef token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindInterfaceImpl( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkClass, // [IN] TypeDef of the type + mdToken tkInterface, // [IN] could be typedef/typeref + mdInterfaceImpl *ptk, // [OUT] Put the interface token here. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindPermission( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkParent, // [IN] Token with the Permission + USHORT usAction, // [IN] The action of the permission + mdPermission *ppm); // [OUT] Put permission token here + + static HRESULT FindProperty( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkTypeDef, // [IN] typedef token + LPCUTF8 szName, // [IN] name of the property + const COR_SIGNATURE *pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size of signature. + mdProperty *ppr); // [OUT] Property token + + static HRESULT FindEvent( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkTypeDef, // [IN] typedef token + LPCUTF8 szName, // [IN] name of the event + mdProperty *pev); // [OUT] Event token + + static HRESULT FindCustomAttributeByToken( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkParent, // [IN] the parent that custom value is associated with + mdToken tkType, // [IN] type of the CustomAttribute + const void *pCustBlob, // [IN] custom value blob + ULONG cbCustBlob, // [IN] size of the blob. + mdCustomAttribute *pcv); // [OUT] CustomAttribute token + + static HRESULT GetCustomAttributeByName(// S_OK or error. + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData); // [OUT] Put size of data here. + + static HRESULT GetCustomAttributeByName(// S_OK or error. + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + mdCustomAttribute pca); // [OUT] found CA token + + static HRESULT MergeUpdateTokenInFieldSig( + CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope. + CMiniMdRW *pMiniMdEmit, // [IN] The emit scope. + IMetaModelCommon *pCommonAssemImport, // [IN] Assembly scope where the signature is from. + const void *pbHashValue, // [IN] Hash value for the import assembly. + ULONG cbHashValue, // [IN] Size in bytes for the hash value. + IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope. + PCCOR_SIGNATURE pbSigImp, // [IN] signature from the imported scope + MDTOKENMAP *ptkMap, // [IN] Internal OID mapping structure. + CQuickBytes *pqkSigEmit, // [OUT] buffer for translated signature + ULONG cbStartEmit, // [IN] start point of buffer to write to + ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp + ULONG *pcbEmit); // [OUT] total number of bytes write to pqkSigEmit + + static HRESULT MergeUpdateTokenInSig( // S_OK or error. + CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope. + CMiniMdRW *pMiniMdEmit, // [IN] The emit scope. + IMetaModelCommon *pCommonAssemImport, // [IN] Assembly scope where the signature is from. + const void *pbHashValue, // [IN] Hash value for the import assembly. + ULONG cbHashValue, // [IN] Size in bytes for the hash value. + IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope. + PCCOR_SIGNATURE pbSigImp, // [IN] signature from the imported scope + MDTOKENMAP *ptkMap, // [IN] Internal OID mapping structure. + CQuickBytes *pqkSigEmit, // [OUT] translated signature + ULONG cbStartEmit, // [IN] start point of buffer to write to + ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp + ULONG *pcbEmit); // [OUT] total number of bytes write to pqkSigEmit + + // This is implemented in a satellite lib because it is only used in emit and depends on + // strong name support in mscorwks.dll. + static HRESULT FindAssemblyRef( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szName, // [IN] Name. + LPCUTF8 szLocale, // [IN] Locale. + const void *pbPublicKeyOrToken, // [IN] Public key or token (based on flags). + ULONG cbPublicKeyOrToken, // [IN] Byte count of public key or token. + USHORT usMajorVersion, // [IN] Major version. + USHORT usMinorVersion, // [IN] Minor version. + USHORT usBuildNumber, // [IN] Build number. + USHORT usRevisionNumber, // [IN] Revision number. + DWORD dwFlags, // [IN] Flags. + mdAssemblyRef *pmar); // [OUT] returned AssemblyRef token. + + static HRESULT FindFile( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szName, // [IN] name for the File. + mdFile *pmf, // [OUT] returned File token. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindExportedType( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szNamespace, // [IN] namespace for the ExportedType. + LPCUTF8 szName, // [IN] name for the ExportedType. + mdExportedType tkEnclosingType, // [IN] enclosing ExportedType token. + mdExportedType *pmct, // [OUT] returned ExportedType token. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT FindManifestResource( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. + LPCUTF8 szName, // [IN] name for the ManifestResource. + mdManifestResource *pmmr, // [OUT] returned ManifestResource token. + RID rid = 0); // [IN] Optional rid to be ignored. + + static HRESULT GetNesterHierarchy( + IMetaModelCommon *pCommon, // Scope in which to find the hierarchy. + mdTypeDef td, // TypeDef whose hierarchy is needed. + CQuickArray<mdTypeDef> &cqaTdNesters, // Array of Nesters. + CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters. + CQuickArray<LPCUTF8> &cqaNames); // Names of the nesters. + + static HRESULT FindNestedTypeRef( + CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef. + CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Namespaces. + CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Names. + mdToken tkResolutionScope, // [IN] Resolution scope for the outermost TypeRef. + mdTypeRef *ptr); // [OUT] Inner most TypeRef token. + + static HRESULT FindNestedTypeDef( + CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef. + CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Namespaces. + CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Names. + mdTypeDef tdNester, // [IN] Enclosing class for the Outermost TypeDef. + mdTypeDef *ptd); // [OUT] Inner most TypeRef token. + + static HRESULT CreateNesterHierarchy( + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope to create the Nesters in. + CQuickArray<LPCUTF8> &cqaNesterNamespaces, // [IN] Array of Nester namespaces. + CQuickArray<LPCUTF8> &cqaNesterNames, // [IN] Array of Nester names. + mdToken tkResolutionScope, // [IN] ResolutionScope for the innermost TypeRef. + mdTypeRef *ptr); // [OUT] Token for the innermost TypeRef. + + static HRESULT ImportTypeDef( + CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope. + CMiniMdRW *pMiniMdEmit, // [IN] Module emit scope. + IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope. + const void *pbHashValue, // [IN] Hash value for import assembly. + ULONG cbHashValue, // [IN] Size in bytes of hash value. + IMetaModelCommon *pCommonImport, // [IN] Module import scope. + mdTypeDef tdImport, // [IN] Imported TypeDef. + bool bReturnTd, // [IN] If the import and emit scopes are identical, return the TypeDef. + mdToken *ptkType); // [OUT] Output token for the imported type in the emit scope. + + static HRESULT ImportTypeRef( + CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope. + CMiniMdRW *pMiniMdEmit, // [IN] Module emit scope. + IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope. + const void *pbHashValue, // [IN] Hash value for import assembly. + ULONG cbHashValue, // [IN] Size in bytes of hash value. + IMetaModelCommon *pCommonImport, // [IN] Module import scope. + mdTypeRef trImport, // [IN] Imported TypeRef. + mdToken *ptkType); // [OUT] Output token for the imported type in the emit scope. + +private: + /* + static bool ImportHelper::CompareCustomAttribute( // + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + ULONG rid); // [IN] the rid of the custom attribute to compare to + */ + + static HRESULT GetTDNesterHierarchy( + IMetaModelCommon *pCommon, // Scope in which to find the hierarchy. + mdTypeDef td, // TypeDef whose hierarchy is needed. + CQuickArray<mdTypeDef> &cqaTdNesters,// Array of Nesters. + CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters. + CQuickArray<LPCUTF8> &cqaNames); // Names of the nesters. + + static HRESULT GetTRNesterHierarchy( + IMetaModelCommon *pCommon, // Scope in which to find the hierarchy. + mdTypeRef tr, // TypeRef whose hierarchy is needed. + CQuickArray<mdTypeRef> &cqaTrNesters,// Array of Nesters. + CQuickArray<LPCUTF8> &cqaNamespaces, // Namespaces of the nesters. + CQuickArray<LPCUTF8> &cqaNames); // Names of the nesters. + + static HRESULT CreateModuleRefFromScope( + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope in which the ModuleRef is to be created. + IMetaModelCommon *pCommonImport, // [IN] Import scope. + mdModuleRef *ptkModuleRef); // [OUT] Output token for ModuleRef. + + static HRESULT CreateModuleRefFromModuleRef( // S_OK or error. + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope. + IMetaModelCommon *pCommon, // [IN] Import scope. + mdModuleRef tkModuleRef, // [IN] ModuleRef token. + mdModuleRef *ptkModuleRef); // [OUT] ModuleRef token in the emit scope. + + static HRESULT CreateModuleRefFromExportedType( // S_OK, S_FALSE or error. + CMiniMdRW *pAssemEmit, // [IN] Import assembly scope. + CMiniMdRW *pMiniMdEmit, // [IN] Emit scope. + mdExportedType tkExportedType, // [IN] ExportedType token in Assembly emit scope. + mdModuleRef *ptkModuleRef); // [OUT] ModuleRef token in the emit scope. + + // CreateAssemblyRefFromAssembly, CompareAssemblyRefToAssembly are in satellite libs because + // they are only used in emit cases and need strong-name support in mscorwks.dll. + + static HRESULT CreateAssemblyRefFromAssembly( // S_OK or error. + CMiniMdRW *pMiniMdAssemEmit, // [IN] Emit assembly scope. + CMiniMdRW *pMiniMdModuleEmit, // [IN] Emit module scope. + IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope. + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + mdAssemblyRef *ptkAssemblyRef); // [OUT] AssemblyRef token. + + static HRESULT CompareAssemblyRefToAssembly( // S_OK, S_FALSE or error. + IMetaModelCommon *pCommonAssem1, // [IN] Assembly that defines the AssemblyRef. + mdAssemblyRef tkAssemRef, // [IN] AssemblyRef. + IMetaModelCommon *pCommonAssem2); // [IN] Assembly against which the Ref is compared. + + static HRESULT CreateAssemblyRefFromAssemblyRef( + CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope. + CMiniMdRW *pMiniMdModuleEmit, // [IN] Module emit scope + IMetaModelCommon *pCommonImport, // [IN] Scope to import the assembly ref from. + mdAssemblyRef tkAssemRef, // [IN] Assembly ref to be imported. + mdAssemblyRef *ptkAssemblyRef); // [OUT] AssemblyRef in the emit scope. +}; + +#endif // __IMPORTHELPER__h__ diff --git a/src/coreclr/md/compiler/mdperf.cpp b/src/coreclr/md/compiler/mdperf.cpp new file mode 100644 index 00000000000..016b28d9a4c --- /dev/null +++ b/src/coreclr/md/compiler/mdperf.cpp @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDperf.cpp +// + +// +// This file provides Compiler Support functionality in metadata. +//***************************************************************************** + +#include "stdafx.h" + +#include "mdperf.h" + +#ifdef FEATURE_METADATA_PERF_STATS + +//----------------------------------------------------------------------------- +// Global array containing the name of the APIs. This is shared across +// all instances of MDCompilerPerf. +//----------------------------------------------------------------------------- +char g_szNameOfAPI[LAST_MD_API][API_NAME_STR_SIZE]; + +//----------------------------------------------------------------------------- +// Constructor. Initialize counters to 0. Initialize names of MD APIs. +//----------------------------------------------------------------------------- +MDCompilerPerf::MDCompilerPerf() +{ + // Initialize counters + for (int idx=0; idx < LAST_MD_API; idx++) + { + MDPerfStats[idx].dwCalledNumTimes = 0; + MDPerfStats[idx].dwQueryPerfCycles = 0; + } + +#undef MD_FUNC +#define MD_FUNC(MDTag)\ + strncpy(g_szNameOfAPI[MDTag ## _ENUM], #MDTag, API_NAME_STR_SIZE-1); + + MD_COMPILER_PERF_TABLE; // Relies on the MD_FUNC defined above. +} + +MDCompilerPerf::~MDCompilerPerf() + { + // Output the stats and cleanup. + MetaDataPerfReport (); + } + +//----------------------------------------------------------------------------- +// Output stats. <TODO>TODO: grow this into stats for per fautomation</TODO> +//----------------------------------------------------------------------------- +void MDCompilerPerf::MetaDataPerfReport () +{ + LARGE_INTEGER freqVal; + DWORD totalCalls=0, totalCycles=0; + + if (!QueryPerformanceFrequency(&freqVal)) + { + printf("Perf counters not supported\n"); + return; + } + + for (int idx=0; idx < LAST_MD_API; idx++) + { + totalCalls += MDPerfStats[idx].dwCalledNumTimes; + totalCycles += MDPerfStats[idx].dwQueryPerfCycles; + } + + if (!(totalCalls && totalCycles && freqVal.QuadPart)) + { + // if any of above is 0 then things don't look good. + printf("No data gathered ...\n"); + return; + } + + printf("\n%-32.32s %-16.16s %-16.16s %-16.16s\n", "API Name", "# Calls", "Cycles", "Time (msec)"); + for (idx=0; idx < LAST_MD_API; idx++) + { + if(MDPerfStats[idx].dwCalledNumTimes != 0) + printf( "%-32.32s %-9d [%3.2d%%] %-16d %-8.2f [%3.2d%%]\n", + g_szNameOfAPI[idx], + MDPerfStats[idx].dwCalledNumTimes, + (MDPerfStats[idx].dwCalledNumTimes*100)/totalCalls, + MDPerfStats[idx].dwQueryPerfCycles, + ((float)MDPerfStats[idx].dwQueryPerfCycles*1000)/(float)freqVal.QuadPart, + (MDPerfStats[idx].dwQueryPerfCycles*100)/totalCycles); + } + printf( "%-32.32s %-9d [100%%] %-16d %-8.2f [100%%]\n\n", + "Total Stats", + totalCalls, + totalCycles, + ((float)totalCycles*1000)/(float)freqVal.QuadPart); + +} + +#endif //FEATURE_METADATA_PERF_STATS diff --git a/src/coreclr/md/compiler/mdperf.h b/src/coreclr/md/compiler/mdperf.h new file mode 100644 index 00000000000..b7726b36e32 --- /dev/null +++ b/src/coreclr/md/compiler/mdperf.h @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// Mdperf.h +// + +// +//***************************************************************************** + +#ifndef __MDCOMPILERPERF_H__ +#define __MDCOMPILERPERF_H__ + +//#define FEATURE_METADATA_PERF_STATS + +#ifdef FEATURE_METADATA_PERF_STATS + +// Avoid dynamic allocs to display the API names. +#define API_NAME_STR_SIZE 80 + +//----------------------------------------------------------------------------- +// In order to add instrumentation for an API, two changes have to be made. +// One, add the API name in the table below (MD_TABLE). +// Second, add two lines of code (shown below) in the implementation +// of the API itself. e.g. +// RegMeta::MyNewMetataDataAPI(...) +// { +// LOG(...); +// START_MD_PERF(); // <------ add this line as is. +// .... +// // API implementation +// ErrExit: +// STOP_MD_PERF(RegMeta_MyNewMetaDataAPI); // <---------- add this line with the appropriate name +// return (hr); +// ] +// +//----------------------------------------------------------------------------- +#define MD_COMPILER_PERF_TABLE\ + MD_FUNC(SaveToMemory)\ + MD_FUNC(DefineMethod)\ + MD_FUNC(DefineMethodImpl)\ + MD_FUNC(SetRVA)\ + MD_FUNC(DefineTypeRefByName)\ + MD_FUNC(DefineImportType)\ + MD_FUNC(DefineMemberRef)\ + MD_FUNC(DefineImportMember)\ + MD_FUNC(DefineEvent)\ + MD_FUNC(SetClassLayout)\ + MD_FUNC(DeleteClassLayout)\ + MD_FUNC(SetFieldMarshal)\ + MD_FUNC(DeleteFieldMarshal)\ + MD_FUNC(DefinePermissionSet)\ + MD_FUNC(SetMemberIndex)\ + MD_FUNC(GetTokenFromSig)\ + MD_FUNC(DefineModuleRef)\ + MD_FUNC(SetParent)\ + MD_FUNC(GetTokenFromTypeSpec)\ + MD_FUNC(DefineUserString)\ + MD_FUNC(DeleteToken)\ + MD_FUNC(SetTypeDefProps)\ + MD_FUNC(DefineNestedType)\ + MD_FUNC(SetMethodProps)\ + MD_FUNC(SetEventProps)\ + MD_FUNC(SetPermissionSetProps)\ + MD_FUNC(DefinePinvokeMap)\ + MD_FUNC(SetPinvokeMap)\ + MD_FUNC(DeletePinvokeMap)\ + MD_FUNC(DefineField)\ + MD_FUNC(DefineProperty)\ + MD_FUNC(DefineParam)\ + MD_FUNC(SetFieldProps)\ + MD_FUNC(SetPropertyProps)\ + MD_FUNC(SetParamProps)\ + MD_FUNC(EnumMembers)\ + MD_FUNC(EnumMembersWithName)\ + MD_FUNC(EnumMethods)\ + MD_FUNC(EnumMethodsWithName)\ + MD_FUNC(EnumFields)\ + MD_FUNC(EnumFieldsWithName)\ + MD_FUNC(EnumParams)\ + MD_FUNC(EnumMemberRefs)\ + MD_FUNC(EnumMethodImpls)\ + MD_FUNC(EnumPermissionSets)\ + MD_FUNC(FindMember)\ + MD_FUNC(FindMethod)\ + MD_FUNC(FindField)\ + MD_FUNC(FindMemberRef)\ + MD_FUNC(GetMethodProps)\ + MD_FUNC(GetMemberRefProps)\ + MD_FUNC(EnumProperties)\ + MD_FUNC(EnumEvents)\ + MD_FUNC(GetEventProps)\ + MD_FUNC(EnumMethodSemantics)\ + MD_FUNC(GetMethodSemantics)\ + MD_FUNC(GetClassLayout)\ + MD_FUNC(GetFieldMarshal)\ + MD_FUNC(GetRVA)\ + MD_FUNC(GetPermissionSetProps)\ + MD_FUNC(GetSigFromToken)\ + MD_FUNC(GetModuleRefProps)\ + MD_FUNC(EnumModuleRefs)\ + MD_FUNC(GetTypeSpecFromToken)\ + MD_FUNC(GetNameFromToken)\ + MD_FUNC(EnumUnresolvedMethods)\ + MD_FUNC(GetUserString)\ + MD_FUNC(GetPinvokeMap)\ + MD_FUNC(EnumSignatures)\ + MD_FUNC(EnumTypeSpecs)\ + MD_FUNC(EnumUserStrings)\ + MD_FUNC(GetParamForMethodIndex)\ + MD_FUNC(GetMemberProps)\ + MD_FUNC(GetFieldProps)\ + MD_FUNC(GetPropertyProps)\ + MD_FUNC(GetParamProps)\ + MD_FUNC(SetModuleProps)\ + MD_FUNC(Save)\ + MD_FUNC(SaveToStream)\ + MD_FUNC(GetSaveSize)\ + MD_FUNC(Merge)\ + MD_FUNC(DefineCustomAttribute)\ + MD_FUNC(SetCustomAttributeValue)\ + MD_FUNC(DefineSecurityAttributeSet)\ + MD_FUNC(UnmarkAll)\ + MD_FUNC(MarkToken)\ + MD_FUNC(IsTokenMarked)\ + MD_FUNC(DefineTypeDef)\ + MD_FUNC(SetHandler)\ + MD_FUNC(CountEnum)\ + MD_FUNC(ResetEnum)\ + MD_FUNC(EnumTypeDefs)\ + MD_FUNC(EnumInterfaceImpls)\ + MD_FUNC(EnumTypeRefs)\ + MD_FUNC(FindTypeDefByName)\ + MD_FUNC(FindTypeDefByGUID)\ + MD_FUNC(GetScopeProps)\ + MD_FUNC(GetModuleFromScope)\ + MD_FUNC(GetTypeDefProps)\ + MD_FUNC(GetInterfaceImplProps)\ + MD_FUNC(GetCustomAttributeByName)\ + MD_FUNC(GetTypeRefProps)\ + MD_FUNC(ResolveTypeRef)\ + MD_FUNC(EnumCustomAttributes)\ + MD_FUNC(GetCustomAttributeProps)\ + MD_FUNC(FindTypeRef)\ + MD_FUNC(RefToDefOptimization)\ + MD_FUNC(DefineAssembly)\ + MD_FUNC(DefineAssemblyRef)\ + MD_FUNC(DefineFile)\ + MD_FUNC(DefineExportedType)\ + MD_FUNC(DefineManifestResource)\ + MD_FUNC(DefineExecutionLocation)\ + MD_FUNC(SetAssemblyProps)\ + MD_FUNC(SetAssemblyRefProps)\ + MD_FUNC(SetFileProps)\ + MD_FUNC(SetExportedTypeProps)\ + MD_FUNC(GetAssemblyProps)\ + MD_FUNC(GetAssemblyRefProps)\ + MD_FUNC(GetFileProps)\ + MD_FUNC(GetExportedTypeProps)\ + MD_FUNC(GetManifestResourceProps)\ + MD_FUNC(EnumAssemblyRefs)\ + MD_FUNC(EnumFiles)\ + MD_FUNC(EnumExportedTypes)\ + MD_FUNC(EnumManifestResources)\ + MD_FUNC(EnumExecutionLocations)\ + MD_FUNC(GetAssemblyFromScope)\ + MD_FUNC(FindExportedTypeByName)\ + MD_FUNC(FindManifestResourceByName)\ + MD_FUNC(FindAssembliesByName)\ + MD_FUNC(SetGenericPars)\ + MD_FUNC(DefineGenericParam)\ + MD_FUNC(SetGenericParamProps)\ + MD_FUNC(EnumGenericParamConstraints)\ + MD_FUNC(GetGenericParamProps)\ + MD_FUNC(GetGenericParamConstraintProps)\ + MD_FUNC(GetPEKind)\ + MD_FUNC(GetVersionString)\ + MD_FUNC(GetAssemblyUnification) + +//----------------------------------------------------------------------------- +// Create an enum of all the API names. This is the index to access the APIs. +//----------------------------------------------------------------------------- +#undef MD_FUNC +#define MD_FUNC(MDTag)\ + MDTag ## _ENUM, + +typedef enum _MDAPIs +{ + MD_COMPILER_PERF_TABLE + LAST_MD_API +} MDApis; + +//----------------------------------------------------------------------------- +// Declare the struct which contais all the interesting stats for a particular +// API call. +//----------------------------------------------------------------------------- +typedef struct _MDAPIPerfData +{ + DWORD dwQueryPerfCycles; // # of cycles spent in this call + DWORD dwCalledNumTimes; // # of times this API was called +} MDAPIPerfData; + + +//----------------------------------------------------------------------------- +// MDCompilerPerf +//----------------------------------------------------------------------------- +class MDCompilerPerf +{ +public: + MDCompilerPerf(); + ~MDCompilerPerf(); + +private: + MDAPIPerfData MDPerfStats[LAST_MD_API]; + + void MetaDataPerfReport (); +}; + +// Note that this macro declares a local var. +#define START_MD_PERF()\ + LARGE_INTEGER __startVal;\ + QueryPerformanceCounter(&__startVal); + +#undef MD_FUNC +#define MD_FUNC(MDTag)\ + MDTag ## _ENUM + +// Note that this macro uses the local var startVal declared in START_MD_PERF() +#define STOP_MD_PERF(MDTag)\ + LARGE_INTEGER __stopVal;\ + QueryPerformanceCounter(&__stopVal);\ + m_MDCompilerPerf.MDPerfStats[MD_FUNC(MDTag)].dwCalledNumTimes++;\ + m_MDCompilerPerf.MDPerfStats[MD_FUNC(MDTag)].dwQueryPerfCycles += (DWORD)(__stopVal.QuadPart - __startVal.QuadPart); + +#else //!FEATURE_METADATA_PERF_STATS + +#define START_MD_PERF() +#define STOP_MD_PERF(MDTag) + +#endif //!FEATURE_METADATA_PERF_STATS + +#endif // __MDCOMPILERPERF_H__ diff --git a/src/coreclr/md/compiler/mdutil.cpp b/src/coreclr/md/compiler/mdutil.cpp new file mode 100644 index 00000000000..55ac77e9a3a --- /dev/null +++ b/src/coreclr/md/compiler/mdutil.cpp @@ -0,0 +1,506 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDUtil.cpp +// + +// +// contains utility code to MD directory. This is only used for the full version. +// +//***************************************************************************** +#include "stdafx.h" +#include "metadata.h" +#include "mdutil.h" +#include "regmeta.h" +#include "disp.h" +#include "mdcommon.h" +#include "importhelper.h" +#include "sstring.h" + +#include <rwutil.h> + +#if defined(FEATURE_METADATA_IN_VM) + +LOADEDMODULES * LOADEDMODULES::s_pLoadedModules = NULL; +UTSemReadWrite * LOADEDMODULES::m_pSemReadWrite = NULL; +RegMeta * LOADEDMODULES::m_HashedModules[LOADEDMODULES_HASH_SIZE] = { NULL }; + +//***************************************************************************** +// Hash a file name. +//***************************************************************************** +ULONG LOADEDMODULES::HashFileName( + LPCWSTR szName) +{ + return HashString(szName) % LOADEDMODULES_HASH_SIZE; +} // LOADEDMODULES::HashFileName + +//--------------------------------------------------------------------------------------- +// +// Initialize the static instance and lock. +// +HRESULT +LOADEDMODULES::InitializeStatics() +{ + HRESULT hr = S_OK; + + if (VolatileLoad(&s_pLoadedModules) == NULL) + { + // Initialize global read-write lock + { + NewHolder<UTSemReadWrite> pSemReadWrite = new (nothrow) UTSemReadWrite(); + IfNullGo(pSemReadWrite); + IfFailGo(pSemReadWrite->Init()); + + if (InterlockedCompareExchangeT<UTSemReadWrite *>(&m_pSemReadWrite, pSemReadWrite, NULL) == NULL) + { // We won the initialization race + pSemReadWrite.SuppressRelease(); + } + } + + // Initialize the global instance + { + NewHolder<LOADEDMODULES> pLoadedModules = new (nothrow) LOADEDMODULES(); + IfNullGo(pLoadedModules); + + { + LOCKWRITE(); + + if (VolatileLoad(&s_pLoadedModules) == NULL) + { + VolatileStore(&s_pLoadedModules, pLoadedModules.Extract()); + } + } + } + } + +ErrExit: + return hr; +} // LOADEDMODULES::InitializeStatics + +//--------------------------------------------------------------------------------------- +// +// Destroy the static instance and lock. +// +void +LOADEDMODULES::DeleteStatics() +{ + HRESULT hr = S_OK; + + if (s_pLoadedModules != NULL) + { + delete s_pLoadedModules; + s_pLoadedModules = NULL; + } + if (m_pSemReadWrite != NULL) + { + delete m_pSemReadWrite; + m_pSemReadWrite = NULL; + } +} // LOADEDMODULES::DeleteStatics + +//***************************************************************************** +// Add a RegMeta pointer to the loaded module list +//***************************************************************************** +HRESULT LOADEDMODULES::AddModuleToLoadedList(RegMeta * pRegMeta) +{ + HRESULT hr = NOERROR; + RegMeta ** ppRegMeta; + + IfFailGo(InitializeStatics()); + + { + LOCKWRITE(); + + ppRegMeta = s_pLoadedModules->Append(); + IfNullGo(ppRegMeta); + + // The cache holds a copy of the pointer, but no ref-count. There is no + // point to the ref-count, because it just changes comparisons against 0 + // to comparisons against 1. + *ppRegMeta = pRegMeta; + + // If the module is read-only, hash it. + if (pRegMeta->IsReadOnly()) + { + ULONG ixHash = HashFileName(pRegMeta->GetNameOfDBFile()); + m_HashedModules[ixHash] = pRegMeta; + } + } + +ErrExit: + return hr; +} // LOADEDMODULES::AddModuleToLoadedList + +//***************************************************************************** +// Remove a RegMeta pointer from the loaded module list +//***************************************************************************** +BOOL LOADEDMODULES::RemoveModuleFromLoadedList(RegMeta * pRegMeta) +{ + BOOL bRemoved = FALSE; // Was this module removed from the cache? + int iFound = -1; // Index at which it was found. + ULONG cRef; // Ref count of the module. + + // Lock the cache for write, so that no other thread will find what this + // thread is about to delete, and so that no other thread will delete + // what this thread is about to try to find. + HRESULT hr = S_OK; + + IfFailGo(InitializeStatics()); + + { + LOCKWRITE(); + + // Search for this module in list of loaded modules. + int count = s_pLoadedModules->Count(); + for (int index = 0; index < count; index++) + { + if ((*s_pLoadedModules)[index] == pRegMeta) + { // found a match to remove + iFound = index; + break; + } + } + + // If the module is still in the cache, it hasn't been deleted yet. + if (iFound >= 0) + { + // See if there are any external references left. + cRef = pRegMeta->GetRefCount(); + + // If the cRef that we got from the module is zero, it will stay that way, + // because no other thread can discover the module while this thread holds + // the lock. + + // OTOH, if the cRef is not zero, this thread can just return, because the + // other thread will eventually take the ref count to zero, and will then + // come through here to clean up the module. And this thread must not + // delete the module out from under other threads. + + // It is possible that the cRef is zero, yet another thread has a pointer that + // it discovered before this thread took the lock. (And that thread has + // released the ref-counts.) In such a case, this thread can still remove the + // module from the cache, and tell the caller to delete it, because the + // other thread will wait on the lock, then discover that the module + // is not in the cache, and it won't try to delete the module. + + if (cRef != 0) + { // Some other thread snuck in and found the entry in the cache. + return FALSE; + } + + // No other thread owns the object. Remove from cache, and tell caller + // that we're done with it. (Caller will delete.) + s_pLoadedModules->Delete(iFound); + bRemoved = TRUE; + + // If the module is read-only, remove from hash. + if (pRegMeta->IsReadOnly()) + { + // There may have been multiple capitalizations pointing to the same entry. + // Find and remove all of them. + for (ULONG ixHash = 0; ixHash < LOADEDMODULES_HASH_SIZE; ++ixHash) + { + if (m_HashedModules[ixHash] == pRegMeta) + m_HashedModules[ixHash] = NULL; + } + } + } + } + +ErrExit: + return bRemoved; +} // LOADEDMODULES::RemoveModuleFromLoadedList + + +//***************************************************************************** +// Search the cached RegMetas for a given scope. +//***************************************************************************** +HRESULT LOADEDMODULES::FindCachedReadOnlyEntry( + LPCWSTR szName, // Name of the desired file. + DWORD dwOpenFlags, // Flags the new file is opened with. + RegMeta ** ppMeta) // Put found RegMeta here. +{ + RegMeta * pRegMeta = 0; + BOOL bWillBeCopyMemory; // Will the opened file be copied to memory? + DWORD dwLowFileSize; // Low bytes of this file's size + DWORD dwLowFileTime; // Low butes of this file's last write time + HRESULT hr; + ULONG ixHash = 0; + + IfFailGo(InitializeStatics()); + + { + LOCKREAD(); + + hr = S_FALSE; // We haven't found a match yet. + + // Avoid confusion. + *ppMeta = NULL; + + bWillBeCopyMemory = IsOfCopyMemory(dwOpenFlags); + + // The cache is locked for read, so the list will not change. + + // Figure out the size and timestamp of this file + WIN32_FILE_ATTRIBUTE_DATA faData; + if (!WszGetFileAttributesEx(szName, GetFileExInfoStandard, &faData)) + return E_FAIL; + dwLowFileSize = faData.nFileSizeLow; + dwLowFileTime = faData.ftLastWriteTime.dwLowDateTime; + + // Check the hash first. + ixHash = HashFileName(szName); + if ((pRegMeta = m_HashedModules[ixHash]) != NULL) + { + _ASSERTE(pRegMeta->IsReadOnly()); + + // Only match if the IsOfCopyMemory() bit is the same in both. This is because + // when ofCopyMemory is set, the file is not locked on disk, and may become stale + // in memory. + // + // Also, only match if the date and size are the same + if (pRegMeta->IsCopyMemory() == bWillBeCopyMemory && + pRegMeta->GetLowFileTimeOfDBFile() == dwLowFileTime && + pRegMeta->GetLowFileSizeOfDBFile() == dwLowFileSize) + { + // If the name matches... + LPCWSTR pszName = pRegMeta->GetNameOfDBFile(); + #ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM + if (wcscmp(szName, pszName) == 0) + #else + if (SString::_wcsicmp(szName, pszName) == 0) + #endif + { + ULONG cRefs; + + // Found it. Add a reference, and return it. + *ppMeta = pRegMeta; + cRefs = pRegMeta->AddRef(); + + LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope found cached RegMeta in hash: %#8x, crefs: %d\n", pRegMeta, cRefs)); + + return S_OK; + } + } + } + + // Not found in hash; loop through each loaded modules + int count = s_pLoadedModules->Count(); + for (int index = 0; index < count; index++) + { + pRegMeta = (*s_pLoadedModules)[index]; + + // If the module is read-only, and the CopyMemory bit matches, and the date + // and size are the same.... + if (pRegMeta->IsReadOnly() && + pRegMeta->IsCopyMemory() == bWillBeCopyMemory && + pRegMeta->GetLowFileTimeOfDBFile() == dwLowFileTime && + pRegMeta->GetLowFileSizeOfDBFile() == dwLowFileSize) + { + // If the name matches... + LPCWSTR pszName = pRegMeta->GetNameOfDBFile(); + #ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM + if (wcscmp(szName, pszName) == 0) + #else + if (SString::_wcsicmp(szName, pszName) == 0) + #endif + { + ULONG cRefs; + + // Found it. Add a reference, and return it. + *ppMeta = pRegMeta; + cRefs = pRegMeta->AddRef(); + + // Update the hash. + m_HashedModules[ixHash] = pRegMeta; + + LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope found cached RegMeta by search: %#8x, crefs: %d\n", pRegMeta, cRefs)); + + return S_OK; + } + } + } + } + +ErrExit: + // Didn't find it. + LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope did not find cached RegMeta\n")); + + _ASSERTE(hr != S_OK); + return hr; +} // LOADEDMODULES::FindCachedReadOnlyEntry + +#ifdef _DEBUG + +//***************************************************************************** +// Search the cached RegMetas for a given scope. +//***************************************************************************** +BOOL LOADEDMODULES::IsEntryInList( + RegMeta * pRegMeta) +{ + HRESULT hr = S_OK; + + IfFailGo(InitializeStatics()); + + { + LOCKREAD(); + + // Loop through each loaded modules + int count = s_pLoadedModules->Count(); + for (int index = 0; index < count; index++) + { + if ((*s_pLoadedModules)[index] == pRegMeta) + { + return TRUE; + } + } + } + +ErrExit: + return FALSE; +} // LOADEDMODULES::IsEntryInList + +#endif //_DEBUG + +#endif //FEATURE_METADATA_IN_VM + +#ifdef FEATURE_METADATA_IN_VM + +//***************************************************************************** +// Remove a RegMeta pointer from the loaded module list +//***************************************************************************** +// static +HRESULT +LOADEDMODULES::ResolveTypeRefWithLoadedModules( + mdTypeRef tkTypeRef, // [IN] TypeRef to be resolved. + RegMeta * pTypeRefRegMeta, // [IN] Scope in which the TypeRef is defined. + IMetaModelCommon * pTypeRefScope, // [IN] Scope in which the TypeRef is defined. + REFIID riid, // [IN] iid for the return interface. + IUnknown ** ppIScope, // [OUT] Return interface. + mdTypeDef * ptd) // [OUT] TypeDef corresponding the TypeRef. +{ + HRESULT hr = NOERROR; + RegMeta * pRegMeta; + CQuickArray<mdTypeRef> cqaNesters; + CQuickArray<LPCUTF8> cqaNesterNamespaces; + CQuickArray<LPCUTF8> cqaNesterNames; + + IfFailGo(InitializeStatics()); + + { + LOCKREAD(); + + // Get the Nesting hierarchy. + IfFailGo(ImportHelper::GetNesterHierarchy( + pTypeRefScope, + tkTypeRef, + cqaNesters, + cqaNesterNamespaces, + cqaNesterNames)); + + int count = s_pLoadedModules->Count(); + for (int index = 0; index < count; index++) + { + pRegMeta = (*s_pLoadedModules)[index]; + + { + // Do not lock the TypeRef RegMeta (again), as it is already locked for read by the caller. + // The code:UTSemReadWrite will block ReadLock even for thread holding already the read lock if + // some other thread is waiting for WriteLock on the same lock. That would cause dead-lock if we + // try to lock for read again here. + CMDSemReadWrite cSemRegMeta((pRegMeta == pTypeRefRegMeta) ? NULL : pRegMeta->GetReaderWriterLock()); + IfFailGo(cSemRegMeta.LockRead()); + + hr = ImportHelper::FindNestedTypeDef( + pRegMeta->GetMiniMd(), + cqaNesterNamespaces, + cqaNesterNames, + mdTokenNil, + ptd); + } + if (hr == CLDB_E_RECORD_NOTFOUND) + { // Process next MetaData module + continue; + } + IfFailGo(hr); + + // Found a loaded module containing the TypeDef. + IfFailGo(pRegMeta->QueryInterface(riid, (void **)ppIScope)); + break; + } + } + if (FAILED(hr)) + { + // cannot find the match! + hr = E_FAIL; + } +ErrExit: + return hr; +} // LOADEDMODULES::ResolveTypeRefWithLoadedModules + +#endif //FEATURE_METADATA_IN_VM + +//******************************************************************************* +// +// Determine the blob size base of the ELEMENT_TYPE_* associated with the blob. +// This cannot be a table lookup because ELEMENT_TYPE_STRING is an unicode string. +// The size of the blob is determined by calling wcsstr of the string + 1. +// +//******************************************************************************* +ULONG _GetSizeOfConstantBlob( + DWORD dwCPlusTypeFlag, // ELEMENT_TYPE_* + void * pValue, // BLOB value + ULONG cchString) // String length in wide chars, or -1 for auto. +{ + ULONG ulSize = 0; + + switch (dwCPlusTypeFlag) + { + case ELEMENT_TYPE_BOOLEAN: + ulSize = sizeof(BYTE); + break; + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + ulSize = sizeof(BYTE); + break; + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + ulSize = sizeof(SHORT); + break; + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_R4: + ulSize = sizeof(LONG); + + break; + + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R8: + ulSize = sizeof(DOUBLE); + break; + + case ELEMENT_TYPE_STRING: + if (pValue == 0) + ulSize = 0; + else + if (cchString != (ULONG) -1) + ulSize = cchString * sizeof(WCHAR); + else + ulSize = (ULONG)(sizeof(WCHAR) * wcslen((LPWSTR)pValue)); + break; + + case ELEMENT_TYPE_CLASS: + // This was originally 'sizeof(IUnknown *)', but that varies across platforms. + // The only legal value is a null pointer, and on 32 bit platforms we've already + // stored 32 bits, so we will use just 32 bits of null. If the type is + // E_T_CLASS, the caller should know that the value is always NULL anyway. + ulSize = sizeof(ULONG); + break; + default: + _ASSERTE(!"Not a valid type to specify default value!"); + break; + } + return ulSize; +} // _GetSizeOfConstantBlob diff --git a/src/coreclr/md/compiler/mdutil.h b/src/coreclr/md/compiler/mdutil.h new file mode 100644 index 00000000000..eadc93a3dd8 --- /dev/null +++ b/src/coreclr/md/compiler/mdutil.h @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDUtil.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __MDUtil__h__ +#define __MDUtil__h__ + +#include "metadata.h" + + +HRESULT _GetFixedSigOfVarArg( // S_OK or error. + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob of COM+ method signature + ULONG cbSigBlob, // [IN] size of signature + CQuickBytes *pqbSig, // [OUT] output buffer for fixed part of VarArg Signature + ULONG *pcbSigBlob); // [OUT] number of bytes written to the above output buffer + +ULONG _GetSizeOfConstantBlob( + DWORD dwCPlusTypeFlag, // ELEMENT_TYPE_* + void *pValue, // BLOB value + ULONG cchString); // Size of string in wide chars, or -1 for auto. + +#if defined(FEATURE_METADATA_IN_VM) + +class RegMeta; + +//********************************************************************* +// +// Structure to record the all loaded modules and helpers. +// RegMeta instance is added to the global variable that is tracking +// the opened scoped. This happens in RegMeta's constructor. +// In RegMeta's destructor, the RegMeta pointer will be removed from +// this list. +// +//********************************************************************* +class UTSemReadWrite; +#define LOADEDMODULES_HASH_SIZE 47 + +class LOADEDMODULES : public CDynArray<RegMeta *> +{ +private: + static HRESULT InitializeStatics(); + + // Global per-process list of loaded modules + static LOADEDMODULES * s_pLoadedModules; + +public: + static void DeleteStatics(); + + // Named for locking macros - see code:LOCKREAD + static UTSemReadWrite * m_pSemReadWrite; + static RegMeta *m_HashedModules[LOADEDMODULES_HASH_SIZE]; + + static ULONG HashFileName(LPCWSTR szName); + + static HRESULT AddModuleToLoadedList(RegMeta *pRegMeta); + static BOOL RemoveModuleFromLoadedList(RegMeta *pRegMeta); // true if found and removed. + + static HRESULT FindCachedReadOnlyEntry(LPCWSTR szName, DWORD dwOpenFlags, RegMeta **ppMeta); + +#ifdef FEATURE_METADATA_IN_VM + static HRESULT ResolveTypeRefWithLoadedModules( + mdTypeRef tkTypeRef, // [IN] TypeRef to be resolved. + RegMeta * pTypeRefRegMeta, // [IN] Scope in which the TypeRef is defined. + IMetaModelCommon * pTypeRefScope, // [IN] Scope in which the TypeRef is defined. + REFIID riid, // [IN] iid for the return interface. + IUnknown ** ppIScope, // [OUT] Return interface. + mdTypeDef * ptd); // [OUT] TypeDef corresponding the TypeRef. +#endif //FEATURE_METADATA_IN_VM + +#ifdef _DEBUG + static BOOL IsEntryInList(RegMeta *pRegMeta); +#endif +}; // class LOADEDMODULES + +#endif //FEATURE_METADATA_IN_VM + +#endif // __MDUtil__h__ diff --git a/src/coreclr/md/compiler/regmeta.cpp b/src/coreclr/md/compiler/regmeta.cpp new file mode 100644 index 00000000000..11c1974c157 --- /dev/null +++ b/src/coreclr/md/compiler/regmeta.cpp @@ -0,0 +1,1605 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RegMeta.cpp +// + +// +// Implementation for meta data public interface methods. +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" +#include "filtermanager.h" +#include "mdperf.h" +#include "switches.h" +#include "posterror.h" +#include "stgio.h" +#include "sstring.h" + +#include "mdinternalrw.h" + + +#include <metamodelrw.h> + +#define DEFINE_CUSTOM_NODUPCHECK 1 +#define DEFINE_CUSTOM_DUPCHECK 2 +#define SET_CUSTOM 3 + +#if defined(_DEBUG) && defined(_TRACE_REMAPS) +#define LOGGING +#endif +#include <log.h> + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +RegMeta::RegMeta() : + m_pStgdb(0), + m_pStgdbFreeList(NULL), + m_pUnk(0), + m_pFilterManager(NULL), +#ifdef FEATURE_METADATA_INTERNAL_APIS + m_pInternalImport(NULL), +#endif + m_pSemReadWrite(NULL), + m_fOwnSem(false), + m_bRemap(false), + m_bSaveOptimized(false), + m_hasOptimizedRefToDef(false), + m_pHandler(0), + m_fIsTypeDefDirty(false), + m_fIsMemberDefDirty(false), + m_fStartedEE(false), + m_pAppDomain(NULL), + m_OpenFlags(0), + m_cRef(0), + m_pFreeThreadedMarshaler(NULL), + m_bCached(false), + m_trLanguageType(0), + m_SetAPICaller(EXTERNAL_CALLER), + m_ModuleType(ValidatorModuleTypeInvalid), + m_bKeepKnownCa(false), + m_pCorProfileData(NULL), + m_ReorderingOptions(NoReordering) +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN + , m_safeToDeleteStgdb(true) +#endif +{ + memset(&m_OptionValue, 0, sizeof(OptionValue)); + +#ifdef _DEBUG + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaBreak)) + { + _ASSERTE(!"RegMeta()"); + } + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_KeepKnownCA)) + m_bKeepKnownCa = true; +#endif // _DEBUG + +} // RegMeta::RegMeta() + +RegMeta::~RegMeta() +{ + BEGIN_CLEANUP_ENTRYPOINT; + + _ASSERTE(!m_bCached); + + HRESULT hr = S_OK; + + LOCKWRITENORET(); + +#ifdef FEATURE_METADATA_INTERNAL_APIS + // This should have worked if we've cached the public interface in the past + _ASSERTE(SUCCEEDED(hr) || (m_pInternalImport == NULL) || (m_pInternalImport->GetCachedPublicInterface(false) == NULL)); +#endif //FEATURE_METADATA_INTERNAL_APIS + + if (SUCCEEDED(hr)) + { +#ifdef FEATURE_METADATA_INTERNAL_APIS + if (m_pInternalImport != NULL) + { + // RegMeta is going away. Make sure we clear up the pointer from MDInternalRW to this RegMeta. + if (FAILED(m_pInternalImport->SetCachedPublicInterface(NULL))) + { // Do nothing on error + } + m_pInternalImport = NULL; + m_fOwnSem = false; + } +#endif //FEATURE_METADATA_INTERNAL_APIS + + UNLOCKWRITE(); + } + + if (m_pFreeThreadedMarshaler) + { + m_pFreeThreadedMarshaler->Release(); + m_pFreeThreadedMarshaler = NULL; + } + + if (m_pSemReadWrite && m_fOwnSem) + delete m_pSemReadWrite; + + // If this RegMeta is a wrapper on an external StgDB, release it. + if (IsOfExternalStgDB(m_OpenFlags)) + { + _ASSERTE(m_pUnk != NULL); // Owning IUnknown for external StgDB. + if (m_pUnk) + m_pUnk->Release(); + m_pUnk = 0; + } + else + { // Not a wrapper, so free our StgDB. + _ASSERTE(m_pUnk == NULL); + // It's possible m_pStdbg is NULL in OOM scenarios + if (m_pStgdb != NULL) + delete m_pStgdb; + m_pStgdb = 0; + } + + // Delete the old copies of Stgdb list. This is the list track all of the + // old snapshuts with ReOpenWithMemory call. + CLiteWeightStgdbRW *pCur; + while (m_pStgdbFreeList) + { + pCur = m_pStgdbFreeList; + m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb; + delete pCur; + } + + // If This RegMeta spun up the runtime (probably to process security + // attributes), shut it down now. + if (m_fStartedEE) + { + m_pAppDomain->Release(); + } + + if (m_pFilterManager != NULL) + delete m_pFilterManager; + + + if (m_OptionValue.m_RuntimeVersion != NULL) + delete[] m_OptionValue.m_RuntimeVersion; + + END_CLEANUP_ENTRYPOINT; + +} // RegMeta::~RegMeta() + +HRESULT RegMeta::SetOption(OptionValue *pOptionValue) +{ + _ASSERTE(pOptionValue); + char* pszRuntimeVersion = NULL; + + if (pOptionValue->m_RuntimeVersion != NULL) + { + SIZE_T dwBufferSize = strlen(pOptionValue->m_RuntimeVersion) + 1; // +1 for null + pszRuntimeVersion = new (nothrow) char[dwBufferSize]; + if (pszRuntimeVersion == NULL) + { + return E_OUTOFMEMORY; + } + strcpy_s(pszRuntimeVersion, dwBufferSize, pOptionValue->m_RuntimeVersion); + } + + memcpy(&m_OptionValue, pOptionValue, sizeof(OptionValue)); + m_OptionValue.m_RuntimeVersion = pszRuntimeVersion; + + return S_OK; +}// SetOption + + +//***************************************************************************** +// Initialize with an existing stgdb. +//***************************************************************************** +__checkReturn +HRESULT +RegMeta::InitWithStgdb( + IUnknown *pUnk, // The IUnknown that owns the life time for the existing stgdb + CLiteWeightStgdbRW *pStgdb) // existing light weight stgdb +{ + // RegMeta created this way will not create a read/write lock semaphore. + HRESULT hr = S_OK; + + _ASSERTE(m_pStgdb == NULL); + m_tdModule = COR_GLOBAL_PARENT_TOKEN; + m_pStgdb = pStgdb; + + m_OpenFlags = ofExternalStgDB; + + // remember the owner of the light weight stgdb + // AddRef it to ensure the lifetime + // + m_pUnk = pUnk; + m_pUnk->AddRef(); + IfFailGo(m_pStgdb->m_MiniMd.GetOption(&m_OptionValue)); +ErrExit: + return hr; +} // RegMeta::InitWithStgdb + +#ifdef FEATURE_METADATA_EMIT + +//***************************************************************************** +// call stgdb InitNew +//***************************************************************************** +__checkReturn +HRESULT +RegMeta::CreateNewMD() +{ + HRESULT hr = NOERROR; + + m_OpenFlags = ofWrite; + + // Allocate our m_pStgdb. + _ASSERTE(m_pStgdb == NULL); + IfNullGo(m_pStgdb = new (nothrow) CLiteWeightStgdbRW); + + // Initialize the new, empty database. + + // First tell the new database what sort of metadata to create + m_pStgdb->m_MiniMd.m_OptionValue.m_MetadataVersion = m_OptionValue.m_MetadataVersion; + m_pStgdb->m_MiniMd.m_OptionValue.m_InitialSize = m_OptionValue.m_InitialSize; + IfFailGo(m_pStgdb->InitNew()); + + // Set up the Module record. + ULONG iRecord; + ModuleRec *pModule; + GUID mvid; + IfFailGo(m_pStgdb->m_MiniMd.AddModuleRecord(&pModule, &iRecord)); + IfFailGo(CoCreateGuid(&mvid)); + IfFailGo(m_pStgdb->m_MiniMd.PutGuid(TBL_Module, ModuleRec::COL_Mvid, pModule, mvid)); + + // Add the dummy module typedef which we are using to parent global items. + TypeDefRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.AddTypeDefRecord(&pRecord, &iRecord)); + m_tdModule = TokenFromRid(iRecord, mdtTypeDef); + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_TypeDef, TypeDefRec::COL_Name, pRecord, COR_WMODULE_CLASS)); + IfFailGo(m_pStgdb->m_MiniMd.SetOption(&m_OptionValue)); + + if (IsThreadSafetyOn()) + { + m_pSemReadWrite = new (nothrow) UTSemReadWrite(); + IfNullGo(m_pSemReadWrite); + IfFailGo(m_pSemReadWrite->Init()); + m_fOwnSem = true; + + INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + } + +ErrExit: + return hr; +} // RegMeta::CreateNewMD + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +//***************************************************************************** +// Create new stgdb for portable pdb +//***************************************************************************** +__checkReturn +HRESULT +RegMeta::CreateNewPortablePdbMD() +{ + HRESULT hr = NOERROR; + // TODO: move the constant to a better location + static const char* PDB_VERSION = "PDB v1.0"; + size_t len = strlen(PDB_VERSION) + 1; + + m_OpenFlags = ofWrite; + + // Allocate our m_pStgdb. + _ASSERTE(m_pStgdb == NULL); + IfNullGo(m_pStgdb = new (nothrow) CLiteWeightStgdbRW); + + // Initialize the new, empty database. + + // First tell the new database what sort of metadata to create + m_pStgdb->m_MiniMd.m_OptionValue.m_MetadataVersion = m_OptionValue.m_MetadataVersion; + m_pStgdb->m_MiniMd.m_OptionValue.m_InitialSize = m_OptionValue.m_InitialSize; + IfFailGo(m_pStgdb->InitNew()); + + // Set up the pdb version + m_OptionValue.m_RuntimeVersion = new char[len]; + strcpy_s(m_OptionValue.m_RuntimeVersion, len, PDB_VERSION); + + IfFailGo(m_pStgdb->m_MiniMd.SetOption(&m_OptionValue)); + + if (IsThreadSafetyOn()) + { + m_pSemReadWrite = new (nothrow) UTSemReadWrite(); + IfNullGo(m_pSemReadWrite); + IfFailGo(m_pSemReadWrite->Init()); + m_fOwnSem = true; + + INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + } + +ErrExit: + return hr; +} // RegMeta::CreateNewPortablePdbMD +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + +#endif //FEATURE_METADATA_EMIT + +//***************************************************************************** +// call stgdb OpenForRead +//***************************************************************************** +HRESULT RegMeta::OpenExistingMD( + LPCWSTR szDatabase, // Name of database. + void *pData, // Data to open on top of, 0 default. + ULONG cbData, // How big is the data. + ULONG dwOpenFlags) // Flags for the open. +{ + HRESULT hr = NOERROR; + void *pbData = pData; // Pointer to original or copied data. + + + + m_OpenFlags = dwOpenFlags; + + if (!IsOfReOpen(dwOpenFlags)) + { + // Allocate our m_pStgdb, if we should. + _ASSERTE(m_pStgdb == NULL); + IfNullGo( m_pStgdb = new (nothrow) CLiteWeightStgdbRW ); + } + + IfFailGo( m_pStgdb->OpenForRead( + szDatabase, + pbData, + cbData, + m_OpenFlags) ); + + if (m_pStgdb->m_MiniMd.m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0 && + m_pStgdb->m_MiniMd.m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0) + m_OptionValue.m_MetadataVersion = MDVersion1; + + else + m_OptionValue.m_MetadataVersion = MDVersion2; + + + + IfFailGo( m_pStgdb->m_MiniMd.SetOption(&m_OptionValue) ); + + if (IsThreadSafetyOn()) + { + m_pSemReadWrite = new (nothrow) UTSemReadWrite(); + IfNullGo(m_pSemReadWrite); + IfFailGo(m_pSemReadWrite->Init()); + m_fOwnSem = true; + + INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + } + + if (!IsOfReOpen(dwOpenFlags)) + { + // There must always be a Global Module class and its the first entry in + // the TypeDef table. + m_tdModule = TokenFromRid(1, mdtTypeDef); + } + +ErrExit: + + return hr; +} //RegMeta::OpenExistingMD + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE +HRESULT RegMeta::OpenExistingMD( + IMDCustomDataSource* pDataSource, // Name of database. + ULONG dwOpenFlags) // Flags to control open. +{ + HRESULT hr = NOERROR; + + m_OpenFlags = dwOpenFlags; + + if (!IsOfReOpen(dwOpenFlags)) + { + // Allocate our m_pStgdb, if we should. + _ASSERTE(m_pStgdb == NULL); + IfNullGo(m_pStgdb = new (nothrow)CLiteWeightStgdbRW); + } + + IfFailGo(m_pStgdb->OpenForRead( + pDataSource, + m_OpenFlags)); + + if (m_pStgdb->m_MiniMd.m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0 && + m_pStgdb->m_MiniMd.m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0) + m_OptionValue.m_MetadataVersion = MDVersion1; + + else + m_OptionValue.m_MetadataVersion = MDVersion2; + + + + IfFailGo(m_pStgdb->m_MiniMd.SetOption(&m_OptionValue)); + + if (IsThreadSafetyOn()) + { + m_pSemReadWrite = new (nothrow)UTSemReadWrite(); + IfNullGo(m_pSemReadWrite); + IfFailGo(m_pSemReadWrite->Init()); + m_fOwnSem = true; + + INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + } + + if (!IsOfReOpen(dwOpenFlags)) + { + // There must always be a Global Module class and its the first entry in + // the TypeDef table. + m_tdModule = TokenFromRid(1, mdtTypeDef); + } + +ErrExit: + + return hr; +} //RegMeta::OpenExistingMD +#endif // FEATURE_METADATA_CUSTOM_DATA_SOURCE + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +//***************************************************************************** +// Gets a cached Internal importer, if available. +// +// Arguments: +// fWithLock - if true, takes a reader lock. +// If false, assumes caller is handling the synchronization. +// +// Returns: +// A cached Internal importer, which gets addreffed. Caller must release! +// If no importer is set, returns NULL +// +// Notes: +// This function also does not trigger the creation of Internal interface. +// Set the cached importer via code:RegMeta.SetCachedInternalInterface +// +// Implements internal API code:IMetaDataHelper::GetCachedInternalInterface. +//***************************************************************************** +IUnknown* RegMeta::GetCachedInternalInterface(BOOL fWithLock) +{ + IUnknown *pRet = NULL; + HRESULT hr = S_OK; + + if (fWithLock) + { + LOCKREAD(); + + pRet = m_pInternalImport; + } + else + { + pRet = m_pInternalImport; + } + if (pRet) pRet->AddRef(); + +ErrExit: + + return pRet; +} //RegMeta::GetCachedInternalInterface + +//***************************************************************************** +// Set the cached Internal interface. This function will return an Error is the +// current cached internal interface is not empty and trying set a non-empty internal +// interface. One RegMeta will only associated +// with one Internal Object. Unless we have bugs somewhere else. It will QI on the +// IUnknown for the IMDInternalImport. If this failed, error will be returned. +// Note: Caller should take a write lock +// +// This does addref the importer (the public and private importers maintain +// weak references to each other). +// +// Implements internal API code:IMetaDataHelper::SetCachedInternalInterface. +//***************************************************************************** +HRESULT RegMeta::SetCachedInternalInterface(IUnknown *pUnk) +{ + HRESULT hr = NOERROR; + IMDInternalImport *pInternal = NULL; + + if (pUnk) + { + if (m_pInternalImport) + { + _ASSERTE(!"Bad state!"); + } + IfFailRet( pUnk->QueryInterface(IID_IMDInternalImport, (void **) &pInternal) ); + + // Should be non-null + _ASSERTE(pInternal); + m_pInternalImport = pInternal; + + // We don't want to add ref the internal interface, so undo the AddRef() from the QI. + pInternal->Release(); + } + else + { + // Internal interface is going away before the public interface. Take ownership on the + // reader writer lock. + m_fOwnSem = true; + m_pInternalImport = NULL; + } + return hr; +} // RegMeta::SetCachedInternalInterface + +#endif //FEATURE_METADATA_INTERNAL_APIS + +//***************************************************************************** +// IUnknown +//***************************************************************************** + +ULONG RegMeta::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} // ULONG RegMeta::AddRef() + + +HRESULT +RegMeta::QueryInterface( + REFIID riid, + void ** ppUnk) +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + int fIsInterfaceRW = false; + *ppUnk = 0; + + if (riid == IID_IUnknown) + { + *ppUnk = (IUnknown *)(IMetaDataImport2 *)this; + } + else if (riid == IID_IMDCommon) + { + *ppUnk = (IMDCommon *)this; + } + else if (riid == IID_IMetaDataImport) + { + *ppUnk = (IMetaDataImport2 *)this; + } + else if (riid == IID_IMetaDataImport2) + { + *ppUnk = (IMetaDataImport2 *)this; + } + else if (riid == IID_IMetaDataAssemblyImport) + { + *ppUnk = (IMetaDataAssemblyImport *)this; + } + else if (riid == IID_IMetaDataTables) + { + *ppUnk = static_cast<IMetaDataTables *>(this); + } + else if (riid == IID_IMetaDataTables2) + { + *ppUnk = static_cast<IMetaDataTables2 *>(this); + } + + else if (riid == IID_IMetaDataInfo) + { + *ppUnk = static_cast<IMetaDataInfo *>(this); + } + +#ifdef FEATURE_METADATA_EMIT + else if (riid == IID_IMetaDataEmit) + { + *ppUnk = (IMetaDataEmit2 *)this; + fIsInterfaceRW = true; + } + else if (riid == IID_IMetaDataEmit2) + { + *ppUnk = (IMetaDataEmit2 *)this; + fIsInterfaceRW = true; + } +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + else if (riid == IID_IMetaDataEmit3) + { + *ppUnk = (IMetaDataEmit3 *)this; + fIsInterfaceRW = true; + } +#endif + else if (riid == IID_IMetaDataAssemblyEmit) + { + *ppUnk = (IMetaDataAssemblyEmit *)this; + fIsInterfaceRW = true; + } +#endif //FEATURE_METADATA_EMIT + + +#ifdef FEATURE_METADATA_EMIT_ALL + else if (riid == IID_IMetaDataFilter) + { + *ppUnk = (IMetaDataFilter *)this; + } +#endif //FEATURE_METADATA_EMIT_ALL + +#ifdef FEATURE_METADATA_INTERNAL_APIS + else if (riid == IID_IMetaDataHelper) + { + *ppUnk = (IMetaDataHelper *)this; + } + else if (riid == IID_IMDInternalEmit) + { + *ppUnk = static_cast<IMDInternalEmit *>(this); + } + else if (riid == IID_IGetIMDInternalImport) + { + *ppUnk = static_cast<IGetIMDInternalImport *>(this); + } +#endif //FEATURE_METADATA_INTERNAL_APIS + +#if defined(FEATURE_METADATA_EMIT) && defined(FEATURE_METADATA_INTERNAL_APIS) + else if (riid == IID_IMetaDataEmitHelper) + { + *ppUnk = (IMetaDataEmitHelper *)this; + fIsInterfaceRW = true; + } +#endif //FEATURE_METADATA_EMIT && FEATURE_METADATA_INTERNAL_APIS + +#ifdef FEATURE_METADATA_IN_VM +#ifdef FEATURE_COMINTEROP + else if (riid == IID_IMarshal) + { + // We will only repond to this interface if scope is opened for ReadOnly + if (IsOfReadOnly(m_OpenFlags)) + { + if (m_pFreeThreadedMarshaler == NULL) + { + // Guard ourselves against first time QI on IMarshal from two different threads.. + LOCKWRITE(); + if (m_pFreeThreadedMarshaler == NULL) + { + // First time! Create the FreeThreadedMarshaler + IfFailGo(CoCreateFreeThreadedMarshaler((IUnknown *)(IMetaDataEmit2 *)this, &m_pFreeThreadedMarshaler)); + } + } + + _ASSERTE(m_pFreeThreadedMarshaler != NULL); + + IfFailGo(m_pFreeThreadedMarshaler->QueryInterface(riid, ppUnk)); + + // AddRef has happened in the QueryInterface and thus should just return + goto ErrExit; + } + else + { + IfFailGo(E_NOINTERFACE); + } + } +#endif // FEATURE_COMINTEROP +#ifdef FEATURE_PREJIT + else if (riid == IID_IMetaDataCorProfileData) + { + *ppUnk = (IMetaDataCorProfileData *)this; + } + else if (riid == IID_IMDInternalMetadataReorderingOptions) + { + *ppUnk = (IMDInternalMetadataReorderingOptions *)this; + } +#endif //FEATURE_PREJIT +#endif //FEATURE_METADATA_IN_VM + else + { + IfFailGo(E_NOINTERFACE); + } + + if (fIsInterfaceRW && IsOfReadOnly(m_OpenFlags)) + { + // They are asking for a read/write interface and this scope was + // opened as Read-Only + + *ppUnk = NULL; + IfFailGo(CLDB_E_INCOMPATIBLE); + } + + if (fIsInterfaceRW) + { + LOCKWRITENORET(); + + if (SUCCEEDED(hr)) + { + hr = m_pStgdb->m_MiniMd.ConvertToRW(); + } + + if (FAILED(hr)) + { + *ppUnk = NULL; + goto ErrExit; + } + } + + AddRef(); +ErrExit: + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::QueryInterface + + +//--------------------------------------------------------------------------------------- +// +// Returns the memory region of the mapped file and type of its mapping. The choice of the file mapping type +// for each scope is CLR implementation specific and user cannot explicitly set it. +// +// The memory is valid only as long as the underlying MetaData scope is opened (there's a reference to +// a MetaData interface for this scope). +// +// Implements public API code:IMetaDataInfo::GetFileMapping. +// +// Arguments: +// ppvData - Fills with pointer to the start of the mapped file. +// pcbData - Fills with the size of the mapped memory region (for flat-mapping it is the size of the +// file). +// pdwMappingType - Fills with type of file mapping (code:CorFileMapping). +// Current CLR implementation returns always code:fmFlat. The other value(s) are reserved for future +// usage. See code:StgIO::MapFileToMem#CreateFileMapping_SEC_IMAGE for more details. +// +// Return Value: +// S_OK - All output data are filled. +// COR_E_NOTSUPPORTED - CLR cannot (or doesn't want to) provide the memory region. +// This can happen when: +// - The MetaData scope was opened with flag code:ofWrite or code:ofCopyMemory. +// Note: code:ofCopyMemory could be supported in future CLR versions. For example if we change +// code:CLiteWeightStgdbRW::OpenForRead to copy whole file (or add a new flag ofCopyWholeFile). +// - The MetaData scope was opened without flag code:ofReadOnly. +// Note: We could support this API without code:ofReadOnly flag in future CLR versions. We just +// need some test coverage and user scenario for it. +// - Only MetaData part of the file was opened using code:OpenScopeOnMemory. +// - The file is not NT PE file (e.g. it is NT OBJ = .obj file produced by managed C++). +// E_INVALIDARG - NULL was passed as an argument value. +// +HRESULT +RegMeta::GetFileMapping( + const void ** ppvData, + ULONGLONG * pcbData, + DWORD * pdwMappingType) +{ + HRESULT hr = S_OK; + + if ((ppvData == NULL) || (pcbData == NULL) || (pdwMappingType == NULL)) + { + return E_INVALIDARG; + } + + // Note: Some of the following checks are duplicit (as some combinations are invalid and ensured by CLR + // implementation), but it is easier to check them all + + // OpenScope flags have to be (ofRead | ofReadOnly) and not ofCopyMemory + // (as code:CLiteWeightStgdbRW::OpenForRead will copy only the MetaData part of the file) + if (((m_OpenFlags & ofReadWriteMask) != ofRead) || + ((m_OpenFlags & ofReadOnly) == 0) || + ((m_OpenFlags & ofCopyMemory) != 0)) + { + IfFailGo(COR_E_NOTSUPPORTED); + } + // The file has to be NT PE file (not CLDB = managed C++ .obj file) and we have to have its full mapping + // (see code:CLiteWeightStgdbRW::OpenForRead) + if ((m_pStgdb->m_pImage == NULL) || + (m_pStgdb->m_dwImageSize == 0) || + (m_pStgdb->GetFileType() != FILETYPE_NTPE)) + { + IfFailGo(COR_E_NOTSUPPORTED); + } + if (m_pStgdb->m_pStgIO->GetFlags() != DBPROP_TMODEF_READ) + { + IfFailGo(COR_E_NOTSUPPORTED); + } + // The file has to be flat-mapped, or copied to memory (file mapping code:MTYPE_IMAGE is not currently + // supported - see code:StgIO::MapFileToMem#CreateFileMapping_SEC_IMAGE) + // Note: Only small files (<=64K) are copied to memory - see code:StgIO::MapFileToMem#CopySmallFiles + if ((m_pStgdb->m_pStgIO->GetMemoryMappedType() != MTYPE_FLAT) && + (m_pStgdb->m_pStgIO->GetMemoryMappedType() != MTYPE_NOMAPPING)) + { + IfFailGo(COR_E_NOTSUPPORTED); + } + // All necessary conditions are satisfied + + *ppvData = m_pStgdb->m_pImage; + *pcbData = m_pStgdb->m_dwImageSize; + // We checked that the file was flat-mapped above + *pdwMappingType = fmFlat; + +ErrExit: + if (FAILED(hr)) + { + *ppvData = NULL; + *pcbData = 0; + *pdwMappingType = 0; + } + + return hr; +} // RegMeta::GetFileMapping + + +//------------------------------------------------------------------------------ +// Metadata dump +// +#ifdef _DEBUG + +#define STRING_BUFFER_LEN 1024 +#define ENUM_BUFFER_SIZE 10 + +int DumpMD_Write(__in __in_z const char *str) +{ + OutputDebugStringA(str); + return 0; // strlen(str); +} // int DumpMD_Write() + +int DumpMD_WriteLine(__in __in_z const char *str) +{ + OutputDebugStringA(str); + OutputDebugStringA("\n"); + return 0; // strlen(str); +} // int DumpMD_Write() + +int DumpMD_VWriteMarker(__in __in_z const char *str, va_list marker) +{ + CQuickBytes m_output; + + int count = -1; + int i = 1; + HRESULT hr; + + while (count < 0) + { + if (FAILED(hr = m_output.ReSizeNoThrow(STRING_BUFFER_LEN * i))) + return 0; + va_list markerCopy; + va_copy(markerCopy, marker); + count = _vsnprintf_s((char *)m_output.Ptr(), STRING_BUFFER_LEN * i, _TRUNCATE, str, markerCopy); + va_end(markerCopy); + i *= 2; + } + OutputDebugStringA((LPCSTR)m_output.Ptr()); + return count; +} // int DumpMD_VWriteMarker() + +int DumpMD_VWrite(__in __in_z const char *str, ...) +{ + va_list marker; + int count; + + va_start(marker, str); + count = DumpMD_VWriteMarker(str, marker); + va_end(marker); + return count; +} // int DumpMD_VWrite() + +int DumpMD_VWriteLine(__in __in_z const char *str, ...) +{ + va_list marker; + int count; + + va_start(marker, str); + count = DumpMD_VWriteMarker(str, marker); + DumpMD_Write("\n"); + va_end(marker); + return count; +} // int DumpMD_VWriteLine() + + +const char *DumpMD_DumpRawNameOfType(RegMeta *pMD, ULONG iType) +{ + if (iType <= iRidMax) + { + const char *pNameTable; + pMD->GetTableInfo(iType, 0,0,0,0, &pNameTable); + return pNameTable; + } + else + // Is the field a coded token? + if (iType <= iCodedTokenMax) + { + int iCdTkn = iType - iCodedToken; + const char *pNameCdTkn; + pMD->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn); + return pNameCdTkn; + } + + // Fixed type. + switch (iType) + { + case iBYTE: + return "BYTE"; + case iSHORT: + return "short"; + case iUSHORT: + return "USHORT"; + case iLONG: + return "long"; + case iULONG: + return "ULONG"; + case iSTRING: + return "string"; + case iGUID: + return "GUID"; + case iBLOB: + return "blob"; + } + // default: + static char buf[30]; + sprintf_s(buf, NumItems(buf), "unknown type 0x%02x", iType); + return buf; +} // const char *DumpMD_DumpRawNameOfType() + +void DumpMD_DumpRawCol(RegMeta *pMD, ULONG ixTbl, ULONG ixCol, ULONG rid, bool bStats) +{ + ULONG ulType; // Type of a column. + ULONG ulVal; // Value of a column. + LPCUTF8 pString; // Pointer to a string. + const void *pBlob; // Pointer to a blob. + ULONG cb; // Size of something. + + pMD->GetColumn(ixTbl, ixCol, rid, &ulVal); + pMD->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0); + + if (ulType <= iRidMax) + { + const char *pNameTable; + pMD->GetTableInfo(ulType, 0,0,0,0, &pNameTable); + DumpMD_VWrite("%s[%x]", pNameTable, ulVal); + } + else + // Is the field a coded token? + if (ulType <= iCodedTokenMax) + { + int iCdTkn = ulType - iCodedToken; + const char *pNameCdTkn; + pMD->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn); + DumpMD_VWrite("%s[%08x]", pNameCdTkn, ulVal); + } + else + { + // Fixed type. + switch (ulType) + { + case iBYTE: + DumpMD_VWrite("%02x", ulVal); + break; + case iSHORT: + case iUSHORT: + DumpMD_VWrite("%04x", ulVal); + break; + case iLONG: + case iULONG: + DumpMD_VWrite("%08x", ulVal); + break; + case iSTRING: + DumpMD_VWrite("string#%x", ulVal); + if (bStats && ulVal) + { + pMD->GetString(ulVal, &pString); + cb = (ULONG) strlen(pString) + 1; + DumpMD_VWrite("(%d)", cb); + } + break; + case iGUID: + DumpMD_VWrite("guid#%x", ulVal); + if (bStats && ulVal) + { + DumpMD_VWrite("(16)"); + } + break; + case iBLOB: + DumpMD_VWrite("blob#%x", ulVal); + if (bStats && ulVal) + { + pMD->GetBlob(ulVal, &cb, &pBlob); + cb += 1; + if (cb > 128) + cb += 1; + if (cb > 16535) + cb += 1; + DumpMD_VWrite("(%d)", cb); + } + break; + default: + DumpMD_VWrite("unknown type 0x%04x", ulVal); + break; + } + } +} // void DumpMD_DumpRawCol() + +ULONG DumpMD_DumpRawColStats(RegMeta *pMD, ULONG ixTbl, ULONG ixCol, ULONG cRows) +{ + ULONG rslt = 0; + ULONG ulType; // Type of a column. + ULONG ulVal; // Value of a column. + LPCUTF8 pString; // Pointer to a string. + const void *pBlob; // Pointer to a blob. + ULONG cb; // Size of something. + + pMD->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0); + + if (IsHeapType(ulType)) + { + for (ULONG rid=1; rid<=cRows; ++rid) + { + pMD->GetColumn(ixTbl, ixCol, rid, &ulVal); + // Fixed type. + switch (ulType) + { + case iSTRING: + if (ulVal) + { + pMD->GetString(ulVal, &pString); + cb = (ULONG) strlen(pString); + rslt += cb + 1; + } + break; + case iGUID: + if (ulVal) + rslt += 16; + break; + case iBLOB: + if (ulVal) + { + pMD->GetBlob(ulVal, &cb, &pBlob); + rslt += cb + 1; + if (cb > 128) + rslt += 1; + if (cb > 16535) + rslt += 1; + } + break; + default: + break; + } + } + } + return rslt; +} // ULONG DumpMD_DumpRawColStats() + +int DumpMD_DumpHex( + const char *szPrefix, // String prefix for first line. + const void *pvData, // The data to print. + ULONG cbData, // Bytes of data to print. + int bText=1, // If true, also dump text. + ULONG nLine=16) // Bytes per line to print. +{ + const BYTE *pbData = static_cast<const BYTE*>(pvData); + ULONG i; // Loop control. + ULONG nPrint; // Number to print in an iteration. + ULONG nSpace; // Spacing calculations. + ULONG nPrefix; // Size of the prefix. + ULONG nLines=0; // Number of lines printed. + const char *pPrefix; // For counting spaces in the prefix. + + // Round down to 8 characters. + nLine = nLine & ~0x7; + + for (nPrefix=0, pPrefix=szPrefix; *pPrefix; ++pPrefix) + { + if (*pPrefix == '\t') + nPrefix = (nPrefix + 8) & ~7; + else + ++nPrefix; + } + //nPrefix = strlen(szPrefix); + do + { // Write the line prefix. + if (szPrefix) + DumpMD_VWrite("%s:", szPrefix); + else + DumpMD_VWrite("%*s:", nPrefix, ""); + szPrefix = 0; + ++nLines; + + // Calculate spacing. + nPrint = min(cbData, nLine); + nSpace = nLine - nPrint; + + // dump in hex. + for(i=0; i<nPrint; i++) + { + if ((i&7) == 0) + DumpMD_Write(" "); + DumpMD_VWrite("%02x ", pbData[i]); + } + if (bText) + { + // Space out to the text spot. + if (nSpace) + DumpMD_VWrite("%*s", nSpace*3+nSpace/8, ""); + // Dump in text. + DumpMD_Write(">"); + for(i=0; i<nPrint; i++) + DumpMD_VWrite("%c", (isprint(pbData[i])) ? pbData[i] : ' '); + // Space out the text, and finish the line. + DumpMD_VWrite("%*s<", nSpace, ""); + } + DumpMD_VWriteLine(""); + + // Next data to print. + cbData -= nPrint; + pbData += nPrint; + } + while (cbData > 0); + + return nLines; +} // int DumpMD_DumpHex() + +void DumpMD_DisplayUserStrings( + RegMeta *pMD) // The scope to dump. +{ + HCORENUM stringEnum = NULL; // string enumerator. + mdString Strings[ENUM_BUFFER_SIZE]; // String tokens from enumerator. + CQuickArray<WCHAR> rUserString; // Buffer to receive string. + WCHAR *szUserString; // Working pointer into buffer. + ULONG chUserString; // Size of user string. + CQuickArray<char> rcBuf; // Buffer to hold the BLOB version of the string. + char *szBuf; // Working pointer into buffer. + ULONG chBuf; // Saved size of the user string. + ULONG count; // Items returned from enumerator. + ULONG totalCount = 1; // Running count of strings. + bool bUnprint = false; // Is an unprintable character found? + HRESULT hr; // A result. + while (SUCCEEDED(hr = pMD->EnumUserStrings( &stringEnum, + Strings, NumItems(Strings), &count)) && + count > 0) + { + if (totalCount == 1) + { // If only one, it is the NULL string, so don't print it. + DumpMD_WriteLine("User Strings"); + DumpMD_WriteLine("-------------------------------------------------------"); + } + for (ULONG i = 0; i < count; i++, totalCount++) + { + do { // Try to get the string into the existing buffer. + hr = pMD->GetUserString( Strings[i], rUserString.Ptr(),(ULONG32)rUserString.MaxSize(), &chUserString); + if (hr == CLDB_S_TRUNCATION) + { // Buffer wasn't big enough, try to enlarge it. + if (FAILED(rUserString.ReSizeNoThrow(chUserString))) + DumpMD_VWriteLine("malloc failed: %#8x.", E_OUTOFMEMORY); + continue; + } + } while (0); + if (FAILED(hr)) DumpMD_VWriteLine("GetUserString failed: %#8x.", hr); + + szUserString = rUserString.Ptr(); + chBuf = chUserString; + + DumpMD_VWrite("%08x : (%2d) L\"", Strings[i], chUserString); + while (chUserString) + { + switch (*szUserString) + { + case 0: + DumpMD_Write("\\0"); break; + case W('\r'): + DumpMD_Write("\\r"); break; + case W('\n'): + DumpMD_Write("\\n"); break; + case W('\t'): + DumpMD_Write("\\t"); break; + default: + if (iswprint(*szUserString)) + DumpMD_VWrite("%lc", *szUserString); + else + { + bUnprint = true; + DumpMD_Write("."); + } + break; + } + ++szUserString; + --chUserString; + } + DumpMD_WriteLine("\""); + + // Print the user string as a blob if an unprintable character is found. + if (bUnprint) + { + bUnprint = false; + szUserString = rUserString.Ptr(); + // REVISIT_TODO: ReSizeNoThrow can fail. Check its return value and add an error path. + rcBuf.ReSizeNoThrow(81); //(chBuf * 5 + 1); + szBuf = rcBuf.Ptr(); + ULONG j,k; + DumpMD_WriteLine("\t\tUser string has unprintables, hex format below:"); + for (j = 0,k=0; j < chBuf; j++) + { + // See rcBuf.ResSizeNoThrow(81) above + sprintf_s (&szBuf[k*5],81-(k*5), "%04x ", szUserString[j]); + k++; + if((k==16)||(j == (chBuf-1))) + { + szBuf[k*5] = '\0'; + DumpMD_VWriteLine("\t\t%s", szBuf); + k=0; + } + } + } + } + } + if (stringEnum) + pMD->CloseEnum(stringEnum); +} // void MDInfo::DisplayUserStrings() + +void DumpMD_DumpRawHeaps( + RegMeta *pMD) // The scope to dump. +{ + HRESULT hr; // A result. + ULONG ulSize; // Bytes in a heap. + const BYTE *pData; // Pointer to a blob. + ULONG cbData; // Size of a blob. + ULONG oData; // Offset of current blob. + char rcPrefix[30]; // To format line prefix. + + pMD->GetBlobHeapSize(&ulSize); + DumpMD_VWriteLine(""); + DumpMD_VWriteLine("Blob Heap: %d(%#x) bytes", ulSize,ulSize); + oData = 0; + do + { + pMD->GetBlob(oData, &cbData, (const void**)&pData); + sprintf_s(rcPrefix, NumItems(rcPrefix), "%5x,%-2x", oData, cbData); + DumpMD_DumpHex(rcPrefix, pData, cbData); + hr = pMD->GetNextBlob(oData, &oData); + } + while (hr == S_OK); + + pMD->GetStringHeapSize(&ulSize); + DumpMD_VWriteLine(""); + DumpMD_VWriteLine("String Heap: %d(%#x) bytes", ulSize,ulSize); + oData = 0; + const char *pString; + do + { + pMD->GetString(oData, &pString); + sprintf_s(rcPrefix, NumItems(rcPrefix), "%08x", oData); + DumpMD_DumpHex(rcPrefix, pString, (ULONG)strlen(pString)+1); + if (*pString != 0) + DumpMD_VWrite("%08x: %s\n", oData, pString); + hr = pMD->GetNextString(oData, &oData); + } + while (hr == S_OK); + DumpMD_VWriteLine(""); + + DumpMD_DisplayUserStrings(pMD); + +} // void DumpMD_DumpRawHeaps() + + +void DumpMD_DumpRaw(RegMeta *pMD, int iDump, bool bStats) +{ + ULONG cTables; // Tables in the database. + ULONG cCols; // Columns in a table. + ULONG cRows; // Rows in a table. + ULONG cbRow; // Bytes in a row of a table. + ULONG iKey; // Key column of a table. + const char *pNameTable; // Name of a table. + ULONG oCol; // Offset of a column. + ULONG cbCol; // Size of a column. + ULONG ulType; // Type of a column. + const char *pNameColumn; // Name of a column. + ULONG ulSize; + + pMD->GetNumTables(&cTables); + + pMD->GetStringHeapSize(&ulSize); + DumpMD_VWrite("Strings: %d(%#x)", ulSize, ulSize); + pMD->GetBlobHeapSize(&ulSize); + DumpMD_VWrite(", Blobs: %d(%#x)", ulSize, ulSize); + pMD->GetGuidHeapSize(&ulSize); + DumpMD_VWrite(", Guids: %d(%#x)", ulSize, ulSize); + pMD->GetUserStringHeapSize(&ulSize); + DumpMD_VWriteLine(", User strings: %d(%#x)", ulSize, ulSize); + + for (ULONG ixTbl = 0; ixTbl < cTables; ++ixTbl) + { + pMD->GetTableInfo(ixTbl, &cbRow, &cRows, &cCols, &iKey, &pNameTable); + + if (cRows == 0 && iDump < 3) + continue; + + if (iDump >= 2) + DumpMD_VWriteLine("================================================="); + DumpMD_VWriteLine("%2d: %-20s cRecs:%5d(%#x), cbRec:%3d(%#x), cbTable:%6d(%#x)", + ixTbl, pNameTable, cRows, cRows, cbRow, cbRow, cbRow * cRows, cbRow * cRows); + + if (iDump < 2) + continue; + + // Dump column definitions for the table. + ULONG ixCol; + for (ixCol=0; ixCol<cCols; ++ixCol) + { + pMD->GetColumnInfo(ixTbl, ixCol, &oCol, &cbCol, &ulType, &pNameColumn); + + DumpMD_VWrite(" col %2x:%c %-12s oCol:%2x, cbCol:%x, %-7s", + ixCol, ((ixCol==iKey)?'*':' '), pNameColumn, oCol, cbCol, DumpMD_DumpRawNameOfType(pMD, ulType)); + + if (bStats) + { + ulSize = DumpMD_DumpRawColStats(pMD, ixTbl, ixCol, cRows); + if (ulSize) + DumpMD_VWrite("(%d)", ulSize); + } + DumpMD_VWriteLine(""); + } + + if (iDump < 3) + continue; + + // Dump the rows. + for (ULONG rid = 1; rid <= cRows; ++rid) + { + if (rid == 1) + DumpMD_VWriteLine("-------------------------------------------------"); + DumpMD_VWrite(" %3x == ", rid); + for (ixCol=0; ixCol < cCols; ++ixCol) + { + if (ixCol) DumpMD_VWrite(", "); + DumpMD_VWrite("%d:", ixCol); + DumpMD_DumpRawCol(pMD, ixTbl, ixCol, rid, bStats); + } + DumpMD_VWriteLine(""); + } + } + + DumpMD_DumpRawHeaps(pMD); + +} // void DumpMD_DumpRaw() + + +int DumpMD_impl(RegMeta *pMD) +{ + DumpMD_DumpRaw(pMD, 3, false); + return 0; +} + +int DumpMD(UINT_PTR iMD) +{ + RegMeta *pMD = reinterpret_cast<RegMeta*>(iMD); + return DumpMD_impl(pMD); +} + +#endif //_DEBUG + +//***************************************************************************** +// Using the existing RegMeta and reopen with another chuck of memory. Make sure that all stgdb +// is still kept alive. +//***************************************************************************** +HRESULT RegMeta::ReOpenWithMemory( + LPCVOID pData, // [in] Location of scope data. + ULONG cbData, // [in] Size of the data pointed to by pData. + DWORD dwReOpenFlags) // [in] ReOpen flags +{ + HRESULT hr = NOERROR; + + // Only allow the ofCopyMemory and ofTakeOwnership flags + if (dwReOpenFlags != 0 && ((dwReOpenFlags & (~(ofCopyMemory|ofTakeOwnership))) > 0)) + return E_INVALIDARG; + + LOCKWRITE(); + + + // put the current m_pStgdb to the free list + m_pStgdb->m_pNextStgdb = m_pStgdbFreeList; + m_pStgdbFreeList = m_pStgdb; + m_pStgdb = new (nothrow) CLiteWeightStgdbRW; + IfNullGo( m_pStgdb ); + IfFailGo( OpenExistingMD(0 /* szFileName */, const_cast<void*>(pData), cbData, ofReOpen|dwReOpenFlags /* flags */) ); + +#ifdef FEATURE_METADATA_INTERNAL_APIS + // We've created a new Stgdb, but may still have an Internal Importer hanging around accessing the old Stgdb. + // The free list ensures we don't have a dangling pointer, but the + // If we have a corresponding InternalInterface, need to clear it because it's now using stale data. + // Others will need to update their Internal interface to get the new data. + { + HRESULT hrIgnore = SetCachedInternalInterface(NULL); + (void)hrIgnore; //prevent "unused variable" error from GCC + _ASSERTE(hrIgnore == NOERROR); // clearing the cached interface should always succeed. + } +#endif //FEATURE_METADATA_INTERNAL_APIS + + // we are done! +ErrExit: + if (FAILED(hr)) + { + // recover to the old state + if (m_pStgdb) + delete m_pStgdb; + m_pStgdb = m_pStgdbFreeList; + m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb; + } +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN + else + { + if( !(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_MD_PreserveDebuggerMetadataMemory)) && IsSafeToDeleteStgdb()) + { + // now that success is assured, delete the old block of memory + // This isn't normally a safe operation because we would have given out + // internal pointers to the memory. However when this feature is enabled + // we track calls that might have given out internal pointers. If none + // of the APIs were ever called then we can safely delete. + CLiteWeightStgdbRW* pStgdb = m_pStgdbFreeList; + m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb; + delete pStgdb; + } + + MarkSafeToDeleteStgdb(); // As of right now, no APIs have given out internal pointers + // to the newly allocated stgdb + } +#endif + + return hr; +} // RegMeta::ReOpenWithMemory + + +//***************************************************************************** +// This function returns the requested public interface based on the given +// internal import interface. +// A common path to call this is updating the matedata for dynamic modules. +//***************************************************************************** +STDAPI MDReOpenMetaDataWithMemoryEx( + void *pImport, // [IN] Given scope. public interfaces + LPCVOID pData, // [in] Location of scope data. + ULONG cbData, // [in] Size of the data pointed to by pData. + DWORD dwReOpenFlags) // [in] Flags for ReOpen +{ + HRESULT hr = S_OK; + IUnknown *pUnk = (IUnknown *) pImport; + IMetaDataImport2 *pMDImport = NULL; + RegMeta *pRegMeta = NULL; + + _ASSERTE(pImport); + + IfFailGo( pUnk->QueryInterface(IID_IMetaDataImport2, (void **) &pMDImport) ); + pRegMeta = (RegMeta*) pMDImport; + + IfFailGo( pRegMeta->ReOpenWithMemory(pData, cbData, dwReOpenFlags) ); + +ErrExit: + if (pMDImport) + pMDImport->Release(); + + return hr; +} // MDReOpenMetaDataWithMemoryEx + +STDAPI MDReOpenMetaDataWithMemory( + void *pImport, // [IN] Given scope. public interfaces + LPCVOID pData, // [in] Location of scope data. + ULONG cbData) // [in] Size of the data pointed to by pData. +{ + return MDReOpenMetaDataWithMemoryEx(pImport, pData, cbData, 0); +} + +// -------------------------------------------------------------------------------------- +// +// Zeros used by public APIs as return value (or pointer to this memory) for invalid input. +// It is used by methods: +// * code:RegMeta::GetPublicApiCompatibilityZeros, and +// * code:RegMeta::GetPublicApiCompatibilityZerosOfSize. +// +const BYTE +RegMeta::s_rgMetaDataPublicApiCompatibilityZeros[64] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// -------------------------------------------------------------------------------------- +// +// Returns pointer to zeros of size (cbSize). +// Used by public APIs to return compatible values with previous releases. +// +const BYTE * +RegMeta::GetPublicApiCompatibilityZerosOfSize(UINT32 cbSize) +{ + if (cbSize <= sizeof(s_rgMetaDataPublicApiCompatibilityZeros)) + { + return s_rgMetaDataPublicApiCompatibilityZeros; + } + _ASSERTE(!"Dangerous call to this method! Reconsider fixing the caller."); + return NULL; +} // RegMeta::GetPublicApiCompatibilityZerosOfSize + + + + +// +// returns the "built for" version of a metadata scope. +// +HRESULT RegMeta::GetVersionString( // S_OK or error. + LPCSTR *pVer) // [OUT] Put version string here. +{ + _ASSERTE(pVer != NULL); + HRESULT hr; + LOCKREAD(); +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + if (m_pStgdb->m_pvMd != NULL) + { +#endif + *pVer = reinterpret_cast<const char*>(reinterpret_cast<const STORAGESIGNATURE*>(m_pStgdb->m_pvMd)->pVersion); +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + } + else + { + //This emptry string matches the fallback behavior we have in other places that query the version string. + *pVer = ""; + } +#endif + hr = S_OK; + ErrExit: + return hr; +} + +#ifdef FEATURE_METADATA_INTERNAL_APIS +//***************************************************************************** +// IGetIMDInternalImport methods +//***************************************************************************** +HRESULT RegMeta::GetIMDInternalImport( + IMDInternalImport ** ppIMDInternalImport // [OUT] Buffer to receive IMDInternalImport* + ) +{ + HRESULT hr = S_OK; + MDInternalRW *pInternalRW = NULL; + bool isLockedForWrite = false; + IUnknown *pIUnkInternal = NULL; + IUnknown *pThis = (IMetaDataImport2*)this; + + pIUnkInternal = this->GetCachedInternalInterface(TRUE); + if (pIUnkInternal) + { + // there is already a cached Internal interface. GetCachedInternalInterface does add ref the + // returned interface + IfFailGo(pIUnkInternal->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport)); + goto ErrExit; + } + + if (this->IsThreadSafetyOn()) + { + _ASSERTE( this->GetReaderWriterLock() ); + IfFailGo(this->GetReaderWriterLock()->LockWrite()); + isLockedForWrite = true; + } + + // check again. Maybe someone else beat us to setting the internal interface while we are waiting + // for the write lock. Don't need to grab the read lock since we already have the write lock. + pIUnkInternal = this->GetCachedInternalInterface(FALSE); + if (pIUnkInternal) + { + // there is already a cached Internal interface. GetCachedInternalInterface does add ref the + // returned interface + IfFailGo(pIUnkInternal->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport)); + goto ErrExit; + } + + // now create the compressed object + IfNullGo( pInternalRW = new (nothrow) MDInternalRW ); + IfFailGo( pInternalRW->InitWithStgdb(pThis, this->GetMiniStgdb() ) ); + + // make the public object and the internal object point to each other. + _ASSERTE( pInternalRW->GetReaderWriterLock() == NULL && + (! this->IsThreadSafetyOn() || this->GetReaderWriterLock() != NULL )); + IfFailGo( this->SetCachedInternalInterface(static_cast<IMDInternalImportENC*>(pInternalRW)) ); + IfFailGo( pInternalRW->SetCachedPublicInterface(pThis)); + IfFailGo( pInternalRW->SetReaderWriterLock(this->GetReaderWriterLock() )); + IfFailGo( pInternalRW->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport)); + +ErrExit: + if (isLockedForWrite == true) + this->GetReaderWriterLock()->UnlockWrite(); + if (pIUnkInternal) + pIUnkInternal->Release(); + if (pInternalRW) + pInternalRW->Release(); + if (FAILED(hr)) + { + if (ppIMDInternalImport) + *ppIMDInternalImport = 0; + } + return hr; +} +#endif //FEATURE_METADATA_INTERNAL_APIS + diff --git a/src/coreclr/md/compiler/regmeta.h b/src/coreclr/md/compiler/regmeta.h new file mode 100644 index 00000000000..82be41bbb47 --- /dev/null +++ b/src/coreclr/md/compiler/regmeta.h @@ -0,0 +1,2116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RegMeta.h +// + +// +// This is the code for the MetaData coclass including both the emit and +// import API's. +// +// This provides an implementation of the public Metadata interfaces via the RegMeta class. It's +// primarily intended for use by tools such as compilers, debuggers, and profilers. +//***************************************************************************** +#ifndef __RegMeta__h__ +#define __RegMeta__h__ + +#include <metamodelrw.h> +#include "../inc/mdlog.h" +#include "utsem.h" + +#include "rwutil.h" +#include "mdperf.h" + +#include "sigparser.h" + +class FilterManager; + +// Support for symbol binding meta data. This is a custom value hung off of +// the Module entry. The CORDBG_SYMBOL_URL needs to be allocated on top of +// a buffer large enough to hold it. +// +#define SZ_CORDBG_SYMBOL_URL W("DebugSymbolUrlData") + +struct CORDBG_SYMBOL_URL +{ + GUID FormatID; // ID of the format type. + WCHAR rcName[2]; // Variable sized name of the item. + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:6305) // "Potential mismatch between sizeof and countof quantities" +#endif + + ULONG Size() const + { + return (ULONG)(sizeof(GUID) + ((wcslen(rcName) + 1) * 2)); + } + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif +}; + + +// +// Internal open flags. +// +#define ofExternalStgDB ofReserved1 +#define IsOfExternalStgDB(x) ((x) & ofExternalStgDB) +#define ofReOpen ofReserved2 +#define IsOfReOpen(x) ((x) & ofReOpen) + + +// Set API caller type +enum SetAPICallerType +{ + DEFINE_API = 0x1, + EXTERNAL_CALLER = 0x2 +}; + +// Define the record entry for the table over which ValidateMetaData iterates over. +// Add a forward declaration for RegMeta. +class RegMeta; +typedef HRESULT (RegMeta::*ValidateRecordFunction)(RID); + +// Support for security attributes. Bundles of attributes (they look much like +// custom attributes) are passed into a single API (DefineSecurityAttributeSet) +// where they're processed and written into the metadata as one or more opaque +// blobs of data. +struct CORSEC_ATTR +{ + CORSEC_ATTR *pNext; // Next structure in list or NULL. + mdToken tkObj; // The object to put the value on. + mdMemberRef tkCtor; // The security attribute constructor. + mdTypeRef tkTypeRef; // Ref to the security attribute type. + mdAssemblyRef tkAssemblyRef; // Ref to the assembly containing the security attribute class. + void const *pCustomAttribute; // The custom value data. + ULONG cbCustomAttribute; // The custom value data length. +}; + +// Support for "Pseudo Custom Attributes". +struct CCustAttrHashKey +{ + mdToken tkType; // Token of the custom attribute type. + int ca; // flag indicating what the ca is. +}; + +class CCustAttrHash : public CClosedHashEx<CCustAttrHashKey, CCustAttrHash> +{ + typedef CCustAttrHashKey T; + + using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::Hash; + using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::Compare; + using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::Status; + using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::SetStatus; + using CClosedHashEx<CCustAttrHashKey, CCustAttrHash>::GetKey; + +public: + CCustAttrHash(int iBuckets=37) : CClosedHashEx<CCustAttrHashKey,CCustAttrHash>(iBuckets) {} + unsigned int Hash(const T *pData); + unsigned int Compare(const T *p1, T *p2); + ELEMENTSTATUS Status(T *pEntry); + void SetStatus(T *pEntry, ELEMENTSTATUS s); + void* GetKey(T *pEntry); +}; + +class MDInternalRW; +struct CaArg; +struct CaNamedArg; + +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN +#define REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED() MarkUnsafeToDeleteStgdb() +#else +#define REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED() +#endif + +// The RegMeta class implements the public metadata interfaces. +// +// Notes: +// This object is primarily consumed by tools, not the runtime itself. (The runtime should be +// using the internal metadata interfaces, not the public ones implemented here). +// The VM uses it for IMetaDataEmit* interfaces. Define* methods are not exposed to the VM +// (eg for Reflection) otherwise. +// This object is a thin veneer exposing a public interface on top of the real storage. +// The runtime also has internal interfaces to access that storage. +// +// This is included outside of md\compiler; so be very careful about using #ifdef to change class layout +// (adding removing interfaces changes class layout) +// +// This exists in both the full and standalone versions of the metadata. +// + +class RegMeta : + public IMetaDataImport2, + public IMetaDataAssemblyImport, + public IMetaDataTables2 + + , public IMetaDataInfo + +#ifdef FEATURE_METADATA_EMIT +#ifndef FEATURE_METADATA_EMIT_PORTABLE_PDB + , public IMetaDataEmit2 +#else + , public IMetaDataEmit3 +#endif + , public IMetaDataAssemblyEmit +#endif + +#ifdef FEATURE_METADATA_EMIT_ALL + , public IMetaDataFilter +#endif + +#ifdef FEATURE_METADATA_INTERNAL_APIS + , public IMetaDataHelper + , public IMDInternalEmit + , public IGetIMDInternalImport +#endif + +#if defined(FEATURE_METADATA_EMIT) && defined(FEATURE_METADATA_INTERNAL_APIS) + , public IMetaDataEmitHelper +#endif + +#if defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT) + , public IMetaDataCorProfileData + , public IMDInternalMetadataReorderingOptions +#endif + , public IMDCommon +{ + friend class CImportTlb; + friend class MDInternalRW; + friend class MDInternalRO; + friend HRESULT TranslateSigHelper( + IMDInternalImport* pImport, + IMDInternalImport* pAssemImport, + const void* pbHashValue, + ULONG cbHashValue, + PCCOR_SIGNATURE pbSigBlob, + ULONG cbSigBlob, + IMetaDataAssemblyEmit* pAssemEmit, + IMetaDataEmit* emit, + CQuickBytes* pqkSigEmit, + ULONG* pcbSig); +public: +//***************************************************************************** +// IUnknown methods +//***************************************************************************** + STDMETHODIMP QueryInterface(REFIID riid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + +//***************************************************************************** +// IMetaDataImport methods +//***************************************************************************** + void STDMETHODCALLTYPE CloseEnum(HCORENUM hEnum); + STDMETHODIMP CountEnum(HCORENUM hEnum, ULONG *pulCount); + STDMETHODIMP ResetEnum(HCORENUM hEnum, ULONG ulPos); + STDMETHODIMP EnumTypeDefs(HCORENUM *phEnum, mdTypeDef rTypeDefs[], + ULONG cMax, ULONG *pcTypeDefs); + STDMETHODIMP EnumInterfaceImpls(HCORENUM *phEnum, mdTypeDef td, + mdInterfaceImpl rImpls[], ULONG cMax, + ULONG* pcImpls); + STDMETHODIMP EnumTypeRefs(HCORENUM *phEnum, mdTypeRef rTypeRefs[], + ULONG cMax, ULONG* pcTypeRefs); + STDMETHODIMP FindTypeDefByName( // S_OK or error. + LPCWSTR szTypeDef, // [IN] Name of the Type. + mdToken tdEncloser, // [IN] TypeDef/TypeRef of Enclosing class. + mdTypeDef *ptd); // [OUT] Put the TypeDef token here. + + STDMETHODIMP GetScopeProps( // S_OK or error. + __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Put name here. + ULONG cchName, // [IN] Size of name buffer in wide chars. + ULONG *pchName, // [OUT] Put size of name (wide chars) here. + GUID *pmvid); // [OUT] Put MVID here. + + STDMETHODIMP GetModuleFromScope( // S_OK. + mdModule *pmd); // [OUT] Put mdModule token here. + + STDMETHODIMP GetTypeDefProps( // S_OK or error. + mdTypeDef td, // [IN] TypeDef token for inquiry. + __out_ecount_opt (cchTypeDef) LPWSTR szTypeDef, // [OUT] Put name here. + ULONG cchTypeDef, // [IN] size of name buffer in wide chars. + ULONG *pchTypeDef, // [OUT] put size of name (wide chars) here. + DWORD *pdwTypeDefFlags, // [OUT] Put flags here. + mdToken *ptkExtends); // [OUT] Put base class TypeDef/TypeRef here. + + STDMETHODIMP GetInterfaceImplProps( // S_OK or error. + mdInterfaceImpl iiImpl, // [IN] InterfaceImpl token. + mdTypeDef *pClass, // [OUT] Put implementing class token here. + mdToken *ptkIface); // [OUT] Put implemented interface token here. + + STDMETHODIMP GetTypeRefProps( + mdTypeRef tr, // S_OK or error. + mdToken *ptkResolutionScope, // [OUT] Resolution scope, mdModuleRef or mdAssemblyRef. + __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Name buffer. + ULONG cchName, // [IN] Size of Name buffer. + ULONG *pchName); // [OUT] Actual size of Name. + + // This access global shared state to looks across multiple metadata scopes that would + // otherwise be independent. + STDMETHODIMP ResolveTypeRef(mdTypeRef tr, REFIID riid, IUnknown **ppIScope, mdTypeDef *ptd); + + STDMETHODIMP EnumMembers( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + mdToken rMembers[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumMembersWithName( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + LPCWSTR szName, // [IN] Limit results to those with this name. + mdToken rMembers[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumMethods( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + mdMethodDef rMethods[], // [OUT] Put MethodDefs here. + ULONG cMax, // [IN] Max MethodDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumMethodsWithName( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + LPCWSTR szName, // [IN] Limit results to those with this name. + mdMethodDef rMethods[], // [OU] Put MethodDefs here. + ULONG cMax, // [IN] Max MethodDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumFields( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + mdFieldDef rFields[], // [OUT] Put FieldDefs here. + ULONG cMax, // [IN] Max FieldDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumFieldsWithName( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef cl, // [IN] TypeDef to scope the enumeration. + LPCWSTR szName, // [IN] Limit results to those with this name. + mdFieldDef rFields[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + + STDMETHODIMP EnumParams( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdMethodDef mb, // [IN] MethodDef to scope the enumeration. + mdParamDef rParams[], // [OUT] Put ParamDefs here. + ULONG cMax, // [IN] Max ParamDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumMemberRefs( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken tkParent, // [IN] Parent token to scope the enumeration. + mdMemberRef rMemberRefs[], // [OUT] Put MemberRefs here. + ULONG cMax, // [IN] Max MemberRefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumMethodImpls( // S_OK, S_FALSE, or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdToken rMethodBody[], // [OUT] Put Method Body tokens here. + mdToken rMethodDecl[], // [OUT] Put Method Declaration tokens here. + ULONG cMax, // [IN] Max tokens to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumPermissionSets( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken tk, // [IN] if !NIL, token to scope the enumeration. + DWORD dwActions, // [IN] if !0, return only these actions. + mdPermission rPermission[], // [OUT] Put Permissions here. + ULONG cMax, // [IN] Max Permissions to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP FindMember( + mdTypeDef td, // [IN] given typedef + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdToken *pmb); // [OUT] matching memberdef + + STDMETHODIMP FindMethod( + mdTypeDef td, // [IN] given typedef + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodDef *pmb); // [OUT] matching memberdef + + STDMETHODIMP FindField( + mdTypeDef td, // [IN] given typedef + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdFieldDef *pmb); // [OUT] matching memberdef + + STDMETHODIMP FindMemberRef( + mdTypeRef td, // [IN] given typeRef + LPCWSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMemberRef *pmr); // [OUT] matching memberref + + STDMETHODIMP GetMethodProps( + mdMethodDef mb, // The method for which to get props. + mdTypeDef *pClass, // Put method's class here. + __out_ecount_opt (cchMethod) LPWSTR szMethod, // Put method's name here. + ULONG cchMethod, // Size of szMethod buffer in wide chars. + ULONG *pchMethod, // Put actual size here + DWORD *pdwAttr, // Put flags here. + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob, // [OUT] actual size of signature blob + ULONG *pulCodeRVA, // [OUT] codeRVA + DWORD *pdwImplFlags); // [OUT] Impl. Flags + + STDMETHODIMP GetMemberRefProps( // S_OK or error. + mdMemberRef mr, // [IN] given memberref + mdToken *ptk, // [OUT] Put classref or classdef here. + __out_ecount_opt (cchMember) LPWSTR szMember, // [OUT] buffer to fill for member's name + ULONG cchMember, // [IN] the count of char of szMember + ULONG *pchMember, // [OUT] actual count of char in member name + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to meta data blob value + ULONG *pbSig); // [OUT] actual size of signature blob + + STDMETHODIMP EnumProperties( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdProperty rProperties[], // [OUT] Put Properties here. + ULONG cMax, // [IN] Max properties to put. + ULONG *pcProperties); // [OUT] Put # put here. + + STDMETHODIMP EnumEvents( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdTypeDef td, // [IN] TypeDef to scope the enumeration. + mdEvent rEvents[], // [OUT] Put events here. + ULONG cMax, // [IN] Max events to put. + ULONG *pcEvents); // [OUT] Put # put here. + + STDMETHODIMP GetEventProps( // S_OK, S_FALSE, or error. + mdEvent ev, // [IN] event token + mdTypeDef *pClass, // [OUT] typedef containing the event declarion. + LPCWSTR szEvent, // [OUT] Event name + ULONG cchEvent, // [IN] the count of wchar of szEvent + ULONG *pchEvent, // [OUT] actual count of wchar for event's name + DWORD *pdwEventFlags, // [OUT] Event flags. + mdToken *ptkEventType, // [OUT] EventType class + mdMethodDef *pmdAddOn, // [OUT] AddOn method of the event + mdMethodDef *pmdRemoveOn, // [OUT] RemoveOn method of the event + mdMethodDef *pmdFire, // [OUT] Fire method of the event + mdMethodDef rmdOtherMethod[], // [OUT] other method of the event + ULONG cMax, // [IN] size of rmdOtherMethod + ULONG *pcOtherMethod); // [OUT] total number of other method of this event + + STDMETHODIMP EnumMethodSemantics( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdMethodDef mb, // [IN] MethodDef to scope the enumeration. + mdToken rEventProp[], // [OUT] Put Event/Property here. + ULONG cMax, // [IN] Max properties to put. + ULONG *pcEventProp); // [OUT] Put # put here. + + STDMETHODIMP GetMethodSemantics( // S_OK, S_FALSE, or error. + mdMethodDef mb, // [IN] method token + mdToken tkEventProp, // [IN] event/property token. + DWORD *pdwSemanticsFlags); // [OUT] the role flags for the method/propevent pair + + STDMETHODIMP GetClassLayout( + mdTypeDef td, // [IN] give typedef + DWORD *pdwPackSize, // [OUT] 1, 2, 4, 8, or 16 + COR_FIELD_OFFSET rFieldOffset[], // [OUT] field offset array + ULONG cMax, // [IN] size of the array + ULONG *pcFieldOffset, // [OUT] needed array size + ULONG *pulClassSize); // [OUT] the size of the class + + STDMETHODIMP GetFieldMarshal( + mdToken tk, // [IN] given a field's memberdef + PCCOR_SIGNATURE *ppvNativeType, // [OUT] native type of this field + ULONG *pcbNativeType); // [OUT] the count of bytes of *ppvNativeType + + STDMETHODIMP GetRVA( // S_OK or error. + mdToken tk, // Member for which to set offset + ULONG *pulCodeRVA, // The offset + DWORD *pdwImplFlags); // the implementation flags + + STDMETHODIMP GetPermissionSetProps( + mdPermission pm, // [IN] the permission token. + DWORD *pdwAction, // [OUT] CorDeclSecurity. + void const **ppvPermission, // [OUT] permission blob. + ULONG *pcbPermission); // [OUT] count of bytes of pvPermission. + + STDMETHODIMP GetSigFromToken( // S_OK or error. + mdSignature mdSig, // [IN] Signature token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token. + ULONG *pcbSig); // [OUT] return size of signature. + + STDMETHODIMP GetModuleRefProps( // S_OK or error. + mdModuleRef mur, // [IN] moduleref token. + __out_ecount_opt (cchName) LPWSTR szName, // [OUT] buffer to fill with the moduleref name. + ULONG cchName, // [IN] size of szName in wide characters. + ULONG *pchName); // [OUT] actual count of characters in the name. + + STDMETHODIMP EnumModuleRefs( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] pointer to the enum. + mdModuleRef rModuleRefs[], // [OUT] put modulerefs here. + ULONG cmax, // [IN] max memberrefs to put. + ULONG *pcModuleRefs); // [OUT] put # put here. + + STDMETHODIMP GetTypeSpecFromToken( // S_OK or error. + mdTypeSpec typespec, // [IN] TypeSpec token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to TypeSpec signature + ULONG *pcbSig); // [OUT] return size of signature. + + STDMETHODIMP GetNameFromToken( // S_OK or error. + mdToken tk, // [IN] Token to get name from. Must have a name. + MDUTF8CSTR *pszUtf8NamePtr); // [OUT] Return pointer to UTF8 name in heap. + + STDMETHODIMP EnumUnresolvedMethods( // S_OK, S_FALSE, or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken rMethods[], // [OUT] Put MemberDefs here. + ULONG cMax, // [IN] Max MemberDefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP GetUserString( // S_OK or error. + mdString stk, // [IN] String token. + __out_ecount_opt (cchString) LPWSTR szString, // [OUT] Copy of string. + ULONG cchString, // [IN] Max chars of room in szString. + ULONG *pchString); // [OUT] How many chars in actual string. + + STDMETHODIMP GetPinvokeMap( // S_OK or error. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD *pdwMappingFlags, // [OUT] Flags used for mapping. + __out_ecount_opt (cchImportName) LPWSTR szImportName, // [OUT] Import name. + ULONG cchImportName, // [IN] Size of the name buffer. + ULONG *pchImportName, // [OUT] Actual number of characters stored. + mdModuleRef *pmrImportDLL); // [OUT] ModuleRef token for the target DLL. + + STDMETHODIMP EnumSignatures( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] pointer to the enum. + mdSignature rSignatures[], // [OUT] put signatures here. + ULONG cmax, // [IN] max signatures to put. + ULONG *pcSignatures); // [OUT] put # put here. + + STDMETHODIMP EnumTypeSpecs( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] pointer to the enum. + mdTypeSpec rTypeSpecs[], // [OUT] put TypeSpecs here. + ULONG cmax, // [IN] max TypeSpecs to put. + ULONG *pcTypeSpecs); // [OUT] put # put here. + + STDMETHODIMP EnumUserStrings( // S_OK or error. + HCORENUM *phEnum, // [IN/OUT] pointer to the enum. + mdString rStrings[], // [OUT] put Strings here. + ULONG cmax, // [IN] max Strings to put. + ULONG *pcStrings); // [OUT] put # put here. + + STDMETHODIMP GetParamForMethodIndex( // S_OK or error. + mdMethodDef md, // [IN] Method token. + ULONG ulParamSeq, // [IN] Parameter sequence. + mdParamDef *ppd); // [IN] Put Param token here. + + STDMETHODIMP GetCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCWSTR szName, // [IN] Name of desired Custom Attribute. + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData); // [OUT] Put size of data here. + + STDMETHODIMP EnumCustomAttributes( // S_OK or error. + HCORENUM *phEnum, // [IN, OUT] COR enumerator. + mdToken tk, // [IN] Token to scope the enumeration, 0 for all. + mdToken tkType, // [IN] Type of interest, 0 for all. + mdCustomAttribute rCustomAttributes[], // [OUT] Put custom attribute tokens here. + ULONG cMax, // [IN] Size of rCustomAttributes. + ULONG *pcCustomAttributes); // [OUT, OPTIONAL] Put count of token values here. + + STDMETHODIMP GetCustomAttributeProps( // S_OK or error. + mdCustomAttribute cv, // [IN] CustomAttribute token. + mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here. + mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here. + void const **ppBlob, // [OUT, OPTIONAL] Put pointer to data here. + ULONG *pcbSize); // [OUT, OPTIONAL] Put size of date here. + + STDMETHODIMP FindTypeRef( // S_OK or error. + mdToken tkResolutionScope, // ResolutionScope. + LPCWSTR szName, // [IN] TypeRef name. + mdTypeRef *ptr); // [OUT] matching TypeRef. + + STDMETHODIMP GetMemberProps( + mdToken mb, // The member for which to get props. + mdTypeDef *pClass, // Put member's class here. + __out_ecount_opt (cchMember) LPWSTR szMember, // Put member's name here. + ULONG cchMember, // Size of szMember buffer in wide chars. + ULONG *pchMember, // Put actual size here + DWORD *pdwAttr, // Put flags here. + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob, // [OUT] actual size of signature blob + ULONG *pulCodeRVA, // [OUT] codeRVA + DWORD *pdwImplFlags, // [OUT] Impl. Flags + DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_* + UVCP_CONSTANT *ppValue, // [OUT] constant value + ULONG *pcbValue); // [OUT] size of constant value + + STDMETHODIMP GetFieldProps( + mdFieldDef mb, // The field for which to get props. + mdTypeDef *pClass, // Put field's class here. + __out_ecount_opt (cchField) LPWSTR szField, // Put field's name here. + ULONG cchField, // Size of szField buffer in wide chars. + ULONG *pchField, // Put actual size here + DWORD *pdwAttr, // Put flags here. + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob, // [OUT] actual size of signature blob + DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_* + UVCP_CONSTANT *ppValue, // [OUT] constant value + ULONG *pcbValue); // [OUT] size of constant value + + STDMETHODIMP GetPropertyProps( // S_OK, S_FALSE, or error. + mdProperty prop, // [IN] property token + mdTypeDef *pClass, // [OUT] typedef containing the property declarion. + LPCWSTR szProperty, // [OUT] Property name + ULONG cchProperty, // [IN] the count of wchar of szProperty + ULONG *pchProperty, // [OUT] actual count of wchar for property name + DWORD *pdwPropFlags, // [OUT] property flags. + PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob + ULONG *pbSig, // [OUT] count of bytes in *ppvSig + DWORD *pdwCPlusTypeFlag, // [OUT] flag for value type. selected ELEMENT_TYPE_* + UVCP_CONSTANT *ppDefaultValue, // [OUT] constant value + ULONG *pcbValue, // [OUT] size of constant value + mdMethodDef *pmdSetter, // [OUT] setter method of the property + mdMethodDef *pmdGetter, // [OUT] getter method of the property + mdMethodDef rmdOtherMethod[], // [OUT] other method of the property + ULONG cMax, // [IN] size of rmdOtherMethod + ULONG *pcOtherMethod); // [OUT] total number of other method of this property + + STDMETHODIMP GetParamProps( // S_OK or error. + mdParamDef tk, // [IN]The Parameter. + mdMethodDef *pmd, // [OUT] Parent Method token. + ULONG *pulSequence, // [OUT] Parameter sequence. + __out_ecount_opt (cchName) LPWSTR szName, // [OUT] Put name here. + ULONG cchName, // [OUT] Size of name buffer. + ULONG *pchName, // [OUT] Put actual size of name here. + DWORD *pdwAttr, // [OUT] Put flags here. + DWORD *pdwCPlusTypeFlag, // [OUT] Flag for value type. selected ELEMENT_TYPE_*. + UVCP_CONSTANT *ppValue, // [OUT] Constant value. + ULONG *pcbValue); // [OUT] size of constant value + + STDMETHODIMP_(BOOL) IsValidToken( // True or False. + mdToken tk); // [IN] Given token. + + STDMETHODIMP GetNestedClassProps( // S_OK or error. + mdTypeDef tdNestedClass, // [IN] NestedClass token. + mdTypeDef *ptdEnclosingClass); // [OUT] EnclosingClass token. + + STDMETHODIMP GetNativeCallConvFromSig( // S_OK or error. + void const *pvSig, // [IN] Pointer to signature. + ULONG cbSig, // [IN] Count of signature bytes. + ULONG *pCallConv); // [OUT] Put calling conv here (see CorPinvokemap). + + STDMETHODIMP IsGlobal( // S_OK or error. + mdToken pd, // [IN] Type, Field, or Method token. + int *pbGlobal); // [OUT] Put 1 if global, 0 otherwise. + +//***************************************************************************** +// IMetaDataImport2 methods +//***************************************************************************** + STDMETHODIMP GetGenericParamProps( // S_OK or error. + mdGenericParam gp, // [IN] GenericParam + ULONG *pulParamSeq, // [OUT] Index of the type parameter + DWORD *pdwParamFlags, // [OUT] Flags, for future use (e.g. variance) + mdToken *ptOwner, // [OUT] Owner (TypeDef or MethodDef) + DWORD *reserved, // [OUT] For future use (e.g. non-type parameters) + __out_ecount_opt (cchName) LPWSTR wzname, // [OUT] Put name here + ULONG cchName, // [IN] Size of buffer + ULONG *pchName); // [OUT] Put size of name (wide chars) here. + + STDMETHODIMP GetGenericParamConstraintProps( // S_OK or error. + mdGenericParamConstraint gpc, // [IN] GenericParamConstraint + mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained + mdToken *ptkConstraintType); // [OUT] TypeDef/Ref/Spec constraint + + STDMETHODIMP GetMethodSpecProps( + mdMethodSpec mi, // [IN] The method instantiation + mdToken *tkParent, // [OUT] MethodDef or MemberRef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob); // [OUT] actual size of signature blob + + STDMETHODIMP EnumGenericParams( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken tk, // [IN] TypeDef or MethodDef whose generic parameters are requested + mdGenericParam rGenericParams[], // [OUT] Put GenericParams here. + ULONG cMax, // [IN] Max GenericParams to put. + ULONG *pcGenericParams); // [OUT] Put # put here. + + STDMETHODIMP EnumGenericParamConstraints( // S_OK or error. + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdGenericParam tk, // [IN] GenericParam whose constraints are requested + mdGenericParamConstraint rGenericParamConstraints[], // [OUT] Put GenericParamConstraints here. + ULONG cMax, // [IN] Max GenericParamConstraints to put. + ULONG *pcGenericParamConstraints); // [OUT] Put # put here. + + STDMETHODIMP EnumMethodSpecs( + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken tk, // [IN] MethodDef or MemberRef whose MethodSpecs are requested + mdMethodSpec rMethodSpecs[], // [OUT] Put MethodSpecs here. + ULONG cMax, // [IN] Max tokens to put. + ULONG *pcMethodSpecs); // [OUT] Put actual count here. + + STDMETHODIMP GetPEKind( // S_OK or error. + DWORD* pdwPEKind, // [OUT] The kind of PE (0 - not a PE) + DWORD* pdwMAchine); // [OUT] Machine as defined in NT header + + STDMETHODIMP GetVersionString( // S_OK or error. + __out_ecount_opt (cchBufSize) LPWSTR pwzBuf, // [OUT] Put version string here. + DWORD cchBufSize, // [IN] size of the buffer, in wide chars + DWORD *pchBufSize); // [OUT] Size of the version string, wide chars, including terminating nul. + +//***************************************************************************** +// IMetaDataAssemblyImport +//***************************************************************************** + STDMETHODIMP GetAssemblyProps( // S_OK or error. + mdAssembly mda, // [IN] The Assembly for which to get the properties. + const void **ppbPublicKey, // [OUT] Pointer to the public key. + ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key. + ULONG *pulHashAlgId, // [OUT] Hash Algorithm. + __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with assembly's simply name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData. + DWORD *pdwAssemblyFlags); // [OUT] Flags. + + STDMETHODIMP GetAssemblyRefProps( // S_OK or error. + mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties. + const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token. + ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token. + __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + ASSEMBLYMETADATA *pMetaData, // [OUT] Assembly MetaData. + const void **ppbHashValue, // [OUT] Hash blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob. + DWORD *pdwAssemblyRefFlags); // [OUT] Flags. + + STDMETHODIMP GetFileProps( // S_OK or error. + mdFile mdf, // [IN] The File for which to get the properties. + __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob. + DWORD *pdwFileFlags); // [OUT] Flags. + + STDMETHODIMP GetExportedTypeProps( // S_OK or error. + mdExportedType mdct, // [IN] The ExportedType for which to get the properties. + __out_ecount_part_opt(cchName, *pchName) LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file. + DWORD *pdwExportedTypeFlags); // [OUT] Flags. + + STDMETHODIMP GetManifestResourceProps( // S_OK or error. + mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties. + __out_ecount_part_opt(cchName, *pchName)LPWSTR szName, // [OUT] Buffer to fill with name. + ULONG cchName, // [IN] Size of buffer in wide chars. + ULONG *pchName, // [OUT] Actual # of wide chars in name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file. + DWORD *pdwResourceFlags); // [OUT] Flags. + + STDMETHODIMP EnumAssemblyRefs( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdAssemblyRef rAssemblyRefs[], // [OUT] Put AssemblyRefs here. + ULONG cMax, // [IN] Max AssemblyRefs to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumFiles( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdFile rFiles[], // [OUT] Put Files here. + ULONG cMax, // [IN] Max Files to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumExportedTypes( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdExportedType rExportedTypes[], // [OUT] Put ExportedTypes here. + ULONG cMax, // [IN] Max ExportedTypes to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP EnumManifestResources( // S_OK or error + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdManifestResource rManifestResources[], // [OUT] Put ManifestResources here. + ULONG cMax, // [IN] Max Resources to put. + ULONG *pcTokens); // [OUT] Put # put here. + + STDMETHODIMP FindExportedTypeByName( // S_OK or error + LPCWSTR szName, // [IN] Name of the ExportedType. + mdExportedType tkEnclosingType, // [IN] Enclosing ExportedType. + mdExportedType *ptkExportedType); // [OUT] Put the ExportedType token here. + + STDMETHODIMP FindManifestResourceByName(// S_OK or error + LPCWSTR szName, // [IN] Name of the ManifestResource. + mdManifestResource *ptkManifestResource); // [OUT] Put the ManifestResource token here. + + STDMETHODIMP GetAssemblyFromScope( // S_OK or error + mdAssembly *ptkAssembly); // [OUT] Put token here. + + // This uses Fusion to lookup, so it's E_NOTIMPL in the standalone versions. + STDMETHODIMP FindAssembliesByName( // S_OK or error + LPCWSTR szAppBase, // [IN] optional - can be NULL + LPCWSTR szPrivateBin, // [IN] optional - can be NULL + LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting + IUnknown *ppIUnk[], // [OUT] put IMetaDataAssemblyImport pointers here + ULONG cMax, // [IN] The max number to put + ULONG *pcAssemblies); // [OUT] The number of assemblies returned. + +#ifdef FEATURE_METADATA_EMIT +//***************************************************************************** +// IMetaDataEmit +//***************************************************************************** + STDMETHODIMP DefineMethod( // S_OK or error. + mdTypeDef td, // Parent TypeDef + LPCWSTR szName, // Name of member + DWORD dwMethodFlags, // Member attributes + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + ULONG ulCodeRVA, + DWORD dwImplFlags, + mdMethodDef *pmd); // Put member token here + + STDMETHODIMP DefineMethodImpl( // S_OK or error. + mdTypeDef td, // [IN] The class implementing the method + mdToken tkBody, // [IN] Method body, MethodDef or MethodRef + mdToken tkDecl); // [IN] Method declaration, MethodDef or MethodRef + + STDMETHODIMP SetMethodImplFlags( // [IN] S_OK or error. + mdMethodDef md, // [IN] Method for which to set impl flags + DWORD dwImplFlags); + + STDMETHODIMP SetFieldRVA( // [IN] S_OK or error. + mdFieldDef fd, // [IN] Field for which to set offset + ULONG ulRVA); // [IN] The offset + + STDMETHODIMP DefineTypeRefByName( // S_OK or error. + mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef. + LPCWSTR szName, // [IN] Name of the TypeRef. + mdTypeRef *ptr); // [OUT] Put TypeRef token here. + + STDMETHODIMP DefineImportType( // S_OK or error. + IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the TypeDef. + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + IMetaDataImport *pImport, // [IN] Scope containing the TypeDef. + mdTypeDef tdImport, // [IN] The imported TypeDef. + IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the TypeDef is imported. + mdTypeRef *ptr); // [OUT] Put TypeRef token here. + + STDMETHODIMP DefineMemberRef( // S_OK or error + mdToken tkImport, // [IN] ClassRef or ClassDef importing a member. + LPCWSTR szName, // [IN] member's name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMemberRef *pmr); // [OUT] memberref token + + STDMETHODIMP DefineImportMember( // S_OK or error. + IMetaDataAssemblyImport *pAssemImport, // [IN] Assemby containing the Member. + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + IMetaDataImport *pImport, // [IN] Import scope, with member. + mdToken mbMember, // [IN] Member in import scope. + IMetaDataAssemblyEmit *pAssemEmit, // [IN] Assembly into which the Member is imported. + mdToken tkImport, // [IN] Classref or classdef in emit scope. + mdMemberRef *pmr); // [OUT] Put member ref here. + + STDMETHODIMP DefineEvent( + mdTypeDef td, // [IN] the class/interface on which the event is being defined + LPCWSTR szEvent, // [IN] Name of the event + DWORD dwEventFlags, // [IN] CorEventAttr + mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef(to the Event class + mdMethodDef mdAddOn, // [IN] required add method + mdMethodDef mdRemoveOn, // [IN] required remove method + mdMethodDef mdFire, // [IN] optional fire method + mdMethodDef rmdOtherMethods[], // [IN] optional array of other methods associate with the event + mdEvent *pmdEvent); // [OUT] output event token + + STDMETHODIMP SetClassLayout( + mdTypeDef td, // [IN] typedef + DWORD dwPackSize, // [IN] packing size specified as 1, 2, 4, 8, or 16 + COR_FIELD_OFFSET rFieldOffsets[], // [IN] array of layout specification + ULONG ulClassSize); // [IN] size of the class + + STDMETHODIMP DeleteClassLayout( + mdTypeDef td); // [IN] typdef token + + STDMETHODIMP SetFieldMarshal( + mdToken tk, // [IN] given a fieldDef or paramDef token + PCCOR_SIGNATURE pvNativeType, // [IN] native type specification + ULONG cbNativeType); // [IN] count of bytes of pvNativeType + + STDMETHODIMP DeleteFieldMarshal( + mdToken tk); // [IN] fieldDef or paramDef token to be deleted. + + STDMETHODIMP DefinePermissionSet( + mdToken tk, // [IN] the object to be decorated. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] permission blob. + ULONG cbPermission, // [IN] count of bytes of pvPermission. + mdPermission *ppm); // [OUT] returned permission token. + + STDMETHODIMP SetRVA( // [IN] S_OK or error. + mdToken md, // [IN] MethodDef for which to set offset + ULONG ulRVA); // [IN] The offset#endif + + STDMETHODIMP GetTokenFromSig( // [IN] S_OK or error. + PCCOR_SIGNATURE pvSig, // [IN] Signature to define. + ULONG cbSig, // [IN] Size of signature data. + mdSignature *pmsig); // [OUT] returned signature token. + + STDMETHODIMP DefineModuleRef( // S_OK or error. + LPCWSTR szName, // [IN] DLL name + mdModuleRef *pmur); // [OUT] returned module ref token + + STDMETHODIMP SetParent( // S_OK or error. + mdMemberRef mr, // [IN] Token for the ref to be fixed up. + mdToken tk); // [IN] The ref parent. + + STDMETHODIMP GetTokenFromTypeSpec( // S_OK or error. + PCCOR_SIGNATURE pvSig, // [IN] ArraySpec Signature to define. + ULONG cbSig, // [IN] Size of signature data. + mdTypeSpec *ptypespec); // [OUT] returned TypeSpec token. + + STDMETHODIMP SaveToMemory( // S_OK or error. + void *pbData, // [OUT] Location to write data. + ULONG cbData); // [IN] Max size of data buffer. + + STDMETHODIMP DefineUserString( // S_OK or error. + LPCWSTR szString, // [IN] User literal string. + ULONG cchString, // [IN] Length of string. + mdString *pstk); // [OUT] String token. + + STDMETHODIMP DeleteToken( // Return code. + mdToken tkObj); // [IN] The token to be deleted + + STDMETHODIMP SetTypeDefProps( // S_OK or error. + mdTypeDef td, // [IN] The TypeDef. + DWORD dwTypeDefFlags, // [IN] TypeDef flags. + mdToken tkExtends, // [IN] Base TypeDef or TypeRef. + mdToken rtkImplements[]); // [IN] Implemented interfaces. + + STDMETHODIMP DefineNestedType( // S_OK or error. + LPCWSTR szTypeDef, // [IN] Name of TypeDef + DWORD dwTypeDefFlags, // [IN] CustomAttribute flags + mdToken tkExtends, // [IN] extends this TypeDef or typeref + mdToken rtkImplements[], // [IN] Implements interfaces + mdTypeDef tdEncloser, // [IN] TypeDef token of the enclosing type. + mdTypeDef *ptd); // [OUT] Put TypeDef token here + + STDMETHODIMP SetMethodProps( // S_OK or error. + mdMethodDef md, // [IN] The MethodDef. + DWORD dwMethodFlags, // [IN] Method attributes. + ULONG ulCodeRVA, // [IN] Code RVA. + DWORD dwImplFlags); // [IN] MethodImpl flags. + + STDMETHODIMP SetEventProps( // S_OK or error. + mdEvent ev, // [IN] The event token. + DWORD dwEventFlags, // [IN] CorEventAttr. + mdToken tkEventType, // [IN] A reference (mdTypeRef or mdTypeRef) to the Event class. + mdMethodDef mdAddOn, // [IN] Add method. + mdMethodDef mdRemoveOn, // [IN] Remove method. + mdMethodDef mdFire, // [IN] Fire method. + mdMethodDef rmdOtherMethods[]); // [IN] Array of other methods associated with the event. + + STDMETHODIMP SetPermissionSetProps( // S_OK or error. + mdToken tk, // [IN] The object to be decorated. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] Permission blob. + ULONG cbPermission, // [IN] Count of bytes of pvPermission. + mdPermission *ppm); // [OUT] Permission token. + + STDMETHODIMP DefinePinvokeMap( // Return code. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD dwMappingFlags, // [IN] Flags used for mapping. + LPCWSTR szImportName, // [IN] Import name. + mdModuleRef mrImportDLL); // [IN] ModuleRef token for the target DLL. + + STDMETHODIMP SetPinvokeMap( // Return code. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD dwMappingFlags, // [IN] Flags used for mapping. + LPCWSTR szImportName, // [IN] Import name. + mdModuleRef mrImportDLL); // [IN] ModuleRef token for the target DLL. + + STDMETHODIMP DeletePinvokeMap( // Return code. + mdToken tk); // [IN]FieldDef or MethodDef. + + STDMETHODIMP DefineCustomAttribute( // Return code. + mdToken tkOwner, // [IN] The object to put the value on. + mdToken tkCtor, // [IN] Constructor of the CustomAttribute type (MemberRef/MethodDef). + void const *pCustomAttribute, // [IN] The custom value data. + ULONG cbCustomAttribute, // [IN] The custom value data length. + mdCustomAttribute *pcv); // [OUT] The custom value token value on return. + + STDMETHODIMP SetCustomAttributeValue( // Return code. + mdCustomAttribute pcv, // [IN] The custom value token whose value to replace. + void const *pCustomAttribute, // [IN] The custom value data. + ULONG cbCustomAttribute); // [IN] The custom value data length. + + STDMETHODIMP DefineField( // S_OK or error. + mdTypeDef td, // Parent TypeDef + LPCWSTR szName, // Name of member + DWORD dwFieldFlags, // Member attributes + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_* + void const *pValue, // [IN] constant value + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdFieldDef *pmd); // [OUT] Put member token here + + STDMETHODIMP DefineProperty( + mdTypeDef td, // [IN] the class/interface on which the property is being defined + LPCWSTR szProperty, // [IN] Name of the property + DWORD dwPropFlags, // [IN] CorPropertyAttr + PCCOR_SIGNATURE pvSig, // [IN] the required type signature + ULONG cbSig, // [IN] the size of the type signature blob + DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_* + void const *pValue, // [IN] constant value + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdMethodDef mdSetter, // [IN] optional setter of the property + mdMethodDef mdGetter, // [IN] optional getter of the property + mdMethodDef rmdOtherMethods[], // [IN] an optional array of other methods + mdProperty *pmdProp); // [OUT] output property token + + STDMETHODIMP DefineParam( + mdMethodDef md, // [IN] Owning method + ULONG ulParamSeq, // [IN] Which param + LPCWSTR szName, // [IN] Optional param name + DWORD dwParamFlags, // [IN] Optional param flags + DWORD dwCPlusTypeFlag, // [IN] flag for value type. selected ELEMENT_TYPE_* + void const *pValue, // [IN] constant value + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdParamDef *ppd); // [OUT] Put param token here + + STDMETHODIMP SetFieldProps( // S_OK or error. + mdFieldDef fd, // [IN] The FieldDef. + DWORD dwFieldFlags, // [IN] Field attributes. + DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue); // [IN] size of constant value (string, in wide chars). + + STDMETHODIMP SetPropertyProps( // S_OK or error. + mdProperty pr, // [IN] Property token. + DWORD dwPropFlags, // [IN] CorPropertyAttr. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdMethodDef mdSetter, // [IN] Setter of the property. + mdMethodDef mdGetter, // [IN] Getter of the property. + mdMethodDef rmdOtherMethods[]); // [IN] Array of other methods. + + STDMETHODIMP SetParamProps( // Return code. + mdParamDef pd, // [IN] Param token. + LPCWSTR szName, // [IN] Param name. + DWORD dwParamFlags, // [IN] Param flags. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*. + void const *pValue, // [OUT] Constant value. + ULONG cchValue); // [IN] size of constant value (string, in wide chars). + + STDMETHODIMP ApplyEditAndContinue( // S_OK or error. + IUnknown *pImport); // [IN] Metadata from the delta PE. + + // Specialized Custom Attributes for security. + STDMETHODIMP DefineSecurityAttributeSet(// Return code. + mdToken tkObj, // [IN] Class or method requiring security attributes. + COR_SECATTR rSecAttrs[], // [IN] Array of security attribute descriptions. + ULONG cSecAttrs, // [IN] Count of elements in above array. + ULONG *pulErrorAttr); // [OUT] On error, index of attribute causing problem. + + STDMETHODIMP TranslateSigWithScope( + IMetaDataAssemblyImport *pAssemImport, // [IN] assembly importing interface + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + IMetaDataImport *import, // [IN] importing interface + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit *pAssemEmti, // [IN] emit assembly interface + IMetaDataEmit *emit, // [IN] emit interface + PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature + ULONG cbTranslatedSigMax, + ULONG *pcbTranslatedSig); // [OUT] count of bytes in the translated signature + +//***************************************************************************** +// IMetaDataEmit2 methods +//***************************************************************************** + STDMETHODIMP SetModuleProps( // S_OK or error. + LPCWSTR szName); // [IN] If not NULL, the name to set. + + STDMETHODIMP Save( // S_OK or error. + LPCWSTR szFile, // [IN] The filename to save to. + DWORD dwSaveFlags); // [IN] Flags for the save. + + STDMETHODIMP SaveToStream( // S_OK or error. + IStream *pIStream, // [IN] A writable stream to save to. + DWORD dwSaveFlags); // [IN] Flags for the save. + + STDMETHODIMP GetSaveSize( // S_OK or error. + CorSaveSize fSave, // [IN] cssAccurate or cssQuick. + DWORD *pdwSaveSize); // [OUT] Put the size here. + + STDMETHODIMP Merge( // S_OK or error. + IMetaDataImport *pImport, // [IN] The scope to be merged. + IMapToken *pHostMapToken, // [IN] Host IMapToken interface to receive token remap notification + IUnknown *pHandler); // [IN] An object to receive to receive error notification. + + STDMETHODIMP MergeEnd(); // S_OK or error. + + STDMETHODIMP DefineMethodSpec( // S_OK or error + mdToken tkImport, // [IN] MethodDef or MemberRef + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodSpec *pmi); // [OUT] method instantiation token + + STDMETHODIMP DefineTypeDef( // S_OK or error. + LPCWSTR szTypeDef, // [IN] Name of TypeDef + DWORD dwTypeDefFlags, // [IN] CustomAttribute flags + mdToken tkExtends, // [IN] extends this TypeDef or typeref + mdToken rtkImplements[], // [IN] Implements interfaces + mdTypeDef *ptd); // [OUT] Put TypeDef token here + + STDMETHODIMP SetHandler( // S_OK. + IUnknown *pUnk); // [IN] The new error handler. + + STDMETHODIMP GetDeltaSaveSize( // S_OK or error. + CorSaveSize fSave, // [IN] cssAccurate or cssQuick. + DWORD *pdwSaveSize); // [OUT] Put the size here. + + STDMETHODIMP SaveDelta( // S_OK or error. + LPCWSTR szFile, // [IN] The filename to save to. + DWORD dwSaveFlags); // [IN] Flags for the save. + + STDMETHODIMP SaveDeltaToStream( // S_OK or error. + IStream *pIStream, // [IN] A writable stream to save to. + DWORD dwSaveFlags); // [IN] Flags for the save. + + STDMETHODIMP SaveDeltaToMemory( // S_OK or error. + void *pbData, // [OUT] Location to write data. + ULONG cbData); // [IN] Max size of data buffer. + + STDMETHODIMP ResetENCLog(); // S_OK or error. + + STDMETHODIMP DefineGenericParam( // S_OK or error. + mdToken tk, // [IN] TypeDef or MethodDef + ULONG ulParamSeq, // [IN] Index of the type parameter + DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance) + LPCWSTR szname, // [IN] Name + DWORD reserved, // [IN] For future use (e.g. non-type parameters) + mdToken rtkConstraints[], // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec) + mdGenericParam *pgp); // [OUT] Put GenericParam token here + + STDMETHODIMP SetGenericParamProps( // S_OK or error. + mdGenericParam gp, // [IN] GenericParam + DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance) + LPCWSTR szName, // [IN] Optional name + DWORD reserved, // [IN] For future use (e.g. non-type parameters) + mdToken rtkConstraints[]); // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec) + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +//***************************************************************************** +// IMetaDataEmit3 methods +//***************************************************************************** + STDMETHODIMP GetReferencedTypeSysTables(// S_OK or error. + ULONG64 *refTables, // [OUT] Bit vector of referenced type system metadata tables. + ULONG refTableRows[], // [OUT] Array of number of rows for each referenced type system table. + const ULONG maxTableRowsSize, // [IN] Max size of the rows array. + ULONG *tableRowsSize); // [OUT] Actual size of the rows array. + + STDMETHODIMP DefinePdbStream( // S_OK or error. + PORT_PDB_STREAM* pdbStream); // [IN] Portable pdb stream data. + + STDMETHODIMP DefineDocument( // S_OK or error. + char *docName, // [IN] Document name (string will be tokenized). + GUID *hashAlg, // [IN] Hash algorithm GUID. + BYTE *hashVal, // [IN] Hash value. + ULONG hashValSize, // [IN] Hash value size. + GUID *lang, // [IN] Language GUID. + mdDocument *docMdToken); // [OUT] Token of the defined document. + + STDMETHODIMP DefineSequencePoints( // S_OK or error. + ULONG docRid, // [IN] Document RID. + BYTE *sequencePtsBlob, // [IN] Sequence point blob. + ULONG sequencePtsBlobSize); // [IN] Sequence point blob size. + + STDMETHODIMP DefineLocalScope( // S_OK or error. + ULONG methodDefRid, // [IN] Method RID. + ULONG importScopeRid, // [IN] Import scope RID. + ULONG firstLocalVarRid, // [IN] First local variable RID (of the continous run). + ULONG firstLocalConstRid, // [IN] First local constant RID (of the continous run). + ULONG startOffset, // [IN] Start offset of the scope. + ULONG length); // [IN] Scope length. + + STDMETHODIMP DefineLocalVariable( // S_OK or error. + USHORT attribute, // [IN] Variable attribute. + USHORT index, // [IN] Variable index (slot). + char *name, // [IN] Variable name. + mdLocalVariable *locVarToken); // [OUT] Token of the defined variable. +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + +//***************************************************************************** +// IMetaDataAssemblyEmit +//***************************************************************************** + STDMETHODIMP DefineAssembly( // S_OK or error. + const void *pbPublicKey, // [IN] Public key of the assembly. + ULONG cbPublicKey, // [IN] Count of bytes in the public key. + ULONG ulHashAlgId, // [IN] Hash Algorithm. + LPCWSTR szName, // [IN] Name of the assembly. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + DWORD dwAssemblyFlags, // [IN] Flags. + mdAssembly *pma); // [OUT] Returned Assembly token. + + STDMETHODIMP DefineAssemblyRef( // S_OK or error. + const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly. + ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token. + LPCWSTR szName, // [IN] Name of the assembly being referenced. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwAssemblyRefFlags, // [IN] Token for Execution Location. + mdAssemblyRef *pmar); // [OUT] Returned AssemblyRef token. + + STDMETHODIMP DefineFile( // S_OK or error. + LPCWSTR szName, // [IN] Name of the file. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwFileFlags, // [IN] Flags. + mdFile *pmf); // [OUT] Returned File token. + + STDMETHODIMP DefineExportedType( // S_OK or error. + LPCWSTR szName, // [IN] Name of the Com Type. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef tkTypeDef, // [IN] TypeDef token within the file. + DWORD dwExportedTypeFlags, // [IN] Flags. + mdExportedType *pmct); // [OUT] Returned ExportedType token. + + STDMETHODIMP DefineManifestResource( // S_OK or error. + LPCWSTR szName, // [IN] Name of the resource. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource. + DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file. + DWORD dwResourceFlags, // [IN] Flags. + mdManifestResource *pmmr); // [OUT] Returned ManifestResource token. + + STDMETHODIMP SetAssemblyProps( // S_OK or error. + mdAssembly pma, // [IN] Assembly token. + const void *pbPublicKey, // [IN] Public key of the assembly. + ULONG cbPublicKey, // [IN] Count of bytes in the public key. + ULONG ulHashAlgId, // [IN] Hash Algorithm. + LPCWSTR szName, // [IN] Name of the assembly. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + DWORD dwAssemblyFlags); // [IN] Flags. + + STDMETHODIMP SetAssemblyRefProps( // S_OK or error. + mdAssemblyRef ar, // [IN] AssemblyRefToken. + const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly. + ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token. + LPCWSTR szName, // [IN] Name of the assembly being referenced. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwAssemblyRefFlags); // [IN] Token for Execution Location. + + STDMETHODIMP SetFileProps( // S_OK or error. + mdFile file, // [IN] File token. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwFileFlags); // [IN] Flags. + + STDMETHODIMP SetExportedTypeProps( // S_OK or error. + mdExportedType ct, // [IN] ExportedType token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef tkTypeDef, // [IN] TypeDef token within the file. + DWORD dwExportedTypeFlags); // [IN] Flags. + + STDMETHODIMP SetManifestResourceProps( // S_OK or error. + mdManifestResource mr, // [IN] ManifestResource token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource. + DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file. + DWORD dwResourceFlags); // [IN] Flags. + +#endif //FEATURE_METADATA_EMIT + +#ifdef FEATURE_METADATA_EMIT_ALL +//***************************************************************************** +// IMetaDataFilter +//***************************************************************************** + STDMETHODIMP UnmarkAll(); // unmark everything in a module + + STDMETHODIMP MarkToken( + mdToken tk); // [IN] Token to be marked + + STDMETHODIMP IsTokenMarked( + mdToken tk, // [IN] Token to be checked + BOOL *pIsMarked); // [OUT] TRUE if token is marked + +#endif //FEATURE_METADATA_EMIT_ALL + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +//***************************************************************************** +// IMetaDataEmitHelper +//***************************************************************************** + STDMETHODIMP DefineMethodSemanticsHelper( + mdToken tkAssociation, // [IN] property or event token + DWORD dwFlags, // [IN] semantics + mdMethodDef md); // [IN] method to associated with + + STDMETHODIMP SetFieldLayoutHelper( // Return hresult. + mdFieldDef fd, // [IN] field to associate the layout info + ULONG ulOffset); // [IN] the offset for the field + + STDMETHODIMP DefineEventHelper( + mdTypeDef td, // [IN] the class/interface on which the event is being defined + LPCWSTR szEvent, // [IN] Name of the event + DWORD dwEventFlags, // [IN] CorEventAttr + mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class + mdEvent *pmdEvent); // [OUT] output event token + + STDMETHODIMP AddDeclarativeSecurityHelper( + mdToken tk, // [IN] Parent token (typedef/methoddef) + DWORD dwAction, // [IN] Security action (CorDeclSecurity) + void const *pValue, // [IN] Permission set blob + DWORD cbValue, // [IN] Byte count of permission set blob + mdPermission*pmdPermission); // [OUT] Output permission token + + STDMETHODIMP SetResolutionScopeHelper( // Return hresult. + mdTypeRef tr, // [IN] TypeRef record to update + mdToken rs); // [IN] new ResolutionScope + + STDMETHODIMP SetManifestResourceOffsetHelper( // Return hresult. + mdManifestResource mr, // [IN] The manifest token + ULONG ulOffset); // [IN] new offset + + STDMETHODIMP SetTypeParent( // Return hresult. + mdTypeDef td, // [IN] Type definition + mdToken tkExtends); // [IN] parent type + + STDMETHODIMP AddInterfaceImpl( // Return hresult. + mdTypeDef td, // [IN] Type definition + mdToken tkInterface); // [IN] interface type + +//***************************************************************************** +// IMDInternalEmit +//***************************************************************************** + + STDMETHODIMP ChangeMvid( // S_OK or error. + REFGUID newMvid); // GUID to use as the MVID + + STDMETHOD(SetMDUpdateMode)( + ULONG updateMode, ULONG *pPreviousUpdateMode); + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + STDMETHODIMP GetPathSeparator( // S_OK or error. + char *path, // [IN] Path string to search. + char *separator, // [OUT] Separator used in path string, NULL if none. + ULONG *partsCount); // [OUT] Number of parts separated by the separator. +#endif + +//***************************************************************************** +// IMetaDataHelper +//***************************************************************************** + STDMETHODIMP GetMetadata( // Result. + ULONG ulSelect, // [IN] Selector. + void **ppData); // [OUT] Put pointer to data here. + + STDMETHODIMP_(IUnknown *) GetCachedInternalInterface(BOOL fWithLock); // S_OK or error + STDMETHODIMP SetCachedInternalInterface(IUnknown *pUnk); // S_OK or error + STDMETHODIMP SetReaderWriterLock(UTSemReadWrite * pSem) + { + _ASSERTE(m_pSemReadWrite == NULL); + m_pSemReadWrite = pSem; + INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + return NOERROR; + } + STDMETHODIMP_(UTSemReadWrite *) GetReaderWriterLock() { return m_pSemReadWrite; } + +#ifndef FEATURE_METADATA_EMIT + // This method is also part of IMetaDataEmit interface, do not declare it twice + STDMETHODIMP TranslateSigWithScope( + IMetaDataAssemblyImport *pAssemImport, // [IN] assembly importing interface + const void *pbHashValue, // [IN] Hash Blob for Assembly. + ULONG cbHashValue, // [IN] Count of bytes. + IMetaDataImport *import, // [IN] importing interface + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit *pAssemEmti, // [IN] emit assembly interface + IMetaDataEmit *emit, // [IN] emit interface + PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature + ULONG cbTranslatedSigMax, + ULONG *pcbTranslatedSig); // [OUT] count of bytes in the translated signature +#endif //!FEATURE_METADATA_EMIT + + //***************************************************************************** + // IGetIMDInternalImport methods + //***************************************************************************** + STDMETHOD(GetIMDInternalImport) ( + IMDInternalImport ** ppIMDInternalImport // [OUT] Buffer to receive IMDInternalImport* + ); + +#endif //FEATURE_METADATA_INTERNAL_APIS + +//***************************************************************************** +// IMetaDataTables +//***************************************************************************** + + // Fills size (*pcbStringsHeapSize) of internal strings heap (#String). + // Returns S_OK or error code. Fills *pcbStringsHeapSize with 0 on error. + // Implements public API code:IMetaDataTables::GetStringHeapSize. + STDMETHODIMP GetStringHeapSize( + __out ULONG *pcbStringsHeapSize); // [OUT] Size of the string heap. + + // Fills size (*pcbBlobsHeapSize) of blobs heap (#Blob). + // Returns S_OK or error code. Fills *pcbBlobsHeapSize with 0 on error. + // Implements public API code:IMetaDataTables::GetBlobHeapSize. + STDMETHODIMP GetBlobHeapSize( + __out ULONG *pcbBlobsHeapSize); // [OUT] Size of the blob heap. + + // Fills size (*pcbGuidsHeapSize) of guids heap (#GUID). + // Returns S_OK or error code. Fills *pcbGuidsHeapSize with 0 on error. + // Implements public API code:IMetaDataTables::GetGuidHeapSize. + STDMETHODIMP GetGuidHeapSize( + __out ULONG *pcbGuidsHeapSize); // [OUT] Size of the Guid heap. + + // Fills size (*pcbUserStringsHeapSize) of user strings heap (#US) (referenced from IL). + // Returns S_OK or error code. Fills *pcbUserStringsHeapSize with 0 on error. + // Implements public API code:IMetaDataTables::GetUserStringHeapSize. + // Backward compatibility: returns S_OK even if the string doesn't have odd number of bytes as specified + // in CLI ECMA specification. + STDMETHODIMP GetUserStringHeapSize( + __out ULONG *pcbUserStringsHeapSize); // [OUT] Size of the user string heap. + + // Implements public API code:IMetaDataTables::GetNumTables. + STDMETHODIMP GetNumTables( + __out ULONG *pcTables); // [OUT] Count of tables. + + // Implements public API code:IMetaDataTables::GetNumTables. + STDMETHODIMP GetTableIndex( + ULONG token, // [IN] Token for which to get table index. + __out ULONG *pixTbl); // [OUT] Put table index here. + + // Implements public API code:IMetaDataTables::GetTableInfo. + STDMETHODIMP GetTableInfo( + ULONG ixTbl, // [IN] Which table. + ULONG *pcbRow, // [OUT] Size of a row, bytes. + ULONG *pcRows, // [OUT] Number of rows. + ULONG *pcCols, // [OUT] Number of columns in each row. + ULONG *piKey, // [OUT] Key column, or -1 if none. + const char **ppName); // [OUT] Name of the table. + + // Implements public API code:IMetaDataTables::GetColumnInfo. + STDMETHODIMP GetColumnInfo( + ULONG ixTbl, // [IN] Which Table. + ULONG ixCol, // [IN] Which Column in the table. + ULONG *poCol, // [OUT] Offset of the column in the row. + ULONG *pcbCol, // [OUT] Size of a column, bytes. + ULONG *pType, // [OUT] Type of the column. + const char **ppName); // [OUT] Name of the Column. + + // Implements public API code:IMetaDataTables::GetCodedTokenInfo. + STDMETHODIMP GetCodedTokenInfo( + ULONG ixCdTkn, // [IN] Which kind of coded token. + ULONG *pcTokens, // [OUT] Count of tokens. + ULONG **ppTokens, // [OUT] List of tokens. + const char **ppName); // [OUT] Name of the CodedToken. + + // Implements public API code:IMetaDataTables::GetRow. + STDMETHODIMP GetRow( + ULONG ixTbl, // [IN] Which table. + ULONG rid, // [IN] Which row. + void **ppRow); // [OUT] Put pointer to row here. + + // Implements public API code:IMetaDataTables::GetColumn. + STDMETHODIMP GetColumn( + ULONG ixTbl, // [IN] Which table. + ULONG ixCol, // [IN] Which column. + ULONG rid, // [IN] Which row. + ULONG *pVal); // [OUT] Put the column contents here. + + //#GetString_IMetaDataTables + // Fills internal null-terminated string (*pszString) at index ixString from string heap (#String). + // Returns S_OK (even for index 0) or error code (if index is invalid, fills *pszString with NULL then). + // Implements public API code:IMetaDataTables::GetString. + STDMETHODIMP GetString( + ULONG ixString, // [IN] Value from a string column. + const char **pszString); // [OUT] Put a pointer to the string here. + + //#GetBlob_IMetaDataTables + // Fills blob entry (*ppvData of size *pcbDataSize) at index ixBlob from blob heap (#Blob). + // Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then). + // Implements public API code:IMetaDataTables::GetBlob. + STDMETHODIMP GetBlob( + ULONG ixBlob, // [IN] Value from a blob column. + ULONG *pcbDataSize, // [OUT] Put size of the blob here. + const void **ppvData); // [OUT] Put a pointer to the blob here. + + //#GetGuid_IMetaDataTables + // Fills guid (*ppGuid) at index ixGuid from guid heap (#GUID). + // Returns S_OK and fills *ppGuid. Returns S_OK even for (invalid) index 0 (fills *ppGuid with pointer + // to zeros then). + // Retruns error code (if index is invalid except 0, fills NULL and o then). + // Implements public API code:IMetaDataTables::GetGuid. + // Backward compatibility: returns S_OK even if the index is 0 which is invalid as specified in CLI ECMA + // specification. In that case returns pointer to GUID from zeros. + STDMETHODIMP GetGuid( + ULONG ixGuid, // [IN] Value from a guid column. + const GUID **ppGuid); // [OUT] Put a pointer to the GUID here. + + //#GetUserString_IMetaDataTables + // Fills user string (*ppvData of size *pcbDataSize) at index ixUserString. + // Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then). + // Implements public API code:IMetaDataTables::GetUserString. + STDMETHODIMP GetUserString( + ULONG ixUserString, // [IN] Value from a UserString column. + __out ULONG *pcbData, // [OUT] Put size of the UserString here. + __deref_out_opt const void **ppData); // [OUT] Put a pointer to the UserString here. + + //#GetNextString_IMetaDataTables + // Fills index of string (*pixNextString) from the internal strings heap (#String) starting behind + // string at index ixString. + // Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextString with 0 on S_FALSE. + // Implements public API code:IMetaDataTables::.GetNextString. + STDMETHODIMP GetNextString( + ULONG ixString, // [IN] Value from a string column. + __out ULONG *pixNextString); // [OUT] Put the index of the next string here. + + //#GetNextBlob_IMetaDataTables + // Fills index of blob (*pixNextBlob) from the blobs heap (#Blob) starting behind blob at index ixBlob. + // Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextBlob with 0 on S_FALSE. + // Implements public API code:IMetaDataTables::GetNextString. + STDMETHODIMP GetNextBlob( + ULONG ixBlob, // [IN] Value from a blob column. + __out ULONG *pixNextBlob); // [OUT] Put the index of the next blob here. + + //#GetNextGuid_IMetaDataTables + // Fills index of guid (*pixNextGuid) from the guids heap (#GUID) starting behind guid at index ixGuid. + // Returns S_OK or S_FALSE (if the new index is invalid). Fills *pixNextGuid with 0 on S_FALSE. + // Implements public API code:IMetaDataTables::GetNextGuid. + // Backward compatibility: returns S_OK even if the guid index (ixGuid) is 0 which is invalid as + // specified in CLI ECMA specification. + STDMETHODIMP GetNextGuid( + ULONG ixGuid, // [IN] Value from a guid column. + __out ULONG *pixNextGuid); // [OUT] Put the index of the next guid here. + + //#GetNextUserString_IMetaDataTables + // Fills index of user string (*pixNextUserString) from the user strings heap (#US) starting behind string + // at index ixUserString. + // Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextUserString with 0 on S_FALSE. + // Implements public API code:IMetaDataTables::GetNextUserString. + // Backward compatibility: returns S_OK even if the string doesn't have odd number of bytes as specified + // in CLI ECMA specification. + STDMETHODIMP GetNextUserString( + ULONG ixUserString, // [IN] Value from a UserString column. + __out ULONG *ixpNextUserString); // [OUT] Put the index of the next user string here. + + // Implements public API code:IMetaDataTables2::GetMetaDataStorage. + STDMETHODIMP GetMetaDataStorage( + const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB'). + ULONG *pcbMd); // [OUT] put size of the stream here. + + // Implements public API code:IMetaDataTables2::GetMetaDataStreamInfo. + STDMETHODIMP GetMetaDataStreamInfo( // Get info about the MD stream. + ULONG ix, // [IN] Stream ordinal desired. + const char **ppchName, // [OUT] put pointer to stream name here. + const void **ppv, // [OUT] put pointer to MD stream here. + ULONG *pcb); // [OUT] put size of the stream here. + + +//***************************************************************************** +// IMetaDataInfo +//***************************************************************************** + + // Returns the memory region of the mapped file and type of its mapping. The choice of the file mapping + // type for each scope is CLR implementation specific and user cannot explicitly set it. + // + // The memory is valid only as long as the underlying MetaData scope is opened (there's a reference to + // a MetaData interface for this scope). + // + // Returns S_OK, COR_E_NOTSUPPORTED (.obj files, etc.), or E_INVALIDARG (if NULL is passed). + // Implements public API code:IMetaDataInfo::GetFileMapping. + STDMETHODIMP GetFileMapping( + const void ** ppvData, // [out] Pointer to the start of the mapped file. + ULONGLONG * pcbData, // [out] Size of the mapped memory region.. + DWORD * pdwMappingType); // [out] Type of file mapping (code:CorFileMapping). + + +#if defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT) + +//***************************************************************************** +// IMetaDataCorProfileData +//***************************************************************************** + + STDMETHOD(SetCorProfileData)( + CorProfileData *pProfileData); // [IN] Pointer to profile data + +//***************************************************************************** +// IMDInternalMetadataReorderingOptions +//***************************************************************************** + + STDMETHOD(SetMetaDataReorderingOptions)( + MetaDataReorderingOptions options); // [IN] metadata reordering options + +#endif //FEATURE_METADATA_IN_VM && FEATURE_PREJIT + +//***************************************************************************** +// IMDCommon methods +//***************************************************************************** + STDMETHOD_(IMetaModelCommon*, GetMetaModelCommon)() + { + return GetMiniMd(); + } + + STDMETHOD_(IMetaModelCommonRO*, GetMetaModelCommonRO)() + { + if (GetMiniMd()->IsWritable()) + { + _ASSERTE(!"IMetaModelCommonRO methods cannot be used because this importer is writable."); + return NULL; + } + return GetMiniMd(); + } + + + // returns the "built for" version of a metadata scope. + __checkReturn + STDMETHOD(GetVersionString)( // S_OK or error. + LPCSTR *pVer); // [OUT] Put version string here. + +//***************************************************************************** +// Helpers. +//***************************************************************************** + + HRESULT MarkAll(); // mark everything in a module + +//***************************************************************************** +// Open / Create support. +//***************************************************************************** + + RegMeta(); + virtual ~RegMeta(); + + HRESULT SetOption(OptionValue *pOptionValue); + + // HRESULT Init(); + // void Cleanup(); + + HRESULT InitWithStgdb( + IUnknown *pUnk, // The IUnknown that owns the life time for the existing stgdb + CLiteWeightStgdbRW *pStgdb); // existing light weight stgdb + + ULONG GetRefCount() { return m_cRef; } + HRESULT AddToCache(); + static HRESULT FindCachedReadOnlyEntry(LPCWSTR szName, DWORD dwOpenFlags, RegMeta **ppMeta); + BOOL IsReadOnly() { return IsOfReadOnly(m_OpenFlags); } + BOOL IsCopyMemory() { return IsOfCopyMemory(m_OpenFlags); } + + + // helper function to reopen RegMeta with a new chuck of memory + HRESULT ReOpenWithMemory( + LPCVOID pData, // [in] Location of scope data. + ULONG cbData, // [in] ReOpen flags + DWORD dwReOpenFlags); // [in] Size of the data pointed to by pData. + + HRESULT CreateNewMD(); + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + HRESULT CreateNewPortablePdbMD(); +#endif + + HRESULT OpenExistingMD( + LPCWSTR szDatabase, // Name of database. + void *pbData, // Data to open on top of, 0 default. + ULONG cbData, // How big is the data. + ULONG dwFlags); // Flags to control open. + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + HRESULT OpenExistingMD( + IMDCustomDataSource* pDataSource, // Name of database. + ULONG dwFlags); // Flags to control open. +#endif + + FORCEINLINE CLiteWeightStgdbRW* GetMiniStgdb() { return m_pStgdb; } + FORCEINLINE CMiniMdRW* GetMiniMd() { return &m_pStgdb->m_MiniMd; } + +//***************************************************************************** + + bool IsTypeDefDirty() { return m_fIsTypeDefDirty;} + void SetTypeDefDirty(bool fDirty) { m_fIsTypeDefDirty = fDirty;} + + bool IsMemberDefDirty() { return m_fIsMemberDefDirty;} + void SetMemberDefDirty(bool fDirty) { m_fIsMemberDefDirty = fDirty;} + + FORCEINLINE BOOL IsThreadSafetyOn() + { + return (m_OptionValue.m_ThreadSafetyOptions & MDThreadSafetyOn) == MDThreadSafetyOn; + } + + LPCWSTR GetNameOfDBFile() { return (m_pStgdb->m_wszFileName == NULL) ? W("") : m_pStgdb->m_wszFileName; } + DWORD GetLowFileTimeOfDBFile() { return m_pStgdb->m_dwDatabaseLFT; } + DWORD GetLowFileSizeOfDBFile() { return m_pStgdb->m_dwDatabaseLFS; } +protected: + // Helper functions used for implementation of MetaData APIs. + HRESULT RefToDefOptimization(); + + FORCEINLINE BOOL PreserveLocalRefs(CorLocalRefPreservation localRefType) + { + return (m_OptionValue.m_LocalRefPreservation & localRefType) == localRefType; + } + + HRESULT PreSave(); + + // Initialize the EE + HRESULT StartupEE(); + + // Define a TypeRef given the name. + enum eCheckDups {eCheckDefault=0, eCheckNo=1, eCheckYes=2}; + + HRESULT _DefinePermissionSet( + mdToken tk, // [IN] the object to be decorated. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] permission blob. + ULONG cbPermission, // [IN] count of bytes of pvPermission. + mdPermission *ppm); // [OUT] returned permission token. + + HRESULT _DefineTypeRef( + mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef. + const void *szName, // [IN] Name of the TypeRef. + BOOL isUnicode, // [IN] Specifies whether the URL is unicode. + mdTypeRef *ptk, // [OUT] Put mdTypeRef here. + eCheckDups eCheck=eCheckDefault); // [IN] Specifies whether to check for duplicates. + + // Define MethodSemantics + HRESULT _DefineMethodSemantics( // S_OK or error. + USHORT usAttr, // [IN] CorMethodSemanticsAttr + mdMethodDef md, // [IN] Method + mdToken tkAssoc, // [IN] Association + BOOL bClear); // [IN] Specifies whether to delete the existing records. + + HRESULT _SaveToStream( // S_OK or error. + IStream *pIStream, // [IN] A writable stream to save to. + DWORD dwSaveFlags); // [IN] Flags for the save. + + HRESULT _SetRVA( // [IN] S_OK or error. + mdToken md, // [IN] Member for which to set offset + ULONG ulCodeRVA, // [IN] The offset + DWORD dwImplFlags); + + HRESULT _DefineEvent( // Return hresult. + mdTypeDef td, // [IN] the class/interface on which the event is being defined + LPCWSTR szEvent, // [IN] Name of the event + DWORD dwEventFlags, // [IN] CorEventAttr + mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class + mdEvent *pmdEvent); // [OUT] output event token + + // Creates and sets a row in the InterfaceImpl table. Optionally clear + // pre-existing records for the owning class. + HRESULT _SetImplements( // S_OK or error. + mdToken rTk[], // Array of TypeRef or TypeDef tokens for implemented interfaces. + mdTypeDef td, // Implementing TypeDef. + BOOL bClear); // Specifies whether to clear the existing records. + + // Sets flags, name and constraints for a single GenericParam record + HRESULT _SetGenericParamProps( // S_OK or error. + mdGenericParam tkGP, // [IN] Formal parameter token + GenericParamRec *pGenericParam, // [IN] GenericParam record ptr + DWORD dwParamFlags, // [IN] Flags, for future use (e.g. variance) + LPCWSTR szName, // [IN] Optional name + DWORD reserved, // [IN] For future use (e.g. non-type parameters) + mdToken rtkConstraints[]); // [IN] Array of type constraints (TypeDef,TypeRef,TypeSpec) + + HRESULT _SetTypeDefProps( // S_OK or error. + mdTypeDef td, // [IN] The TypeDef. + DWORD dwTypeDefFlags, // [IN] TypeDef flags. + mdToken tkExtends, // [IN] Base TypeDef or TypeRef. + mdToken rtkImplements[]); // [IN] Implemented interfaces. + + HRESULT _SetEventProps1( // Return hresult. + mdEvent ev, // [IN] Event token. + DWORD dwEventFlags, // [IN] Event flags. + mdToken tkEventType); // [IN] Event type class. + + HRESULT _SetEventProps2( // Return hresult. + mdEvent ev, // [IN] Event token. + mdMethodDef mdAddOn, // [IN] Add method. + mdMethodDef mdRemoveOn, // [IN] Remove method. + mdMethodDef mdFire, // [IN] Fire method. + mdMethodDef rmdOtherMethods[], // [IN] An array of other methods. + BOOL bClear); // [IN] Specifies whether to clear the existing MethodSemantics records. + + HRESULT _SetPermissionSetProps( // Return hresult. + mdPermission tkPerm, // [IN] Permission token. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] Permission blob. + ULONG cbPermission); // [IN] Count of bytes of pvPermission. + + HRESULT _DefinePinvokeMap( // Return hresult. + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD dwMappingFlags, // [IN] Flags used for mapping. + LPCWSTR szImportName, // [IN] Import name. + mdModuleRef mrImportDLL); // [IN] ModuleRef token for the target DLL. + + HRESULT _DefineSetConstant( // Return hresult. + mdToken tk, // [IN] Parent token. + DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchString, // [IN] Size of string in wide chars, or -1 for default. + BOOL bSearch); // [IN] Specifies whether to search for an existing record. + + HRESULT _SetMethodProps( // S_OK or error. + mdMethodDef md, // [IN] The MethodDef. + DWORD dwMethodFlags, // [IN] Method attributes. + ULONG ulCodeRVA, // [IN] Code RVA. + DWORD dwImplFlags); // [IN] MethodImpl flags. + + HRESULT _SetFieldProps( // S_OK or error. + mdFieldDef fd, // [IN] The FieldDef. + DWORD dwFieldFlags, // [IN] Field attributes. + DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue); // [IN] size of constant value (string, in wide chars). + + HRESULT _SetClassLayout( // S_OK or error. + mdTypeDef td, // [IN] The class. + ULONG dwPackSize, // [IN] The packing size. + ULONG ulClassSize); // [IN, OPTIONAL] The class size. + + HRESULT _SetFieldOffset( // S_OK or error. + mdFieldDef fd, // [IN] The field. + ULONG ulOffset); // [IN] The offset of the field. + + HRESULT _SetPropertyProps( // S_OK or error. + mdProperty pr, // [IN] Property token. + DWORD dwPropFlags, // [IN] CorPropertyAttr. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdMethodDef mdSetter, // [IN] Setter of the property. + mdMethodDef mdGetter, // [IN] Getter of the property. + mdMethodDef rmdOtherMethods[]); // [IN] Array of other methods. + + HRESULT _SetParamProps( // Return code. + mdParamDef pd, // [IN] Param token. + LPCWSTR szName, // [IN] Param name. + DWORD dwParamFlags, // [IN] Param flags. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*. + void const *pValue, // [OUT] Constant value. + ULONG cchValue); // [IN] size of constant value (string, in wide chars). + + HRESULT _SetAssemblyProps( // S_OK or error. + mdAssembly pma, // [IN] Assembly token. + const void *pbOriginator, // [IN] Originator of the assembly. + ULONG cbOriginator, // [IN] Count of bytes in the Originator blob. + ULONG ulHashAlgId, // [IN] Hash Algorithm. + LPCWSTR szName, // [IN] Name of the assembly. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + DWORD dwAssemblyFlags); // [IN] Flags. + + HRESULT _SetAssemblyRefProps( // S_OK or error. + mdAssemblyRef ar, // [IN] AssemblyRefToken. + const void *pbPublicKeyOrToken, // [IN] Public key or token of the assembly. + ULONG cbPublicKeyOrToken, // [IN] Count of bytes in the public key or token. + LPCWSTR szName, // [IN] Name of the assembly being referenced. + const ASSEMBLYMETADATA *pMetaData, // [IN] Assembly MetaData. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwAssemblyRefFlags); // [IN] Token for Execution Location. + + HRESULT _SetFileProps( // S_OK or error. + mdFile file, // [IN] File token. + const void *pbHashValue, // [IN] Hash Blob. + ULONG cbHashValue, // [IN] Count of bytes in the Hash Blob. + DWORD dwFileFlags) ; // [IN] Flags. + + HRESULT _SetExportedTypeProps( // S_OK or error. + mdExportedType ct, // [IN] ExportedType token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef tkTypeDef, // [IN] TypeDef token within the file. + DWORD dwExportedTypeFlags); // [IN] Flags. + + HRESULT _SetManifestResourceProps( // S_OK or error. + mdManifestResource mr, // [IN] ManifestResource token. + mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource. + DWORD dwOffset, // [IN] Offset to the beginning of the resource within the file. + DWORD dwResourceFlags); // [IN] Flags. + + HRESULT _DefineTypeDef( // S_OK or error. + LPCWSTR szTypeDef, // [IN] Name of TypeDef + DWORD dwTypeDefFlags, // [IN] CustomAttribute flags + mdToken tkExtends, // [IN] extends this TypeDef or typeref + mdToken rtkImplements[], // [IN] Implements interfaces + mdTypeDef tdEncloser, // [IN] TypeDef token of the Enclosing Type. + mdTypeDef *ptd); // [OUT] Put TypeDef token here + + HRESULT _SetFieldMarshal( + mdToken tk, // [IN] given a fieldDef or paramDef token + PCCOR_SIGNATURE pvNativeType, // [IN] native type specification + ULONG cbNativeType); // [IN] count of bytes of pvNativeType + + HRESULT _IsKnownCustomAttribute( // S_OK, S_FALSE, or error. + mdToken tkType, // [IN] Token of custom attribute's type. + int *pca); // [OUT] Put value from KnownCustAttr enum here. + + HRESULT _DefineModuleRef( // S_OK or error. + LPCWSTR szName, // [IN] DLL name + mdModuleRef *pmur); // [OUT] returned module ref token + + HRESULT _HandleKnownCustomAttribute( // S_OK or error. + mdToken tkObj, // [IN] Object being attributed. + const void *pData, // [IN] Custom Attribute data blob. + ULONG cbData, // [IN] Count of bytes in the data. + int ca, // [IN] Value from KnownCustAttr enum. + int *bKeep); // [OUT} Keep the known CA? + + HRESULT _HandleNativeTypeCustomAttribute(// S_OK or error. + mdToken tkObj, // Object being attributed. + CaArg *pArgs, // Pointer to args. + CaNamedArg *pNamedArgs, // Pointer to named args. + CQuickArray<BYTE> &qNativeType); // Native type is built here. + + // Find a given param of a Method. + HRESULT _FindParamOfMethod( // S_OK or error. + mdMethodDef md, // [IN] The owning method of the param. + ULONG iSeq, // [IN] The sequence # of the param. + mdParamDef *pParamDef); // [OUT] Put ParamDef token here. + + // Given the signature, return the token for signature. + HRESULT _GetTokenFromSig( // S_OK or error. + PCCOR_SIGNATURE pvSig, // [IN] Signature to define. + ULONG cbSig, // [IN] Size of signature data. + mdSignature *pmsig); // [OUT] returned signature token. + + // Turn the specified internal flags on. + HRESULT _TurnInternalFlagsOn( // S_OK or error. + mdToken tkObj, // [IN] Target object whose internal flags are targeted. + DWORD flags); // [IN] Specifies flags to be turned on. + + // This routine eliminates duplicates from the given list of InterfaceImpl tokens + // to be defined. It checks for duplicates against the database only if the + // TypeDef for which these tokens are being defined is not a new one. + HRESULT _InterfaceImplDupProc( // S_OK or error. + mdToken rTk[], // Array of TypeRef or TypeDef tokens for implemented interfaces. + mdTypeDef td, // Implementing TypeDef. + CQuickBytes *pcqbTk); // Quick Byte object for placing the array of unique tokens. + + // Helper : convert a text field signature to a com format + HRESULT _ConvertTextElementTypeToComSig(// Return hresult. + IMetaDataEmit *emit, // [IN] emit interface. + BOOL fCreateTrIfNotFound, // [IN] create typeref if not found or fail out? + LPCSTR *ppOneArgSig, // [IN|OUT] class file format signature. On exit, it will be next arg starting point + CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature + ULONG cbStart, // [IN] bytes that are already in pqbNewSig + ULONG *pcbCount); // [OUT] count of bytes put into the QuickBytes buffer + + HRESULT _CheckCmodForCallConv( // S_OK, -1 if found, or error. + PCCOR_SIGNATURE pbSig, // [IN] Signature to check. + ULONG *pcbTotal, // [OUT] Put bytes consumed here. + ULONG *pCallConv); // [OUT] If found, put calling convention here. + + HRESULT _SearchOneArgForCallConv( // S_OK, -1 if found, or error. + PCCOR_SIGNATURE pbSig, // [IN] Signature to check. + ULONG *pcbTotal, // [OUT] Put bytes consumed here. + ULONG *pCallConv); // [OUT] If found, put calling convention here. + + + + int inline IsGlobalMethodParent(mdTypeDef *ptd) + { + if (IsGlobalMethodParentTk(*ptd)) + { + *ptd = m_tdModule; + return (true); + } + return (false); + } + + int inline IsGlobalMethodParentToken(mdTypeDef td) + { + return (!IsNilToken(m_tdModule) && td == m_tdModule); + } + + FORCEINLINE BOOL IsENCOn() + { + _ASSERTE( ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC) == + m_pStgdb->m_MiniMd.IsENCOn() ); + return (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC; + } + + FORCEINLINE BOOL IsIncrementalOn() + { + return (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateIncremental; + } + + FORCEINLINE BOOL CheckDups(CorCheckDuplicatesFor checkdup) + { + return ((m_OptionValue.m_DupCheck & checkdup) || + (m_OptionValue.m_UpdateMode == MDUpdateIncremental || + m_OptionValue.m_UpdateMode == MDUpdateENC) ); + } + + FORCEINLINE HRESULT UpdateENCLog(mdToken tk, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault) + { + _ASSERTE( ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC) == + m_pStgdb->m_MiniMd.IsENCOn() ); + return m_pStgdb->m_MiniMd.UpdateENCLog(tk, funccode); + } + + FORCEINLINE HRESULT UpdateENCLog2(ULONG ixTbl, ULONG iRid, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault) + { + _ASSERTE( ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC) == + m_pStgdb->m_MiniMd.IsENCOn() ); + return m_pStgdb->m_MiniMd.UpdateENCLog2(ixTbl, iRid, funccode); + } + + FORCEINLINE bool IsCallerDefine() { return m_SetAPICaller == DEFINE_API; } + FORCEINLINE void SetCallerDefine() { m_SetAPICaller = DEFINE_API; } + FORCEINLINE bool IsCallerExternal() { return m_SetAPICaller == EXTERNAL_CALLER; } + FORCEINLINE void SetCallerExternal() { m_SetAPICaller = EXTERNAL_CALLER; } + +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN + bool IsSafeToDeleteStgdb() + { + return m_safeToDeleteStgdb && m_pStgdb->m_MiniMd.IsSafeToDelete(); + } + + FORCEINLINE void MarkUnsafeToDeleteStgdb() { m_safeToDeleteStgdb = false; } + FORCEINLINE void MarkSafeToDeleteStgdb() { m_safeToDeleteStgdb = true; } +#endif + + // Define Validate methods for all tables. +#undef MiniMdTable +#define MiniMdTable(x) HRESULT Validate##x(RID rid); + MiniMdTables() +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + PortablePdbMiniMdTables() +#endif + + // Validate a record in a generic sense using Meta-Meta data. + STDMETHODIMP ValidateRecord(ULONG ixTbl, ULONG ulRow); + + // Validate if the signature is properly formed with regards to the + // compression scheme. + STDMETHODIMP ValidateSigCompression( + mdToken tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig); // [IN] Size in bytes of the signature. + + // Validate one argument given the offset to the beginning of the + // argument, size of the full signature and the currentl offset value. + STDMETHODIMP ValidateOneArg( + mdToken tk, // [IN] Token whose signature is being processed. + PCCOR_SIGNATURE &pbSig, // [IN] Pointer to the beginning of argument. + ULONG cbSig, // [IN] Size in bytes of the full signature. + ULONG *pulCurByte, // [IN/OUT] Current offset into the signature.. + ULONG *pulNSentinels, // [IN/OUT] Number of sentinels + BOOL bNoVoidAllowed); // [IN] Flag indicating whether "void" is disallowed for this arg + + // Validate the given Method signature. + STDMETHODIMP ValidateMethodSig( + mdToken tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size in bytes of the signature. + DWORD dwFlags); // [IN] Method flags. + + // Validate the given Field signature. + STDMETHODIMP ValidateFieldSig( + mdToken tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig); // [IN] Size in bytes of the signature. + + // Validate the given MethodSpec signature. + STDMETHODIMP ValidateMethodSpecSig( + mdMethodSpec tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size in bytes of the signature. + ULONG *ulArity); // [OUT] Arity of the instantiation. + + +protected: + + // This scope's Stgdb. This stores the actual data which the class then exposes. + // This storage may be shared by an internal metadata object too. + // This is read-write so that the RegMeta class can implement the emit interfaces. + CLiteWeightStgdbRW *m_pStgdb; + + CLiteWeightStgdbRW *m_pStgdbFreeList; // This scope's Stgdb. + mdTypeDef m_tdModule; // The global module. + IUnknown *m_pUnk; // The IUnknown that owns the Stgdb. + FilterManager *m_pFilterManager; // Contains helper functions for marking + +#ifdef FEATURE_METADATA_INTERNAL_APIS + // Pointer to internal interface. This is a weak reference (it doesn't addref/release). + IMDInternalImport *m_pInternalImport; +#endif //FEATURE_METADATA_INTERNAL_APIS + + UTSemReadWrite *m_pSemReadWrite; + unsigned m_fOwnSem : 1; + unsigned m_bRemap : 1; // If true, there is a token mapper. + unsigned m_bSaveOptimized : 1; // If true, save optimization has been done. + unsigned m_hasOptimizedRefToDef : 1; // true if we have performed ref to def optimization + IUnknown *m_pHandler; + bool m_fIsTypeDefDirty; // This flag is set when the TypeRef to TypeDef map is not valid + bool m_fIsMemberDefDirty; // This flag is set when the MemberRef to MemberDef map is not valid + bool m_fStartedEE; // Set when EE runtime has been started up. + IUnknown *m_pAppDomain; // AppDomain in which managed security code will be run. + +private: + ULONG m_OpenFlags; // Open time flags. + + LONG m_cRef; // Ref count. + IUnknown *m_pFreeThreadedMarshaler; // FreeThreadedMarshaler + +#ifdef FEATURE_METADATA_PERF_STATS + MDCompilerPerf m_MDCompilerPerf; // Compiler perf object to store all stats. +#endif + + // If true, cached in list of global scopes. This is very dangerous because it may allow + // unpredictable state sharing between seemingly unrelated dispensers. + bool m_bCached; + + OptionValue m_OptionValue; + + mdTypeRef m_trLanguageType; + + // Specifies whether the caller of the Set API is one of the Define functions + // or an external API. This allows for performance optimization in the Set APIs + // by not checking for Duplicates in certain cases. + SetAPICallerType m_SetAPICaller; + + CorValidatorModuleType m_ModuleType; + CCustAttrHash m_caHash; // Hashed list of custom attribute types seen. + + bool m_bKeepKnownCa; // Should all known CA's be kept? + + CorProfileData *m_pCorProfileData; + + MetaDataReorderingOptions m_ReorderingOptions; + +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN + bool m_safeToDeleteStgdb; // This starts out true, but gets set to FALSE if we detect + // a RegMeta API call that might have given out an internal pointer. + // There is an equivalent state in MiniMD, and both must be + // TRUE in order to delete safely. +#endif + +private: + // Returns pointer to zeros of size (cbSize). + // Used by public APIs to return compatible values with previous releases. + static const BYTE *GetPublicApiCompatibilityZerosOfSize(UINT32 cbSize); + // Returns pointer to zeros typed as type T. + // Used by public APIs to return compatible values with previous releases. + template<class T> + T *GetPublicApiCompatibilityZeros() + { + static_assert_no_msg(sizeof(T) <= sizeof(s_rgMetaDataPublicApiCompatibilityZeros)); + return reinterpret_cast<T *>(s_rgMetaDataPublicApiCompatibilityZeros); + } + // Zeros used by public APIs as return value (or pointer to this memory) for invalid input. + // It is used by methods: + // * code:RegMeta::GetPublicApiCompatibilityZeros, and + // * code:RegMeta::GetPublicApiCompatibilityZerosOfSize. + static const BYTE s_rgMetaDataPublicApiCompatibilityZeros[64]; + +}; // class RegMeta + + +#endif // __RegMeta__h__ diff --git a/src/coreclr/md/compiler/regmeta_compilersupport.cpp b/src/coreclr/md/compiler/regmeta_compilersupport.cpp new file mode 100644 index 00000000000..b2d635530b8 --- /dev/null +++ b/src/coreclr/md/compiler/regmeta_compilersupport.cpp @@ -0,0 +1,231 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RegMeta.cpp +// + +// +// Implementation for meta data public interface methods. +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" +#include "filtermanager.h" +#include "mdperf.h" +#include "switches.h" +#include "posterror.h" +#include "stgio.h" +#include "sstring.h" + +#include <metamodelrw.h> + +#define DEFINE_CUSTOM_NODUPCHECK 1 +#define DEFINE_CUSTOM_DUPCHECK 2 +#define SET_CUSTOM 3 + +#if defined(_DEBUG) && defined(_TRACE_REMAPS) +#define LOGGING +#endif +#include <log.h> + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +#ifdef FEATURE_METADATA_EMIT + +//***************************************************************************** +// Merge the pImport scope to this scope +//***************************************************************************** +STDMETHODIMP RegMeta::Merge( // S_OK or error. + IMetaDataImport *pImport, // [IN] The scope to be merged. + IMapToken *pHostMapToken, // [IN] Host IMapToken interface to receive token remap notification + IUnknown *pHandler) // [IN] An object to receive to receive error notification. +{ + return E_NOTIMPL; +} // RegMeta::Merge + + +//***************************************************************************** +// real merge takes place here +//***************************************************************************** +STDMETHODIMP RegMeta::MergeEnd() // S_OK or error. +{ + return E_NOTIMPL; +} // RegMeta::MergeEnd + + +//***************************************************************************** +// As the Stgdb object to get the save size for the metadata delta. +//***************************************************************************** +STDMETHODIMP RegMeta::GetDeltaSaveSize( // S_OK or error. + CorSaveSize fSave, // [IN] cssAccurate or cssQuick. + DWORD *pdwSaveSize) // [OUT] Put the size here. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Make sure we're in EnC mode + if (!IsENCOn()) + { + _ASSERTE(!"Not in EnC mode!"); + IfFailGo(META_E_NOT_IN_ENC_MODE); + } + + m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration(); + hr = GetSaveSize(fSave, pdwSaveSize); + m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration(); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::GetDeltaSaveSize + +//***************************************************************************** +// Saves a metadata delta to a file of a given name. +//***************************************************************************** +STDMETHODIMP RegMeta::SaveDelta( // S_OK or error. + LPCWSTR szFile, // [IN] The filename to save to. + DWORD dwSaveFlags) // [IN] Flags for the save. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + + // Make sure we're in EnC mode + if (!IsENCOn()) + { + _ASSERTE(!"Not in EnC mode!"); + IfFailGo(META_E_NOT_IN_ENC_MODE); + } + + + + m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration(); + hr = Save(szFile, dwSaveFlags); + m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration(); + +ErrExit: + + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::SaveDelta + +//***************************************************************************** +// Saves a metadata delta to a stream. +//***************************************************************************** +STDMETHODIMP RegMeta::SaveDeltaToStream( // S_OK or error. + IStream *pIStream, // [IN] A writable stream to save to. + DWORD dwSaveFlags) // [IN] Flags for the save. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Make sure we're in EnC mode + if (!IsENCOn()) + { + _ASSERTE(!"Not in EnC mode!"); + IfFailGo(META_E_NOT_IN_ENC_MODE); + } + + + + m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration(); + hr = SaveToStream(pIStream, dwSaveFlags); + m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration(); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::SaveDeltaToStream + +//***************************************************************************** +// Saves a copy of the scope into the memory buffer provided. The buffer size +// must be at least as large as the GetSaveSize value. +//***************************************************************************** +STDMETHODIMP RegMeta::SaveDeltaToMemory( // S_OK or error. + void *pbData, // [OUT] Location to write data. + ULONG cbData) // [IN] Max size of data buffer. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Make sure we're in EnC mode + if (!IsENCOn()) + { + _ASSERTE(!"Not in EnC mode!"); + IfFailGo(META_E_NOT_IN_ENC_MODE); + } + + + m_pStgdb->m_MiniMd.EnableDeltaMetadataGeneration(); + hr = SaveToMemory(pbData, cbData); + m_pStgdb->m_MiniMd.DisableDeltaMetadataGeneration(); + +ErrExit: + + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::SaveDeltaToMemory + +//***************************************************************************** +// Resets the current edit and continue session +// +// Implements public API code:IMetaDataEmit2::ResetENCLog. +//***************************************************************************** +STDMETHODIMP +RegMeta::ResetENCLog() +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Make sure we're in EnC mode + if (!IsENCOn()) + { + _ASSERTE(!"Not in EnC mode!"); + IfFailGo(META_E_NOT_IN_ENC_MODE); + } + + IfFailGo(m_pStgdb->m_MiniMd.ResetENCLog()); +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::ResetENCLog + +#endif //FEATURE_METADATA_EMIT diff --git a/src/coreclr/md/compiler/regmeta_emit.cpp b/src/coreclr/md/compiler/regmeta_emit.cpp new file mode 100644 index 00000000000..b2b53798f96 --- /dev/null +++ b/src/coreclr/md/compiler/regmeta_emit.cpp @@ -0,0 +1,2042 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: RegMeta_IMetaDataImport.cpp +// + +// +// Some methods of code:RegMeta class which implement public API interfaces: +// * code:IMetaDataEmit +// * code:IMetaDataEmit2 +// +// ====================================================================================== + +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" +#include "filtermanager.h" +#include "mdperf.h" +#include "switches.h" +#include "posterror.h" +#include "stgio.h" +#include "sstring.h" + +#include <metamodelrw.h> + +#define DEFINE_CUSTOM_NODUPCHECK 1 +#define DEFINE_CUSTOM_DUPCHECK 2 +#define SET_CUSTOM 3 + +#if defined(_DEBUG) && defined(_TRACE_REMAPS) +#define LOGGING +#endif +#include <log.h> + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +#ifdef FEATURE_METADATA_EMIT + +//***************************************************************************** +// Set module properties on a scope. +//***************************************************************************** +STDMETHODIMP RegMeta::SetModuleProps( // S_OK or error. + LPCWSTR szName) // [IN] If not NULL, the name to set. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + ModuleRec *pModule; // The module record to modify. + + LOG((LOGMD, "RegMeta::SetModuleProps(%S)\n", MDSTR(szName))); + + + START_MD_PERF() + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + IfFailGo(m_pStgdb->m_MiniMd.GetModuleRecord(1, &pModule)); + if (szName != NULL) + { + LPCWSTR szFile = NULL; + size_t cchFile; + + SplitPathInterior(szName, NULL, 0, NULL, 0, &szFile, &cchFile, NULL, 0); + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Module, ModuleRec::COL_Name, pModule, szFile)); + } + + IfFailGo(UpdateENCLog(TokenFromRid(1, mdtModule))); + +ErrExit: + + STOP_MD_PERF(SetModuleProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::SetModuleProps() + +//***************************************************************************** +// Saves a scope to a file of a given name. +//***************************************************************************** +STDMETHODIMP RegMeta::Save( // S_OK or error. + LPCWSTR szFile, // [IN] The filename to save to. + DWORD dwSaveFlags) // [IN] Flags for the save. +{ + HRESULT hr=S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::Save(%S, 0x%08x)\n", MDSTR(szFile), dwSaveFlags)); + START_MD_PERF() + LOCKWRITE(); + + // Check reserved param.. + if (dwSaveFlags != 0) + IfFailGo (E_INVALIDARG); + IfFailGo(PreSave()); + IfFailGo(m_pStgdb->Save(szFile, dwSaveFlags)); + + // Reset m_bSaveOptimized, this is to handle the incremental and ENC + // scenerios where one may do multiple saves. + _ASSERTE(m_bSaveOptimized && !m_pStgdb->m_MiniMd.IsPreSaveDone()); + m_bSaveOptimized = false; + +#if defined(_DEBUG) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump)) + { + int DumpMD_impl(RegMeta *pMD); + DumpMD_impl(this); + } +#endif // _DEBUG + +ErrExit: + + STOP_MD_PERF(Save); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::Save() + +//***************************************************************************** +// Saves a scope to a stream. +//***************************************************************************** +STDMETHODIMP RegMeta::SaveToStream( // S_OK or error. + IStream *pIStream, // [IN] A writable stream to save to. + DWORD dwSaveFlags) // [IN] Flags for the save. +{ + HRESULT hr=S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOCKWRITE(); + + LOG((LOGMD, "RegMeta::SaveToStream(0x%08x, 0x%08x)\n", pIStream, dwSaveFlags)); + START_MD_PERF() + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + hr = _SaveToStream(pIStream, dwSaveFlags); + + STOP_MD_PERF(SaveToStream); + +#if defined(_DEBUG) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump)) + { + int DumpMD_impl(RegMeta *pMD); + DumpMD_impl(this); + } +#endif // _DEBUG + +ErrExit: + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::SaveToStream() + +//***************************************************************************** +// Saves a scope to a stream. +//***************************************************************************** +HRESULT RegMeta::_SaveToStream( // S_OK or error. + IStream *pIStream, // [IN] A writable stream to save to. + DWORD dwSaveFlags) // [IN] Flags for the save. +{ + HRESULT hr=S_OK; + + IfFailGo(PreSave()); + IfFailGo( m_pStgdb->SaveToStream(pIStream, m_ReorderingOptions, m_pCorProfileData) ); + + // Reset m_bSaveOptimized, this is to handle the incremental and ENC + // scenerios where one may do multiple saves. + _ASSERTE(m_bSaveOptimized && !m_pStgdb->m_MiniMd.IsPreSaveDone()); + m_bSaveOptimized = false; + +ErrExit: + + return hr; +} // STDMETHODIMP RegMeta::_SaveToStream() + +//***************************************************************************** +// Saves a copy of the scope into the memory buffer provided. The buffer size +// must be at least as large as the GetSaveSize value. +//***************************************************************************** +STDMETHODIMP RegMeta::SaveToMemory( // S_OK or error. + void *pbData, // [OUT] Location to write data. + ULONG cbData) // [IN] Max size of data buffer. +{ + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + IStream *pStream = 0; // Working pointer for save. + + LOG((LOGMD, "MD RegMeta::SaveToMemory(0x%08x, 0x%08x)\n", + pbData, cbData)); + START_MD_PERF(); + +#ifdef _DEBUG + ULONG cbActual; // Size of the real data. + IfFailGo(GetSaveSize(cssAccurate, &cbActual)); + _ASSERTE(cbData >= cbActual); +#endif + + { // cannot lock before the debug statement. Because GetSaveSize is also a public API which will take the Write lock. + LOCKWRITE(); + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + // Create a stream interface on top of the user's data buffer, then simply + // call the save to stream method. + IfFailGo(CInMemoryStream::CreateStreamOnMemory(pbData, cbData, &pStream)); + IfFailGo(_SaveToStream(pStream, 0)); + } +ErrExit: + if (pStream) + pStream->Release(); + STOP_MD_PERF(SaveToMemory); + END_ENTRYPOINT_NOTHROW; + + return (hr); +} // STDMETHODIMP RegMeta::SaveToMemory() + +//***************************************************************************** +// As the Stgdb object to get the save size for the scope. +//***************************************************************************** +STDMETHODIMP RegMeta::GetSaveSize( // S_OK or error. + CorSaveSize fSave, // [IN] cssAccurate or cssQuick. + DWORD *pdwSaveSize) // [OUT] Put the size here. +{ + HRESULT hr=S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + FilterTable *ft = NULL; + + LOG((LOGMD, "RegMeta::GetSaveSize(0x%08x, 0x%08x)\n", fSave, pdwSaveSize)); + START_MD_PERF(); + LOCKWRITE(); + + ft = m_pStgdb->m_MiniMd.GetFilterTable(); + IfNullGo(ft); + + if (m_pStgdb->m_MiniMd.m_UserStringHeap.GetUnalignedSize() == 0) + { + if (!IsENCDelta(m_pStgdb->m_MiniMd.m_OptionValue.m_UpdateMode) && + !m_pStgdb->m_MiniMd.IsMinimalDelta()) + { + BYTE rgData[] = {' ', 0, 0}; + UINT32 nIndex; + IfFailGo(m_pStgdb->m_MiniMd.PutUserString( + MetaData::DataBlob(rgData, sizeof(rgData)), + &nIndex)); + // Make sure this user string is marked + if (ft->Count() != 0) + { + IfFailGo( m_pFilterManager->MarkNewUserString(TokenFromRid(nIndex, mdtString))); + } + } + } + + + if (ft->Count() != 0) + { + int iCount; + + // There is filter table. Linker is using /opt:ref. + // Make sure that we are marking the AssemblyDef token! + iCount = m_pStgdb->m_MiniMd.getCountAssemblys(); + _ASSERTE(iCount <= 1); + + if (iCount) + { + IfFailGo(m_pFilterManager->Mark(TokenFromRid(iCount, mdtAssembly))); + } + } + + IfFailGo(PreSave()); + + hr = m_pStgdb->GetSaveSize(fSave, (UINT32 *)pdwSaveSize, m_ReorderingOptions, m_pCorProfileData); + +ErrExit: + STOP_MD_PERF(GetSaveSize); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::GetSaveSize + +#ifdef FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// Unmark everything in this module +// +// Implements public API code:IMetaDataFilter::UnmarkAll. +//***************************************************************************** +HRESULT RegMeta::UnmarkAll() +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + int i; + int iCount; + TypeDefRec *pRec; + ULONG ulEncloser; + NestedClassRec *pNestedClass; + CustomAttributeRec *pCARec; + mdToken tkParent; + int iStart, iEnd; + + LOG((LOGMD, "RegMeta::UnmarkAll\n")); + + START_MD_PERF(); + LOCKWRITE(); + +#if 0 + // We cannot enable this check. Because our tests are depending on this.. Sigh.. + if (m_pFilterManager != NULL) + { + // UnmarkAll has been called before + IfFailGo( META_E_HAS_UNMARKALL ); + } +#endif // 0 + + // calculate the TypeRef and TypeDef mapping here + // + IfFailGo( RefToDefOptimization() ); + + // unmark everything in the MiniMd. + IfFailGo( m_pStgdb->m_MiniMd.UnmarkAll() ); + + // instantiate the filter manager + m_pFilterManager = new (nothrow) FilterManager( &(m_pStgdb->m_MiniMd) ); + IfNullGo( m_pFilterManager ); + + // Mark all public typedefs. + iCount = m_pStgdb->m_MiniMd.getCountTypeDefs(); + + // Mark all of the public TypeDef. We need to skip over the <Module> typedef + for (i = 2; i <= iCount; i++) + { + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(i, &pRec)); + if (m_OptionValue.m_LinkerOption == MDNetModule) + { + // Client is asking us to keep private type as well. + IfFailGo( m_pFilterManager->Mark(TokenFromRid(i, mdtTypeDef)) ); + } + else if (i != 1) + { + // when client is not set to MDNetModule, global functions/fields won't be keep by default + // + if (IsTdPublic(pRec->GetFlags())) + { + IfFailGo( m_pFilterManager->Mark(TokenFromRid(i, mdtTypeDef)) ); + } + else if ( IsTdNestedPublic(pRec->GetFlags()) || + IsTdNestedFamily(pRec->GetFlags()) || + IsTdNestedFamORAssem(pRec->GetFlags()) ) + { + // This nested class would potentially be visible outside, either + // directly or through inheritence. If the enclosing class is + // marked, this nested class must be marked. + // + IfFailGo(m_pStgdb->m_MiniMd.FindNestedClassHelper(TokenFromRid(i, mdtTypeDef), &ulEncloser)); + _ASSERTE( !InvalidRid(ulEncloser) && + "Bad metadata for nested type!" ); + IfFailGo(m_pStgdb->m_MiniMd.GetNestedClassRecord(ulEncloser, &pNestedClass)); + tkParent = m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pNestedClass); + if ( m_pStgdb->m_MiniMd.GetFilterTable()->IsTypeDefMarked(tkParent)) + IfFailGo( m_pFilterManager->Mark(TokenFromRid(i, mdtTypeDef)) ); + } + } + } + + if (m_OptionValue.m_LinkerOption == MDNetModule) + { + // Mark global function if NetModule. We will not keep _Delete method. + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(1, &pRec)); + iStart = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(1, (RID *)&iEnd)); + for ( i = iStart; i < iEnd; i ++ ) + { + RID rid; + MethodRec *pMethodRec; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(i, &rid)); + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(rid, &pMethodRec)); + + // check the name + if (IsMdRTSpecialName(pMethodRec->GetFlags())) + { + LPCUTF8 szName; + IfFailGo(m_pStgdb->m_MiniMd.getNameOfMethod(pMethodRec, &szName)); + + // Only mark method if not a _Deleted method + if (strcmp(szName, COR_DELETED_NAME_A) != 0) + IfFailGo( m_pFilterManager->Mark( TokenFromRid( rid, mdtMethodDef) ) ); + } + else + { + // + if (!IsMiForwardRef(pMethodRec->GetImplFlags()) || + IsMiRuntime(pMethodRec->GetImplFlags()) || + IsMdPinvokeImpl(pMethodRec->GetFlags()) ) + + IfFailGo( m_pFilterManager->Mark( TokenFromRid( rid, mdtMethodDef) ) ); + } + } + } + + // mark the module property + IfFailGo( m_pFilterManager->Mark(TokenFromRid(1, mdtModule)) ); + + // We will also keep all of the TypeRef that has any CustomAttribute hang off it. + iCount = m_pStgdb->m_MiniMd.getCountCustomAttributes(); + + // Mark all of the TypeRef used by CA's + for (i = 1; i <= iCount; i++) + { + IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(i, &pCARec)); + tkParent = m_pStgdb->m_MiniMd.getParentOfCustomAttribute(pCARec); + if (TypeFromToken(tkParent) == mdtTypeRef) + { + m_pFilterManager->Mark(tkParent); + } + } +ErrExit: + + STOP_MD_PERF(UnmarkAll); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::UnmarkAll + +#endif //FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// Mark everything in this module +//***************************************************************************** +HRESULT RegMeta::MarkAll() +{ + HRESULT hr = NOERROR; + + // mark everything in the MiniMd. + IfFailGo( m_pStgdb->m_MiniMd.MarkAll() ); + + // instantiate the filter manager if not instantiated + if (m_pFilterManager == NULL) + { + m_pFilterManager = new (nothrow) FilterManager( &(m_pStgdb->m_MiniMd) ); + IfNullGo( m_pFilterManager ); + } +ErrExit: + + return hr; +} // HRESULT RegMeta::MarkAll + +#ifdef FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// Mark the transitive closure of a token +//@todo GENERICS: What about GenericParam, MethodSpec? +// +// Implements public API code:IMetaDataFilter::MarkToken. +//***************************************************************************** +STDMETHODIMP RegMeta::MarkToken( // Return code. + mdToken tk) // [IN] token to be Marked +{ + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + // LOG((LOGMD, "RegMeta::MarkToken(0x%08x)\n", tk)); + START_MD_PERF(); + LOCKWRITE(); + + if (m_pStgdb->m_MiniMd.GetFilterTable() == NULL || m_pFilterManager == NULL) + { + // UnmarkAll has not been called. Everything is considered marked. + // No need to do anything extra! + IfFailGo( META_E_MUST_CALL_UNMARKALL ); + } + + switch ( TypeFromToken(tk) ) + { + case mdtTypeDef: + case mdtMethodDef: + case mdtFieldDef: + case mdtMemberRef: + case mdtTypeRef: + case mdtTypeSpec: + case mdtMethodSpec: + case mdtSignature: + case mdtString: +#if _DEBUG + if (TypeFromToken(tk) == mdtTypeDef) + { + TypeDefRec *pType; + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tk), &pType)); + LPCSTR szTypeDefName; + if (m_pStgdb->m_MiniMd.getNameOfTypeDef(pType, &szTypeDefName) == S_OK) + { + LOG((LOGMD, "MarkToken: Host is marking typetoken 0x%08x with name <%s>\n", tk, szTypeDefName)); + } + } + else + if (TypeFromToken(tk) == mdtMethodDef) + { + MethodRec *pMeth; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMeth)); + LPCSTR szMethodName; + if (m_pStgdb->m_MiniMd.getNameOfMethod(pMeth, &szMethodName) == S_OK) + { + LOG((LOGMD, "MarkToken: Host is marking methodtoken 0x%08x with name <%s>\n", tk, szMethodName)); + } + } + else + if (TypeFromToken(tk) == mdtFieldDef) + { + FieldRec *pField; + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tk), &pField)); + LPCSTR szFieldName; + if (m_pStgdb->m_MiniMd.getNameOfField(pField, &szFieldName) == S_OK) + { + LOG((LOGMD, "MarkToken: Host is marking field token 0x%08x with name <%s>\n", tk, szFieldName)); + } + } + else + { + LOG((LOGMD, "MarkToken: Host is marking token 0x%08x\n", tk)); + } +#endif // _DEBUG + if (!IsValidToken(tk)) + IfFailGo( E_INVALIDARG ); + + IfFailGo( m_pFilterManager->Mark(tk) ); + break; + + case mdtBaseType: + // no need to mark base type + goto ErrExit; + + default: + _ASSERTE(!"Bad token type!"); + hr = E_INVALIDARG; + break; + } +ErrExit: + + STOP_MD_PERF(MarkToken); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::MarkToken + +//***************************************************************************** +// Unmark everything in this module +//@todo GENERICS: What about GenericParam, MethodSpec? +// +// Implements public API code:IMetaDataFilter::IsTokenMarked. +//***************************************************************************** +HRESULT RegMeta::IsTokenMarked( + mdToken tk, // [IN] Token to check if marked or not + BOOL *pIsMarked) // [OUT] true if token is marked +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + FilterTable *pFilter = NULL; + + LOG((LOGMD, "RegMeta::IsTokenMarked(0x%08x)\n", tk)); + START_MD_PERF(); + LOCKREAD(); + + pFilter = m_pStgdb->m_MiniMd.GetFilterTable(); + IfNullGo( pFilter ); + + if (!IsValidToken(tk)) + IfFailGo( E_INVALIDARG ); + + switch ( TypeFromToken(tk) ) + { + case mdtTypeRef: + *pIsMarked = pFilter->IsTypeRefMarked(tk); + break; + case mdtTypeDef: + *pIsMarked = pFilter->IsTypeDefMarked(tk); + break; + case mdtFieldDef: + *pIsMarked = pFilter->IsFieldMarked(tk); + break; + case mdtMethodDef: + *pIsMarked = pFilter->IsMethodMarked(tk); + break; + case mdtParamDef: + *pIsMarked = pFilter->IsParamMarked(tk); + break; + case mdtMemberRef: + *pIsMarked = pFilter->IsMemberRefMarked(tk); + break; + case mdtCustomAttribute: + *pIsMarked = pFilter->IsCustomAttributeMarked(tk); + break; + case mdtPermission: + *pIsMarked = pFilter->IsDeclSecurityMarked(tk); + break; + case mdtSignature: + *pIsMarked = pFilter->IsSignatureMarked(tk); + break; + case mdtEvent: + *pIsMarked = pFilter->IsEventMarked(tk); + break; + case mdtProperty: + *pIsMarked = pFilter->IsPropertyMarked(tk); + break; + case mdtModuleRef: + *pIsMarked = pFilter->IsModuleRefMarked(tk); + break; + case mdtTypeSpec: + *pIsMarked = pFilter->IsTypeSpecMarked(tk); + break; + case mdtInterfaceImpl: + *pIsMarked = pFilter->IsInterfaceImplMarked(tk); + break; + case mdtString: + default: + _ASSERTE(!"Bad token type!"); + hr = E_INVALIDARG; + break; + } +ErrExit: + + STOP_MD_PERF(IsTokenMarked); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::IsTokenMarked + +#endif //FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// Create and populate a new TypeDef record. +//***************************************************************************** +STDMETHODIMP RegMeta::DefineTypeDef( // S_OK or error. + LPCWSTR szTypeDef, // [IN] Name of TypeDef + DWORD dwTypeDefFlags, // [IN] CustomAttribute flags + mdToken tkExtends, // [IN] extends this TypeDef or typeref + mdToken rtkImplements[], // [IN] Implements interfaces + mdTypeDef *ptd) // [OUT] Put TypeDef token here +{ + HRESULT hr = S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::DefineTypeDef(%S, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + MDSTR(szTypeDef), dwTypeDefFlags, tkExtends, + rtkImplements, ptd)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + _ASSERTE(!IsTdNested(dwTypeDefFlags)); + + IfFailGo(_DefineTypeDef(szTypeDef, dwTypeDefFlags, + tkExtends, rtkImplements, mdTokenNil, ptd)); +ErrExit: + STOP_MD_PERF(DefineTypeDef); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::DefineTypeDef() + + +//***************************************************************************** +// Implements public API code:IMetaDataFilter::SetHandler. +//***************************************************************************** +STDMETHODIMP RegMeta::SetHandler( // S_OK. + IUnknown *pUnk) // [IN] The new error handler. +{ + HRESULT hr = S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + IMapToken *pIMap = NULL; + + LOG((LOGMD, "RegMeta::SetHandler(0x%08x)\n", pUnk)); + START_MD_PERF(); + LOCKWRITE(); + + m_pHandler = pUnk; + + // Ignore the error return by SetHandler + IfFailGo(m_pStgdb->m_MiniMd.SetHandler(pUnk)); + + // Figure out up front if remap is supported. + if (pUnk) + pUnk->QueryInterface(IID_IMapToken, (PVOID *) &pIMap); + m_bRemap = (pIMap != 0); + if (pIMap) + pIMap->Release(); + +ErrExit: + + STOP_MD_PERF(SetHandler); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::SetHandler() + +//******************************************************************************* +// Internal helper functions. +//******************************************************************************* + +//******************************************************************************* +// Perform optimizations of the metadata prior to saving. +//******************************************************************************* +HRESULT RegMeta::PreSave() // Return code. +{ + HRESULT hr = S_OK; // A result. + CMiniMdRW *pMiniMd; // The MiniMd with the data. + unsigned bRemapOld = m_bRemap; + + // For convenience. + pMiniMd = &(m_pStgdb->m_MiniMd); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + // If the code has already been optimized there is nothing to do. + if (m_bSaveOptimized) + goto ErrExit; + + IfFailGo(RefToDefOptimization()); + + // we need to update MethodImpl table here with ref to def result + if (pMiniMd->GetMemberRefToMemberDefMap() != NULL) + { + MethodImplRec *pMethodImplRec; + mdToken tkMethodBody; + mdToken tkMethodDecl; + mdToken newTK; + ULONG cMethodImplRecs; // Count of MemberRefs. + ULONG iMI; + + cMethodImplRecs = pMiniMd->getCountMethodImpls(); + // Enum through all member ref's looking for ref's to internal things. + for (iMI = 1; iMI <= cMethodImplRecs; iMI++) + { // Get a MethodImpl. + IfFailGo(pMiniMd->GetMethodImplRecord(iMI, &pMethodImplRec)); + tkMethodBody = pMiniMd->getMethodBodyOfMethodImpl(pMethodImplRec); + if (TypeFromToken(tkMethodBody) == mdtMemberRef) + { + // did it get remapped to a def + newTK = *(pMiniMd->GetMemberRefToMemberDefMap()->Get(RidFromToken(tkMethodBody))); + if (!IsNilToken(newTK)) + { + // yes... fix up the value... + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, + MethodImplRec::COL_MethodBody, + pMethodImplRec, + newTK)); + } + } + // do the same thing for MethodDecl + tkMethodDecl = pMiniMd->getMethodDeclarationOfMethodImpl(pMethodImplRec); + if (TypeFromToken(tkMethodDecl) == mdtMemberRef) + { + // did it get remapped to a def + newTK = *(pMiniMd->GetMemberRefToMemberDefMap()->Get(RidFromToken(tkMethodDecl))); + if (!IsNilToken(newTK)) + { + // yes... fix up the value... + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodImpl, + MethodImplRec::COL_MethodDeclaration, + pMethodImplRec, + newTK)); + } + } + } + } + + // reget the minimd because it can be swapped in the call of ProcessFilter + pMiniMd = &(m_pStgdb->m_MiniMd); + + // Don't repeat this process again. + m_bSaveOptimized = true; + + // call get save size to trigger the PreSaveXXX on MetaModelRW class. + IfFailGo(m_pStgdb->m_MiniMd.PreSave(m_ReorderingOptions, m_pCorProfileData)); + +ErrExit: + m_bRemap = bRemapOld; + + return hr; +} // RegMeta::PreSave + +//******************************************************************************* +// Perform optimizations of ref to def +//******************************************************************************* +HRESULT RegMeta::RefToDefOptimization() +{ + mdToken mfdef; // Method or Field Def. + LPCSTR szName; // MemberRef or TypeRef name. + const COR_SIGNATURE *pvSig; // Signature of the MemberRef. + ULONG cbSig; // Size of the signature blob. + HRESULT hr = S_OK; // A result. + ULONG iMR; // For iterating MemberRefs. + CMiniMdRW *pMiniMd; // The MiniMd with the data. + ULONG cMemberRefRecs; // Count of MemberRefs. + MemberRefRec *pMemberRefRec; // A MemberRefRec. + + + + START_MD_PERF(); + + // the Ref to Def map is still up-to-date + if (IsMemberDefDirty() == false && IsTypeDefDirty() == false && m_hasOptimizedRefToDef == true) + goto ErrExit; + + pMiniMd = &(m_pStgdb->m_MiniMd); + + // The basic algorithm here is: + // + // calculate all of the TypeRef to TypeDef map and store it at TypeRefToTypeDefMap + // for each MemberRef mr + // { + // get the parent of mr + // if (parent of mr is a TypeRef and has been mapped to a TypeDef) + // { + // Remap MemberRef to MemberDef + // } + // } + // + // There are several places where errors are eaten, since this whole thing is + // an optimization step and not doing it would still be valid. + // + + // Ensure the size + // initialize the token remap manager. This class will track all of the Refs to Defs map and also + // token movements due to removing pointer tables or sorting. + // + if ( pMiniMd->GetTokenRemapManager() == NULL) + { + + IfFailGo( pMiniMd->InitTokenRemapManager() ); + } + else + { + IfFailGo( pMiniMd->GetTokenRemapManager()->ClearAndEnsureCapacity(pMiniMd->getCountTypeRefs(), pMiniMd->getCountMemberRefs())); + } + + // If this is the first time or more TypeDef has been introduced, recalculate the TypeRef to TypeDef map + if (IsTypeDefDirty() || m_hasOptimizedRefToDef == false) + { + IfFailGo( pMiniMd->CalculateTypeRefToTypeDefMap() ); + } + + // If this is the first time or more memberdefs has been introduced, recalculate the TypeRef to TypeDef map + if (IsMemberDefDirty() || m_hasOptimizedRefToDef == false) + { + mdToken tkParent; + cMemberRefRecs = pMiniMd->getCountMemberRefs(); + + // Enum through all member ref's looking for ref's to internal things. + for (iMR = 1; iMR<=cMemberRefRecs; iMR++) + { // Get a MemberRef. + IfFailGo(pMiniMd->GetMemberRefRecord(iMR, &pMemberRefRec)); + + // If not member of the TypeRef, skip it. + tkParent = pMiniMd->getClassOfMemberRef(pMemberRefRec); + + if ( TypeFromToken(tkParent) == mdtMethodDef ) + { + // always track the map even though it is already in the original scope + *(pMiniMd->GetMemberRefToMemberDefMap()->Get(iMR)) = tkParent; + continue; + } + + if ( TypeFromToken(tkParent) != mdtTypeRef && TypeFromToken(tkParent) != mdtTypeDef ) + { + // this has been either optimized to mdtMethodDef, mdtFieldDef or referring to + // ModuleRef + continue; + } + + // In the case of global function, we have tkParent as m_tdModule. + // We will always do the optmization. + if (TypeFromToken(tkParent) == mdtTypeRef) + { + // If we're preserving local typerefs, skip this token + if (PreserveLocalRefs(MDPreserveLocalTypeRef)) + { + continue; + } + + // The parent is a TypeRef. We need to check to see if this TypeRef is optimized to a TypeDef + tkParent = *(pMiniMd->GetTypeRefToTypeDefMap()->Get(RidFromToken(tkParent)) ); + // tkParent = pMapTypeRefToTypeDef[RidFromToken(tkParent)]; + if ( RidFromToken(tkParent) == 0) + { + continue; + } + } + + // If we're preserving local memberrefs, skip this token + if (PreserveLocalRefs(MDPreserveLocalMemberRef)) + { + continue; + } + + // Get the name and signature of this mr. + IfFailGo(pMiniMd->getNameOfMemberRef(pMemberRefRec, &szName)); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pMemberRefRec, &pvSig, &cbSig)); + + // Look for a member with the same def. Might not be found if it is + // inherited from a base class. + //<TODO>@future: this should support inheritence checking. + // Look for a member with the same name and signature.</TODO> + hr = ImportHelper::FindMember(pMiniMd, tkParent, szName, pvSig, cbSig, &mfdef); + if (hr != S_OK) + { + #if _TRACE_REMAPS + // Log the failure. + LOG((LF_METADATA, LL_INFO10, "Member %S//%S.%S not found\n", szNamespace, szTDName, rcMRName)); + #endif + continue; + } + + // We will only record this if mfdef is a methoddef. We don't support + // parent of MemberRef as fielddef. As if we can optimize MemberRef to FieldDef, + // we can remove this row. + // + if ( (TypeFromToken(mfdef) == mdtMethodDef) && + (m_bRemap || tkParent == m_tdModule ) ) + { + // Always change the parent if it is the global function. + // Or change the parent if we have a remap that we can send notification. + // + IfFailGo(pMiniMd->PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pMemberRefRec, mfdef)); + } + + // We will always track the changes. In MiniMd::PreSaveFull, we will use this map to send + // notification to our host if there is any IMapToken provided. + // + *(pMiniMd->GetMemberRefToMemberDefMap()->Get(iMR)) = mfdef; + + } // EnumMemberRefs + } + + // Reset return code from likely search failures. + hr = S_OK; + + SetMemberDefDirty(false); + SetTypeDefDirty(false); + m_hasOptimizedRefToDef = true; +ErrExit: + STOP_MD_PERF(RefToDefOptimization); + + return hr; +} // RegMeta::RefToDefOptimization + +//***************************************************************************** +// Define a TypeRef given the fully qualified name. +//***************************************************************************** +HRESULT RegMeta::_DefineTypeRef( + mdToken tkResolutionScope, // [IN] ModuleRef or AssemblyRef. + const void *szName, // [IN] Name of the TypeRef. + BOOL isUnicode, // [IN] Specifies whether the URL is unicode. + mdTypeRef *ptk, // [OUT] Put mdTypeRef here. + eCheckDups eCheck) // [IN] Specifies whether to check for duplicates. +{ + HRESULT hr = S_OK; + LPUTF8 szUTF8FullQualName; + CQuickBytes qbNamespace; + CQuickBytes qbName; + int bSuccess; + ULONG ulStringLen; + + + + + _ASSERTE(ptk && szName); + _ASSERTE (TypeFromToken(tkResolutionScope) == mdtModule || + TypeFromToken(tkResolutionScope) == mdtModuleRef || + TypeFromToken(tkResolutionScope) == mdtAssemblyRef || + TypeFromToken(tkResolutionScope) == mdtTypeRef || + tkResolutionScope == mdTokenNil); + + if (isUnicode) + { + UTF8STR((LPCWSTR)szName, szUTF8FullQualName); + } + else + { + szUTF8FullQualName = (LPUTF8)szName; + } + PREFIX_ASSUME(szUTF8FullQualName != NULL); + + ulStringLen = (ULONG)(strlen(szUTF8FullQualName) + 1); + IfFailGo(qbNamespace.ReSizeNoThrow(ulStringLen)); + IfFailGo(qbName.ReSizeNoThrow(ulStringLen)); + bSuccess = ns::SplitPath(szUTF8FullQualName, + (LPUTF8)qbNamespace.Ptr(), + ulStringLen, + (LPUTF8)qbName.Ptr(), + ulStringLen); + _ASSERTE(bSuccess); + + // Search for existing TypeRef record. + if (eCheck==eCheckYes || (eCheck==eCheckDefault && CheckDups(MDDupTypeRef))) + { + hr = ImportHelper::FindTypeRefByName(&(m_pStgdb->m_MiniMd), tkResolutionScope, + (LPCUTF8)qbNamespace.Ptr(), + (LPCUTF8)qbName.Ptr(), ptk); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + hr = S_OK; + goto NormalExit; + } + else + { + hr = META_S_DUPLICATE; + goto NormalExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + // Create TypeRef record. + TypeRefRec *pRecord; + RID iRecord; + + IfFailGo(m_pStgdb->m_MiniMd.AddTypeRefRecord(&pRecord, &iRecord)); + + // record the more defs are introduced. + SetTypeDefDirty(true); + + // Give token back to caller. + *ptk = TokenFromRid(iRecord, mdtTypeRef); + + // Set the fields of the TypeRef record. + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeRef, TypeRefRec::COL_Namespace, + pRecord, (LPUTF8)qbNamespace.Ptr())); + + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeRef, TypeRefRec::COL_Name, + pRecord, (LPUTF8)qbName.Ptr())); + + if (!IsNilToken(tkResolutionScope)) + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_TypeRef, TypeRefRec::COL_ResolutionScope, + pRecord, tkResolutionScope)); + IfFailGo(UpdateENCLog(*ptk)); + + // Hash the name. + IfFailGo(m_pStgdb->m_MiniMd.AddNamedItemToHash(TBL_TypeRef, *ptk, (LPUTF8)qbName.Ptr(), 0)); + +ErrExit: + ; +NormalExit: + + return hr; +} // HRESULT RegMeta::_DefineTypeRef() + +//******************************************************************************* +// Define MethodSemantics +//******************************************************************************* +HRESULT RegMeta::_DefineMethodSemantics( // S_OK or error. + USHORT usAttr, // [IN] CorMethodSemanticsAttr. + mdMethodDef md, // [IN] Method. + mdToken tkAssoc, // [IN] Association. + BOOL bClear) // [IN] Specifies whether to delete the exisiting entries. +{ + HRESULT hr = S_OK; + MethodSemanticsRec *pRecord = 0; + MethodSemanticsRec *pRecord1; // Use this to recycle a MethodSemantics record. + RID iRecord; + HENUMInternal hEnum; + + + + _ASSERTE(TypeFromToken(md) == mdtMethodDef || IsNilToken(md)); + _ASSERTE(RidFromToken(tkAssoc)); + HENUMInternal::ZeroEnum(&hEnum); + + // Clear all matching records by setting association to a Nil token. + if (bClear) + { + RID i; + + IfFailGo( m_pStgdb->m_MiniMd.FindMethodSemanticsHelper(tkAssoc, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&i)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(i, &pRecord1)); + if (usAttr == pRecord1->GetSemantic()) + { + pRecord = pRecord1; + iRecord = i; + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSemantics, + MethodSemanticsRec::COL_Association, pRecord, mdPropertyNil)); + // In Whidbey, we should create ENC log record here. + } + } + } + // If setting (not just clearing) the association, do that now. + if (!IsNilToken(md)) + { + // Create a new record required + if (pRecord == NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.AddMethodSemanticsRecord(&pRecord, &iRecord)); + } + + // Save the data. + pRecord->SetSemantic(usAttr); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSemantics, + MethodSemanticsRec::COL_Method, pRecord, md)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_MethodSemantics, + MethodSemanticsRec::COL_Association, pRecord, tkAssoc)); + + // regardless if we reuse the record or create the record, add the MethodSemantics to the hash + IfFailGo( m_pStgdb->m_MiniMd.AddMethodSemanticsToHash(iRecord) ); + + // Create log record for non-token table. + IfFailGo(UpdateENCLog2(TBL_MethodSemantics, iRecord)); + } + +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + + return hr; +} // HRESULT RegMeta::_DefineMethodSemantics() + +//******************************************************************************* +// Turn the specified internal flags on. +//******************************************************************************* +HRESULT RegMeta::_TurnInternalFlagsOn( // S_OK or error. + mdToken tkObj, // [IN] Target object whose internal flags are targetted. + DWORD flags) // [IN] Specifies flags to be turned on. +{ + HRESULT hr; + MethodRec *pMethodRec; + FieldRec *pFieldRec; + TypeDefRec *pTypeDefRec; + + switch (TypeFromToken(tkObj)) + { + case mdtMethodDef: + IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkObj), &pMethodRec)); + pMethodRec->AddFlags(flags); + break; + case mdtFieldDef: + IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(tkObj), &pFieldRec)); + pFieldRec->AddFlags(flags); + break; + case mdtTypeDef: + IfFailRet(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkObj), &pTypeDefRec)); + pTypeDefRec->AddFlags(flags); + break; + default: + _ASSERTE(!"Not supported token type!"); + return E_INVALIDARG; + } + return S_OK; +} // RegMeta::_TurnInternalFlagsOn + +//***************************************************************************** +// Helper: Set the properties on the given TypeDef token. +//***************************************************************************** +HRESULT RegMeta::_SetTypeDefProps( // S_OK or error. + mdTypeDef td, // [IN] The TypeDef. + DWORD dwTypeDefFlags, // [IN] TypeDef flags. + mdToken tkExtends, // [IN] Base TypeDef or TypeRef. + mdToken rtkImplements[]) // [IN] Implemented interfaces. +{ + HRESULT hr = S_OK; // A result. + BOOL bClear = IsENCOn() || IsCallerExternal(); // Specifies whether to clear the InterfaceImpl records. + TypeDefRec *pRecord; // New TypeDef record. + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + _ASSERTE(TypeFromToken(tkExtends) == mdtTypeDef || TypeFromToken(tkExtends) == mdtTypeRef || TypeFromToken(tkExtends) == mdtTypeSpec || + IsNilToken(tkExtends) || tkExtends == UINT32_MAX); + + // Get the record. + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRecord)); + + if (dwTypeDefFlags != UINT32_MAX) + { + // No one should try to set the reserved flags explicitly. + _ASSERTE((dwTypeDefFlags & (tdReservedMask&~tdRTSpecialName)) == 0); + // Clear the reserved flags from the flags passed in. + dwTypeDefFlags &= (~tdReservedMask); + // Preserve the reserved flags stored. + dwTypeDefFlags |= (pRecord->GetFlags() & tdReservedMask); + // Set the flags. + pRecord->SetFlags(dwTypeDefFlags); + } + if (tkExtends != UINT32_MAX) + { + if (IsNilToken(tkExtends)) + tkExtends = mdTypeDefNil; + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_TypeDef, TypeDefRec::COL_Extends, + pRecord, tkExtends)); + } + + // Implemented interfaces. + if (rtkImplements) + IfFailGo(_SetImplements(rtkImplements, td, bClear)); + + IfFailGo(UpdateENCLog(td)); +ErrExit: + return hr; +} // HRESULT RegMeta::_SetTypeDefProps() + +//****************************************************************************** +// Creates and sets a row in the InterfaceImpl table. Optionally clear +// pre-existing records for the owning class. +//****************************************************************************** +HRESULT RegMeta::_SetImplements( // S_OK or error. + mdToken rTk[], // Array of TypeRef or TypeDef or TypeSpec tokens for implemented interfaces. + mdTypeDef td, // Implementing TypeDef. + BOOL bClear) // Specifies whether to clear the existing records. +{ + HRESULT hr = S_OK; + ULONG i = 0; + ULONG j; + InterfaceImplRec *pInterfaceImpl; + RID iInterfaceImpl; + RID ridStart; + RID ridEnd; + CQuickBytes cqbTk; + const mdToken *pTk; + bool fIsTableVirtualSortValid; + + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && rTk); + _ASSERTE(!m_bSaveOptimized && "Cannot change records after PreSave() and before Save()."); + + // Clear all exising InterfaceImpl records by setting the parent to Nil. + if (bClear) + { + IfFailGo(m_pStgdb->m_MiniMd.GetInterfaceImplsForTypeDef( + RidFromToken(td), &ridStart, &ridEnd)); + for (j = ridStart; j < ridEnd; j++) + { + IfFailGo(m_pStgdb->m_MiniMd.GetInterfaceImplRecord( + m_pStgdb->m_MiniMd.GetInterfaceImplRid(j), + &pInterfaceImpl)); + _ASSERTE (td == m_pStgdb->m_MiniMd.getClassOfInterfaceImpl(pInterfaceImpl)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_InterfaceImpl, InterfaceImplRec::COL_Class, + pInterfaceImpl, mdTypeDefNil)); + } + } + + // Eliminate duplicates from the array passed in. + if (CheckDups(MDDupInterfaceImpl)) + { + IfFailGo(_InterfaceImplDupProc(rTk, td, &cqbTk)); + pTk = (mdToken *)cqbTk.Ptr(); + } + else + pTk = rTk; + + // Get the state of InterfaceImpl table's VirtualSort + fIsTableVirtualSortValid = m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_InterfaceImpl); + // Loop for each implemented interface. + while (!IsNilToken(pTk[i])) + { + _ASSERTE(TypeFromToken(pTk[i]) == mdtTypeRef || TypeFromToken(pTk[i]) == mdtTypeDef + || TypeFromToken(pTk[i]) == mdtTypeSpec); + + // Create the interface implementation record. + IfFailGo(m_pStgdb->m_MiniMd.AddInterfaceImplRecord(&pInterfaceImpl, &iInterfaceImpl)); + + // Set data. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_InterfaceImpl, InterfaceImplRec::COL_Class, + pInterfaceImpl, td)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_InterfaceImpl, InterfaceImplRec::COL_Interface, + pInterfaceImpl, pTk[i])); + // Had the table valid VirtualSort? + if (fIsTableVirtualSortValid) + { // Validate table's VistualSort after adding 1 record and store its + // new validation state + IfFailGo(m_pStgdb->m_MiniMd.ValidateVirtualSortAfterAddRecord( + TBL_InterfaceImpl, + &fIsTableVirtualSortValid)); + } + + i++; + + IfFailGo(UpdateENCLog(TokenFromRid(mdtInterfaceImpl, iInterfaceImpl))); + } +ErrExit: + + return hr; +} // HRESULT RegMeta::_SetImplements() + +//****************************************************************************** +// This routine eliminates duplicates from the given list of InterfaceImpl tokens +// to be defined. It checks for duplicates against the database only if the +// TypeDef for which these tokens are being defined is not a new one. +//****************************************************************************** +HRESULT RegMeta::_InterfaceImplDupProc( // S_OK or error. + mdToken rTk[], // Array of TypeRef or TypeDef or TypeSpec tokens for implemented interfaces. + mdTypeDef td, // Implementing TypeDef. + CQuickBytes *pcqbTk) // Quick Byte object for placing the array of unique tokens. +{ + HRESULT hr = S_OK; + ULONG i = 0; + ULONG iUniqCount = 0; + BOOL bDupFound; + + while (!IsNilToken(rTk[i])) + { + _ASSERTE(TypeFromToken(rTk[i]) == mdtTypeRef || TypeFromToken(rTk[i]) == mdtTypeDef + || TypeFromToken(rTk[i]) == mdtTypeSpec); + bDupFound = false; + + // Eliminate duplicates from the input list of tokens by looking within the list. + for (ULONG j = 0; j < iUniqCount; j++) + { + if (rTk[i] == ((mdToken *)pcqbTk->Ptr())[j]) + { + bDupFound = true; + break; + } + } + + // If no duplicate is found record it in the list. + if (!bDupFound) + { + IfFailGo(pcqbTk->ReSizeNoThrow((iUniqCount+1) * sizeof(mdToken))); + ((mdToken *)pcqbTk->Ptr())[iUniqCount] = rTk[i]; + iUniqCount++; + } + i++; + } + + // Create a Nil token to signify the end of list. + IfFailGo(pcqbTk->ReSizeNoThrow((iUniqCount+1) * sizeof(mdToken))); + ((mdToken *)pcqbTk->Ptr())[iUniqCount] = mdTokenNil; +ErrExit: + + return hr; +} // HRESULT RegMeta::_InterfaceImplDupProc() + +//******************************************************************************* +// helper to define event +//******************************************************************************* +HRESULT RegMeta::_DefineEvent( // Return hresult. + mdTypeDef td, // [IN] the class/interface on which the event is being defined + LPCWSTR szEvent, // [IN] Name of the event + DWORD dwEventFlags, // [IN] CorEventAttr + mdToken tkEventType, // [IN] a reference (mdTypeRef or mdTypeRef) to the Event class + mdEvent *pmdEvent) // [OUT] output event token +{ + HRESULT hr = S_OK; + EventRec *pEventRec = NULL; + RID iEventRec; + EventMapRec *pEventMap; + RID iEventMap; + mdEvent mdEv; + LPUTF8 szUTF8Event; + UTF8STR(szEvent, szUTF8Event); + PREFIX_ASSUME(szUTF8Event != NULL); + + + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && td != mdTypeDefNil); + _ASSERTE(IsNilToken(tkEventType) || TypeFromToken(tkEventType) == mdtTypeDef || + TypeFromToken(tkEventType) == mdtTypeRef || TypeFromToken(tkEventType) == mdtTypeSpec); + _ASSERTE(szEvent && pmdEvent); + + if (CheckDups(MDDupEvent)) + { + hr = ImportHelper::FindEvent(&(m_pStgdb->m_MiniMd), td, szUTF8Event, pmdEvent); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(*pmdEvent), &pEventRec)); + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + if (! pEventRec) + { + // Create a new map if one doesn't exist already, else retrieve the existing one. + // The event map must be created before the EventRecord, the new event map will + // be pointing past the first event record. + IfFailGo(m_pStgdb->m_MiniMd.FindEventMapFor(RidFromToken(td), &iEventMap)); + if (InvalidRid(iEventMap)) + { + // Create new record. + IfFailGo(m_pStgdb->m_MiniMd.AddEventMapRecord(&pEventMap, &iEventMap)); + // Set parent. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_EventMap, + EventMapRec::COL_Parent, pEventMap, td)); + IfFailGo(UpdateENCLog2(TBL_EventMap, iEventMap)); + } + else + { + IfFailGo(m_pStgdb->m_MiniMd.GetEventMapRecord(iEventMap, &pEventMap)); + } + + // Create a new event record. + IfFailGo(m_pStgdb->m_MiniMd.AddEventRecord(&pEventRec, &iEventRec)); + + // Set output parameter. + *pmdEvent = TokenFromRid(iEventRec, mdtEvent); + + // Add Event to EventMap. + IfFailGo(m_pStgdb->m_MiniMd.AddEventToEventMap(RidFromToken(iEventMap), iEventRec)); + + IfFailGo(UpdateENCLog2(TBL_EventMap, iEventMap, CMiniMdRW::eDeltaEventCreate)); + } + + mdEv = *pmdEvent; + + // Set data + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_Event, EventRec::COL_Name, pEventRec, szUTF8Event)); + IfFailGo(_SetEventProps1(*pmdEvent, dwEventFlags, tkEventType)); + + // Add the <Event token, typedef token> to the lookup table + if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Event)) + IfFailGo( m_pStgdb->m_MiniMd.AddEventToLookUpTable(*pmdEvent, td) ); + + IfFailGo(UpdateENCLog(*pmdEvent)); + +ErrExit: + + return hr; +} // HRESULT RegMeta::_DefineEvent() + + +//****************************************************************************** +// Set the specified properties on the Event Token. +//****************************************************************************** +HRESULT RegMeta::_SetEventProps1( // Return hresult. + mdEvent ev, // [IN] Event token. + DWORD dwEventFlags, // [IN] Event flags. + mdToken tkEventType) // [IN] Event type class. +{ + EventRec *pRecord; + HRESULT hr = S_OK; + + _ASSERTE(TypeFromToken(ev) == mdtEvent && RidFromToken(ev)); + + IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(ev), &pRecord)); + if (dwEventFlags != UINT32_MAX) + { + // Don't let caller set reserved bits + dwEventFlags &= ~evReservedMask; + // Preserve reserved bits. + dwEventFlags |= (pRecord->GetEventFlags() & evReservedMask); + + pRecord->SetEventFlags(static_cast<USHORT>(dwEventFlags)); + } + if (!IsNilToken(tkEventType)) + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_Event, EventRec::COL_EventType, + pRecord, tkEventType)); +ErrExit: + return hr; +} // HRESULT RegMeta::_SetEventProps1() + +//****************************************************************************** +// Set the specified properties on the given Event token. +//****************************************************************************** +HRESULT RegMeta::_SetEventProps2( // Return hresult. + mdEvent ev, // [IN] Event token. + mdMethodDef mdAddOn, // [IN] Add method. + mdMethodDef mdRemoveOn, // [IN] Remove method. + mdMethodDef mdFire, // [IN] Fire method. + mdMethodDef rmdOtherMethods[], // [IN] An array of other methods. + BOOL bClear) // [IN] Specifies whether to clear the existing MethodSemantics records. +{ + EventRec *pRecord; + HRESULT hr = S_OK; + + + + _ASSERTE(TypeFromToken(ev) == mdtEvent && RidFromToken(ev)); + + IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(ev), &pRecord)); + + // Remember the AddOn method. + if (!IsNilToken(mdAddOn)) + { + _ASSERTE(TypeFromToken(mdAddOn) == mdtMethodDef); + IfFailGo(_DefineMethodSemantics(msAddOn, mdAddOn, ev, bClear)); + } + + // Remember the RemoveOn method. + if (!IsNilToken(mdRemoveOn)) + { + _ASSERTE(TypeFromToken(mdRemoveOn) == mdtMethodDef); + IfFailGo(_DefineMethodSemantics(msRemoveOn, mdRemoveOn, ev, bClear)); + } + + // Remember the fire method. + if (!IsNilToken(mdFire)) + { + _ASSERTE(TypeFromToken(mdFire) == mdtMethodDef); + IfFailGo(_DefineMethodSemantics(msFire, mdFire, ev, bClear)); + } + + // Store all of the other methods. + if (rmdOtherMethods) + { + int i = 0; + mdMethodDef mb; + + while (1) + { + mb = rmdOtherMethods[i++]; + if (IsNilToken(mb)) + break; + _ASSERTE(TypeFromToken(mb) == mdtMethodDef); + IfFailGo(_DefineMethodSemantics(msOther, mb, ev, bClear)); + + // The first call would've cleared all the existing ones. + bClear = false; + } + } +ErrExit: + + return hr; +} // HRESULT RegMeta::_SetEventProps2() + +//****************************************************************************** +// Set Permission on the given permission token. +//****************************************************************************** +HRESULT RegMeta::_SetPermissionSetProps( // Return hresult. + mdPermission tkPerm, // [IN] Permission token. + DWORD dwAction, // [IN] CorDeclSecurity. + void const *pvPermission, // [IN] Permission blob. + ULONG cbPermission) // [IN] Count of bytes of pvPermission. +{ + DeclSecurityRec *pRecord; + HRESULT hr = S_OK; + + _ASSERTE(TypeFromToken(tkPerm) == mdtPermission && cbPermission != UINT32_MAX); + _ASSERTE(dwAction && dwAction <= dclMaximumValue); + + IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(tkPerm), &pRecord)); + + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_DeclSecurity, DeclSecurityRec::COL_PermissionSet, + pRecord, pvPermission, cbPermission)); +ErrExit: + return hr; +} // HRESULT RegMeta::_SetPermissionSetProps() + +//****************************************************************************** +// Define or set value on a constant record. +//****************************************************************************** +HRESULT RegMeta::_DefineSetConstant( // Return hresult. + mdToken tk, // [IN] Parent token. + DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchString, // [IN] Size of string in wide chars, or -1 for default. + BOOL bSearch) // [IN] Specifies whether to search for an existing record. +{ + HRESULT hr = S_OK; + + + + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + ConstantRec *pConstRec = 0; + RID iConstRec; + ULONG cbBlob; + ULONG ulValue = 0; + + if (bSearch) + { + IfFailGo(m_pStgdb->m_MiniMd.FindConstantHelper(tk, &iConstRec)); + if (!InvalidRid(iConstRec)) + IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(iConstRec, &pConstRec)); + } + if (! pConstRec) + { + IfFailGo(m_pStgdb->m_MiniMd.AddConstantRecord(&pConstRec, &iConstRec)); + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_Constant, ConstantRec::COL_Parent, + pConstRec, tk)); + IfFailGo( m_pStgdb->m_MiniMd.AddConstantToHash(iConstRec) ); + } + + // Add values to the various columns of the constant value row. + pConstRec->SetType(static_cast<BYTE>(dwCPlusTypeFlag)); + if (!pValue) + pValue = &ulValue; + cbBlob = _GetSizeOfConstantBlob(dwCPlusTypeFlag, (void *)pValue, cchString); + if (cbBlob > 0) + { +#if BIGENDIAN + void *pValueTemp; + pValueTemp = (void *)alloca(cbBlob); + IfFailGo(m_pStgdb->m_MiniMd.SwapConstant(pValue, dwCPlusTypeFlag, pValueTemp, cbBlob)); + pValue = pValueTemp; +#endif + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_Constant, ConstantRec::COL_Value, + pConstRec, pValue, cbBlob)); + } + + + // Create log record for non-token record. + IfFailGo(UpdateENCLog2(TBL_Constant, iConstRec)); + } +ErrExit: + + return hr; +} // HRESULT RegMeta::_DefineSetConstant() + + +//***************************************************************************** +// Helper: Set the properties on the given Method token. +//***************************************************************************** +HRESULT RegMeta::_SetMethodProps( // S_OK or error. + mdMethodDef md, // [IN] The MethodDef. + DWORD dwMethodFlags, // [IN] Method attributes. + ULONG ulCodeRVA, // [IN] Code RVA. + DWORD dwImplFlags) // [IN] MethodImpl flags. +{ + MethodRec *pRecord; + HRESULT hr = S_OK; + + _ASSERTE(TypeFromToken(md) == mdtMethodDef && RidFromToken(md)); + + // Get the Method record. + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pRecord)); + + // Set the data. + if (dwMethodFlags != UINT32_MAX) + { + // Preserve the reserved flags stored already and always keep the mdRTSpecialName + dwMethodFlags |= (pRecord->GetFlags() & mdReservedMask); + + // Set the flags. + pRecord->SetFlags(static_cast<USHORT>(dwMethodFlags)); + } + if (ulCodeRVA != UINT32_MAX) + pRecord->SetRVA(ulCodeRVA); + if (dwImplFlags != UINT32_MAX) + pRecord->SetImplFlags(static_cast<USHORT>(dwImplFlags)); + + IfFailGo(UpdateENCLog(md)); +ErrExit: + return hr; +} // HRESULT RegMeta::_SetMethodProps() + + +//***************************************************************************** +// Helper: Set the properties on the given Field token. +//***************************************************************************** +HRESULT RegMeta::_SetFieldProps( // S_OK or error. + mdFieldDef fd, // [IN] The FieldDef. + DWORD dwFieldFlags, // [IN] Field attributes. + DWORD dwCPlusTypeFlag, // [IN] Flag for the value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue) // [IN] size of constant value (string, in wide chars). +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + FieldRec *pRecord; + HRESULT hr = S_OK; + int bHasDefault = false; // If defining a constant, in this call. + + _ASSERTE (TypeFromToken(fd) == mdtFieldDef && RidFromToken(fd)); + + // Get the Field record. + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pRecord)); + + // See if there is a Constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (dwFieldFlags == UINT32_MAX) + dwFieldFlags = pRecord->GetFlags(); + dwFieldFlags |= fdHasDefault; + + bHasDefault = true; + } + + // Set the flags. + if (dwFieldFlags != UINT32_MAX) + { + if ( IsFdHasFieldRVA(dwFieldFlags) && !IsFdHasFieldRVA(pRecord->GetFlags()) ) + { + // This will trigger field RVA to be created if it is not yet created! + _SetRVA(fd, 0, 0); + } + + // Preserve the reserved flags stored. + dwFieldFlags |= (pRecord->GetFlags() & fdReservedMask); + // Set the flags. + pRecord->SetFlags(static_cast<USHORT>(dwFieldFlags)); + } + + IfFailGo(UpdateENCLog(fd)); + + // Set the Constant. + if (bHasDefault) + { + BOOL bSearch = IsCallerExternal() || IsENCOn(); + IfFailGo(_DefineSetConstant(fd, dwCPlusTypeFlag, pValue, cchValue, bSearch)); + } + +ErrExit: + return hr; +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} // RegMeta::_SetFieldProps + +//***************************************************************************** +// Helper: Set the properties on the given Property token. +//***************************************************************************** +HRESULT RegMeta::_SetPropertyProps( // S_OK or error. + mdProperty pr, // [IN] Property token. + DWORD dwPropFlags, // [IN] CorPropertyAttr. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type, selected ELEMENT_TYPE_* + void const *pValue, // [IN] Constant value. + ULONG cchValue, // [IN] size of constant value (string, in wide chars). + mdMethodDef mdSetter, // [IN] Setter of the property. + mdMethodDef mdGetter, // [IN] Getter of the property. + mdMethodDef rmdOtherMethods[]) // [IN] Array of other methods. +{ + PropertyRec *pRecord; + BOOL bClear = IsCallerExternal() || IsENCOn() || IsIncrementalOn(); + HRESULT hr = S_OK; + int bHasDefault = false; // If true, constant value this call. + + + + _ASSERTE(TypeFromToken(pr) == mdtProperty && RidFromToken(pr)); + + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(pr), &pRecord)); + + if (dwPropFlags != UINT32_MAX) + { + // Clear the reserved flags from the flags passed in. + dwPropFlags &= (~prReservedMask); + } + // See if there is a constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (dwPropFlags == UINT32_MAX) + dwPropFlags = pRecord->GetPropFlags(); + dwPropFlags |= prHasDefault; + + bHasDefault = true; + } + if (dwPropFlags != UINT32_MAX) + { + // Preserve the reserved flags. + dwPropFlags |= (pRecord->GetPropFlags() & prReservedMask); + // Set the flags. + pRecord->SetPropFlags(static_cast<USHORT>(dwPropFlags)); + } + + // store the getter (or clear out old one). + if (mdGetter != UINT32_MAX) + { + _ASSERTE(TypeFromToken(mdGetter) == mdtMethodDef || IsNilToken(mdGetter)); + IfFailGo(_DefineMethodSemantics(msGetter, mdGetter, pr, bClear)); + } + + // Store the setter (or clear out old one). + if (mdSetter != UINT32_MAX) + { + _ASSERTE(TypeFromToken(mdSetter) == mdtMethodDef || IsNilToken(mdSetter)); + IfFailGo(_DefineMethodSemantics(msSetter, mdSetter, pr, bClear)); + } + + // Store all of the other methods. + if (rmdOtherMethods) + { + int i = 0; + mdMethodDef mb; + + while (1) + { + mb = rmdOtherMethods[i++]; + if (IsNilToken(mb)) + break; + _ASSERTE(TypeFromToken(mb) == mdtMethodDef); + IfFailGo(_DefineMethodSemantics(msOther, mb, pr, bClear)); + + // The first call to _DefineMethodSemantics would've cleared all the records + // that match with msOther and pr. + bClear = false; + } + } + + IfFailGo(UpdateENCLog(pr)); + + // Set the constant. + if (bHasDefault) + { + BOOL bSearch = IsCallerExternal() || IsENCOn() || IsIncrementalOn(); + IfFailGo(_DefineSetConstant(pr, dwCPlusTypeFlag, pValue, cchValue, bSearch)); + } + +ErrExit: + + return hr; +} // HRESULT RegMeta::_SetPropertyProps() + + +//***************************************************************************** +// Helper: This routine sets properties on the given Param token. +//***************************************************************************** +HRESULT RegMeta::_SetParamProps( // Return code. + mdParamDef pd, // [IN] Param token. + LPCWSTR szName, // [IN] Param name. + DWORD dwParamFlags, // [IN] Param flags. + DWORD dwCPlusTypeFlag, // [IN] Flag for value type. selected ELEMENT_TYPE_*. + void const *pValue, // [OUT] Constant value. + ULONG cchValue) // [IN] size of constant value (string, in wide chars). +{ + HRESULT hr = S_OK; + ParamRec *pRecord; + int bHasDefault = false; // Is there a default for this call. + + _ASSERTE(TypeFromToken(pd) == mdtParamDef && RidFromToken(pd)); + + IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(pd), &pRecord)); + + // Set the properties. + if (szName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_Param, ParamRec::COL_Name, pRecord, szName)); + } + + if (dwParamFlags != UINT32_MAX) + { + // No one should try to set the reserved flags explicitly. + _ASSERTE((dwParamFlags & pdReservedMask) == 0); + // Clear the reserved flags from the flags passed in. + dwParamFlags &= (~pdReservedMask); + } + // See if there is a constant. + if ((dwCPlusTypeFlag != ELEMENT_TYPE_VOID && dwCPlusTypeFlag != ELEMENT_TYPE_END && + dwCPlusTypeFlag != UINT32_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (dwParamFlags == UINT32_MAX) + dwParamFlags = pRecord->GetFlags(); + dwParamFlags |= pdHasDefault; + + bHasDefault = true; + } + // Set the flags. + if (dwParamFlags != UINT32_MAX) + { + // Preserve the reserved flags stored. + dwParamFlags |= (pRecord->GetFlags() & pdReservedMask); + // Set the flags. + pRecord->SetFlags(static_cast<USHORT>(dwParamFlags)); + } + + // ENC log for the param record. + IfFailGo(UpdateENCLog(pd)); + + // Defer setting the constant until after the ENC log for the param. Due to the way that + // parameter records are re-ordered, ENC needs the param record log entry to be IMMEDIATELY + // after the param added function. + + // Set the constant. + if (bHasDefault) + { + BOOL bSearch = IsCallerExternal() || IsENCOn(); + IfFailGo(_DefineSetConstant(pd, dwCPlusTypeFlag, pValue, cchValue, bSearch)); + } + +ErrExit: + return hr; +} // HRESULT RegMeta::_SetParamProps() + +//***************************************************************************** +// Create and populate a new TypeDef record. +//***************************************************************************** +HRESULT RegMeta::_DefineTypeDef( // S_OK or error. + LPCWSTR szTypeDef, // [IN] Name of TypeDef + DWORD dwTypeDefFlags, // [IN] CustomAttribute flags + mdToken tkExtends, // [IN] extends this TypeDef or typeref + mdToken rtkImplements[], // [IN] Implements interfaces + mdTypeDef tdEncloser, // [IN] TypeDef token of the Enclosing Type. + mdTypeDef *ptd) // [OUT] Put TypeDef token here +{ + HRESULT hr = S_OK; // A result. + TypeDefRec *pRecord = NULL; // New TypeDef record. + RID iRecord; // New TypeDef RID. + CQuickBytes qbNamespace; // Namespace buffer. + CQuickBytes qbName; // Name buffer. + LPUTF8 szTypeDefUTF8; // Full name in UTF8. + ULONG ulStringLen; // Length of the TypeDef string. + int bSuccess; // Return value for SplitPath(). + + + + _ASSERTE(IsTdAutoLayout(dwTypeDefFlags) || IsTdSequentialLayout(dwTypeDefFlags) || IsTdExplicitLayout(dwTypeDefFlags)); + + _ASSERTE(ptd); + _ASSERTE(TypeFromToken(tkExtends) == mdtTypeRef || TypeFromToken(tkExtends) == mdtTypeDef || TypeFromToken(tkExtends) == mdtTypeSpec + || IsNilToken(tkExtends)); + _ASSERTE(szTypeDef && *szTypeDef); + _ASSERTE(IsNilToken(tdEncloser) || IsTdNested(dwTypeDefFlags)); + + UTF8STR(szTypeDef, szTypeDefUTF8); + PREFIX_ASSUME(szTypeDefUTF8 != NULL); + + ulStringLen = (ULONG)(strlen(szTypeDefUTF8) + 1); + IfFailGo(qbNamespace.ReSizeNoThrow(ulStringLen)); + IfFailGo(qbName.ReSizeNoThrow(ulStringLen)); + bSuccess = ns::SplitPath(szTypeDefUTF8, + (LPUTF8)qbNamespace.Ptr(), + ulStringLen, + (LPUTF8)qbName.Ptr(), + ulStringLen); + _ASSERTE(bSuccess); + + if (CheckDups(MDDupTypeDef)) + { + // Check for existence. Do a query by namespace and name. + hr = ImportHelper::FindTypeDefByName(&(m_pStgdb->m_MiniMd), + (LPCUTF8)qbNamespace.Ptr(), + (LPCUTF8)qbName.Ptr(), + tdEncloser, + ptd); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + { + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(*ptd), &pRecord)); + // <TODO>@FUTURE: Should we check to see if the GUID passed is correct?</TODO> + } + else + { + hr = META_S_DUPLICATE; + goto ErrExit; + } + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + if (!pRecord) + { + // Create the new record. + IfFailGo(m_pStgdb->m_MiniMd.AddTypeDefRecord(&pRecord, &iRecord)); + + // Invalidate the ref to def optimization since more def is introduced + SetTypeDefDirty(true); + + if (!IsNilToken(tdEncloser)) + { + NestedClassRec *pNestedClassRec; + RID iNestedClassRec; + + // Create a new NestedClass record. + IfFailGo(m_pStgdb->m_MiniMd.AddNestedClassRecord(&pNestedClassRec, &iNestedClassRec)); + // Set the NestedClass value. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_NestedClass, NestedClassRec::COL_NestedClass, + pNestedClassRec, TokenFromRid(iRecord, mdtTypeDef))); + // Set the NestedClass value. + IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_NestedClass, NestedClassRec::COL_EnclosingClass, + pNestedClassRec, tdEncloser)); + + IfFailGo( m_pStgdb->m_MiniMd.AddNestedClassToHash(iNestedClassRec) ); + + // Create the log record for the non-token record. + IfFailGo(UpdateENCLog2(TBL_NestedClass, iNestedClassRec)); + } + + // Give token back to caller. + *ptd = TokenFromRid(iRecord, mdtTypeDef); + } + + // Set the namespace and name. + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeDef, TypeDefRec::COL_Name, + pRecord, (LPCUTF8)qbName.Ptr())); + IfFailGo(m_pStgdb->m_MiniMd.PutString(TBL_TypeDef, TypeDefRec::COL_Namespace, + pRecord, (LPCUTF8)qbNamespace.Ptr())); + + SetCallerDefine(); + IfFailGo(_SetTypeDefProps(*ptd, dwTypeDefFlags, tkExtends, rtkImplements)); +ErrExit: + SetCallerExternal(); + + return hr; +} // RegMeta::_DefineTypeDef + +#endif //FEATURE_METADATA_EMIT + +#if defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT) + +//****************************************************************************** +//--- IMetaDataCorProfileData +//****************************************************************************** + +HRESULT RegMeta::SetCorProfileData( + CorProfileData *pProfileData) // [IN] Pointer to profile data +{ + m_pCorProfileData = pProfileData; + + return S_OK; +} + +//****************************************************************************** +//--- IMDInternalMetadataReorderingOptions +//****************************************************************************** + +HRESULT RegMeta::SetMetaDataReorderingOptions( + MetaDataReorderingOptions options) // [IN] Metadata reordering options +{ + m_ReorderingOptions = options; + + return S_OK; +} + +#endif //defined(FEATURE_METADATA_IN_VM) && defined(FEATURE_PREJIT) diff --git a/src/coreclr/md/compiler/regmeta_imetadatatables.cpp b/src/coreclr/md/compiler/regmeta_imetadatatables.cpp new file mode 100644 index 00000000000..b00dcaa08b9 --- /dev/null +++ b/src/coreclr/md/compiler/regmeta_imetadatatables.cpp @@ -0,0 +1,718 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: RegMeta_IMetaDataTables.cpp +// + +// +// Methods of code:RegMeta class which implement public API interfaces code:IMetaDataTables: +// * code:RegMeta::GetStringHeapSize +// * code:RegMeta::GetBlobHeapSize +// * code:RegMeta::GetGuidHeapSize +// * code:RegMeta::GetUserStringHeapSize +// +// * code:RegMeta#GetString_IMetaDataTables +// * code:RegMeta#GetBlob_IMetaDataTables +// * code:RegMeta#GetGuid_IMetaDataTables +// * code:RegMeta#GetUserString_IMetaDataTables +// +// * code:RegMeta::GetNextString +// * code:RegMeta::GetNextBlob +// * code:RegMeta::GetNextGuid +// * code:RegMeta::GetNextUserString +// +// * code:RegMeta::GetNumTables +// * code:RegMeta::GetTableIndex +// * code:RegMeta::GetTableInfo +// * code:RegMeta::GetColumnInfo +// * code:RegMeta::GetCodedTokenInfo +// * code:RegMeta::GetRow +// * code:RegMeta::GetColumn +// +// Methods of code:RegMeta class which implement public API interfaces code:IMetaDataTables2: +// * code:RegMeta::GetMetaDataStorage +// * code:RegMeta::GetMetaDataStreamInfo +// +// ====================================================================================== + +#include "stdafx.h" +#include "regmeta.h" + +// -------------------------------------------------------------------------------------- +// +// Fills size (*pcbStringsHeapSize) of internal strings heap (#String). +// Returns S_OK or error code. Fills *pcbStringsHeapSize with 0 on error. +// Implements public API code:IMetaDataTables::GetStringHeapSize. +// +HRESULT +RegMeta::GetStringHeapSize( + __out ULONG *pcbStringsHeapSize) // [OUT] Size of the string heap. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + *pcbStringsHeapSize = m_pStgdb->m_MiniMd.m_StringHeap.GetUnalignedSize(); + + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetStringHeapSize + +// -------------------------------------------------------------------------------------- +// +// Fills size (*pcbBlobsHeapSize) of blobs heap (#Blob). +// Returns S_OK or error code. Fills *pcbBlobsHeapSize with 0 on error. +// Implements public API code:IMetaDataTables::GetBlobHeapSize. +// +HRESULT +RegMeta::GetBlobHeapSize( + __out ULONG *pcbBlobsHeapSize) // [OUT] Size of the blob heap. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + *pcbBlobsHeapSize = m_pStgdb->m_MiniMd.m_BlobHeap.GetUnalignedSize(); + + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetBlobHeapSize + +// -------------------------------------------------------------------------------------- +// +// Fills size (*pcbGuidsHeapSize) of guids heap (#GUID). +// Returns S_OK or error code. Fills *pcbGuidsHeapSize with 0 on error. +// Implements public API code:IMetaDataTables::GetGuidHeapSize. +// +HRESULT +RegMeta::GetGuidHeapSize( + __out ULONG *pcbGuidsHeapSize) // [OUT] Size of the Guid heap. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + *pcbGuidsHeapSize = m_pStgdb->m_MiniMd.m_GuidHeap.GetSize(); + + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetGuidHeapSize + +// -------------------------------------------------------------------------------------- +// +// Fills size (*pcbUserStringsHeapSize) of user strings heap (#US) (referenced from IL). +// Returns S_OK or error code. Fills *pcbUserStringsHeapSize with 0 on error. +// Implements public API code:IMetaDataTables::GetUserStringHeapSize. +// +HRESULT +RegMeta::GetUserStringHeapSize( + __out ULONG *pcbUserStringsHeapSize) // [OUT] Size of the user string heap. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + *pcbUserStringsHeapSize = m_pStgdb->m_MiniMd.m_UserStringHeap.GetUnalignedSize(); + + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetUserStringHeapSize + +// -------------------------------------------------------------------------------------- +// +//#GetString_IMetaDataTables +// +// Fills internal null-terminated string (*pszString) at index ixString from string heap (#String). +// Returns S_OK (even for index 0) or error code (if index is invalid, fills *pszString with NULL then). +// Implements public API code:IMetaDataTables::GetString. +// +HRESULT RegMeta::GetString( + ULONG ixString, // [IN] Value from a string column. + const char **pszString) // [OUT] Put a pointer to the string here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(m_pStgdb->m_MiniMd.getString( + ixString, + pszString)); + + _ASSERTE(hr == S_OK); + goto Exit; +ErrExit: + *pszString = NULL; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetString + +// -------------------------------------------------------------------------------------- +// +//#GetBlob_IMetaDataTables +// +// Fills blob entry (*ppvData of size *pcbDataSize) at index ixBlob from blob heap (#Blob). +// Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then). +// Implements public API code:IMetaDataTables::GetBlob. +// +HRESULT RegMeta::GetBlob( + ULONG ixBlob, // [IN] Value from a blob column. + __out ULONG *pcbDataSize, // [OUT] Put size of the blob here. + __deref_out const void **ppvData) // [OUT] Put a pointer to the blob here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + MetaData::DataBlob dataBlob; + IfFailGo(m_pStgdb->m_MiniMd.getBlob(ixBlob, &dataBlob)); + + *ppvData = (const void *)dataBlob.GetDataPointer(); + *pcbDataSize = dataBlob.GetSize(); + + _ASSERTE(hr == S_OK); + goto Exit; +ErrExit: + *ppvData = NULL; + *pcbDataSize = 0; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetBlob + +// -------------------------------------------------------------------------------------- +// +//#GetGuid_IMetaDataTables +// +// Fills guid (*ppGuid) at index ixGuid from guid heap (#GUID). +// Returns S_OK and fills *ppGuid. Returns S_OK even for (invalid) index 0 (fills *ppGuid with pointer to +// zeros then). +// Retruns error code (if index is invalid except 0, fills NULL and o then). +// Implements public API code:IMetaDataTables::GetGuid. +// +// Backward compatibility: returns S_OK even if the index is 0 which is invalid as specified in CLI ECMA +// specification. In that case returns pointer to GUID from zeros. +// +HRESULT RegMeta::GetGuid( + ULONG ixGuid, // [IN] Value from a guid column. + const GUID **ppGuid) // [OUT] Put a pointer to the GUID here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + if (ixGuid == 0) + { + // Return zeros + *ppGuid = GetPublicApiCompatibilityZeros<const GUID>(); + hr = S_OK; + } + else + { + IfFailGo(m_pStgdb->m_MiniMd.m_GuidHeap.GetGuid( + ixGuid, + ppGuid)); + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetGuid + +// -------------------------------------------------------------------------------------- +// +//#GetUserString_IMetaDataTables +// +// Fills user string (*ppvData of size *pcbDataSize) at index ixUserString. +// Returns S_OK (even for index 0) or error code (if index is invalid, fills NULL and o then). +// Implements public API code:IMetaDataTables::GetUserString. +// +HRESULT +RegMeta::GetUserString( + ULONG ixUserString, // [IN] Value from a UserString column. + __out ULONG *pcbDataSize, // [OUT] Put size of the UserString here. + __deref_out_opt const void **ppvData) // [OUT] Put a pointer to the UserString here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + MetaData::DataBlob userString; + IfFailGo(m_pStgdb->m_MiniMd.GetUserString(ixUserString, &userString)); + _ASSERTE(hr == S_OK); + + *ppvData = (const void *)userString.GetDataPointer(); + *pcbDataSize = userString.GetSize(); + + goto Exit; +ErrExit: + *ppvData = NULL; + *pcbDataSize = 0; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetUserString + +// -------------------------------------------------------------------------------------- +// +//#GetNextString_IMetaDataTables +// +// Fills index of string (*pixNextString) from the internal strings heap (#String) starting behind string +// at index ixString. +// Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextString with 0 on S_FALSE. +// Implements public API code:IMetaDataTables::GetNextString. +// +HRESULT +RegMeta::GetNextString( + ULONG ixString, // [IN] Value from a string column. + __out ULONG *pixNextString) // [OUT] Put the index of the next string here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + // Get string at index ixString + LPCSTR szString; + IfFailGo(m_pStgdb->m_MiniMd.m_StringHeap.GetString( + ixString, + &szString)); + _ASSERTE(hr == S_OK); + + // Get index behind the string - doesn't overflow, because string heap was verified + UINT32 ixNextString; + ixNextString = ixString + (UINT32)(strlen(szString) + 1); + + // Verify that the next index is in the string heap + if (!m_pStgdb->m_MiniMd.m_StringHeap.IsValidIndex(ixNextString)) + { // The next index is invalid + goto ErrExit; + } + // The next index is valid + *pixNextString = ixNextString; + goto Exit; + +ErrExit: + // Fill output parameters on error + *pixNextString = 0; + // Return S_FALSE if either of the string indexes is invalid (backward API compatibility) + hr = S_FALSE; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetNextString + +// -------------------------------------------------------------------------------------- +// +//#GetNextBlob_IMetaDataTables +// +// Fills index of blob (*pixNextBlob) from the blobs heap (#Blob) starting behind blob at index ixBlob. +// Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextBlob with 0 on S_FALSE. +// Implements public API code:IMetaDataTables::GetNextString. +// +HRESULT +RegMeta::GetNextBlob( + ULONG ixBlob, // [IN] Value from a blob column. + __out ULONG *pixNextBlob) // [OUT] Put the index of the next blob here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + // Get blob at index ixBlob (verifies that the blob is in the blob heap) + MetaData::DataBlob blob; + IfFailGo(m_pStgdb->m_MiniMd.m_BlobHeap.GetBlobWithSizePrefix( + ixBlob, + &blob)); + _ASSERTE(hr == S_OK); + + // Get index behind the blob - doesn't overflow, because the blob is in the blob heap + UINT32 ixNextBlob; + ixNextBlob = ixBlob + blob.GetSize(); + + // Verify that the next index is in the blob heap + if (!m_pStgdb->m_MiniMd.m_BlobHeap.IsValidIndex(ixNextBlob)) + { // The next index is invalid + goto ErrExit; + } + // The next index is valid + *pixNextBlob = ixNextBlob; + goto Exit; + +ErrExit: + // Fill output parameters on error + *pixNextBlob = 0; + // Return S_FALSE if either of the string indexes is invalid (backward API compatibility) + hr = S_FALSE; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetNextBlob + +// -------------------------------------------------------------------------------------- +// +//#GetNextGuid_IMetaDataTables +// +// Fills index of guid (*pixNextGuid) from the guids heap (#GUID) starting behind guid at index ixGuid. +// Returns S_OK or S_FALSE (if the new index is invalid). Fills *pixNextGuid with 0 on S_FALSE. +// Implements public API code:IMetaDataTables::GetNextGuid. +// +// Backward compatibility: returns S_OK even if the guid index (ixGuid) is 0 which is invalid as +// specified in CLI ECMA specification. +// +HRESULT +RegMeta::GetNextGuid( + ULONG ixGuid, // [IN] Value from a guid column. + __out ULONG *pixNextGuid) // [OUT] Put the index of the next guid here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + S_UINT32 ixNextGuid = S_UINT32(ixGuid) + S_UINT32(1); + if (ixNextGuid.IsOverflow()) + { // It's invalid index (UINT32_MAX) + goto ErrExit; + } + if (!m_pStgdb->m_MiniMd.m_GuidHeap.IsValidIndex(ixNextGuid.Value())) + { // The next index is invalid + goto ErrExit; + } + _ASSERTE(hr == S_OK); + // The next index is valid + *pixNextGuid = ixNextGuid.Value(); + goto Exit; + +ErrExit: + // Fill output parameters on error + *pixNextGuid = 0; + // Return S_FALSE if next guid index is invalid (backward API compatibility) + hr = S_FALSE; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetNextGuid + +// -------------------------------------------------------------------------------------- +// +//#GetNextUserString_IMetaDataTables +// +// Fills index of user string (*pixNextUserString) from the user strings heap (#US) starting behind string +// at index ixUserString. +// Returns S_OK or S_FALSE (if either index is invalid). Fills *pixNextUserString with 0 on S_FALSE. +// Implements public API code:IMetaDataTables::GetNextUserString. +// +// Backward compatibility: returns S_OK even if the string doesn't have odd number of bytes as specified +// in CLI ECMA specification. +// +HRESULT +RegMeta::GetNextUserString( + ULONG ixUserString, // [IN] Value from a UserString column. + __out ULONG *pixNextUserString) // [OUT] Put the index of the next user string here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + // Get user string at index ixUserString (verifies that the user string is in the heap) + MetaData::DataBlob userString; + IfFailGo(m_pStgdb->m_MiniMd.m_UserStringHeap.GetBlobWithSizePrefix( + ixUserString, + &userString)); + _ASSERTE(hr == S_OK); + + // Get index behind the user string - doesn't overflow, because the user string is in the heap + UINT32 ixNextUserString; + ixNextUserString = ixUserString + userString.GetSize(); + + // Verify that the next index is in the user string heap + if (!m_pStgdb->m_MiniMd.m_UserStringHeap.IsValidIndex(ixNextUserString)) + { // The next index is invalid + goto ErrExit; + } + // The next index is valid + *pixNextUserString = ixNextUserString; + goto Exit; + +ErrExit: + // Fill output parameters on error + *pixNextUserString = 0; + // Return S_FALSE if either of the string indexes is invalid (backward API compatibility) + hr = S_FALSE; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetNextUserString + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables::GetNumTables. +// +HRESULT +RegMeta::GetNumTables( + __out ULONG *pcTables) // [OUT] Count of tables. +{ + BEGIN_ENTRYPOINT_NOTHROW; + + // These are for dumping metadata information. + // We probably don't need to do any lock here. + + *pcTables = m_pStgdb->m_MiniMd.GetCountTables(); + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} // RegMeta::GetNumTables + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables::GetTableIndex. +// +HRESULT +RegMeta::GetTableIndex( + ULONG token, // [IN] Token for which to get table index. + __out ULONG *pixTbl) // [OUT] Put table index here. +{ + BEGIN_ENTRYPOINT_NOTHROW; + + // These are for dumping metadata information. + // We probably don't need to do any lock here. + + *pixTbl = CMiniMdRW::GetTableForToken(token); + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} // RegMeta::GetTableIndex + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables::GetTableInfo. +// +HRESULT +RegMeta::GetTableInfo( + ULONG ixTbl, // [IN] Which table. + ULONG *pcbRow, // [OUT] Size of a row, bytes. + ULONG *pcRows, // [OUT] Number of rows. + ULONG *pcCols, // [OUT] Number of columns in each row. + ULONG *piKey, // [OUT] Key column, or -1 if none. + const char **ppName) // [OUT] Name of the table. +{ + HRESULT hr = S_OK; + CMiniTableDef *pTbl = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + // These are for dumping metadata information. + // We probably don't need to do any lock here. + + if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables()) + IfFailGo(E_INVALIDARG); + pTbl = &m_pStgdb->m_MiniMd.m_TableDefs[ixTbl]; + if (pcbRow != NULL) + *pcbRow = pTbl->m_cbRec; + if (pcRows != NULL) + *pcRows = m_pStgdb->m_MiniMd.GetCountRecs(ixTbl); + if (pcCols != NULL) + *pcCols = pTbl->m_cCols; + if (piKey != NULL) + *piKey = (pTbl->m_iKey == (BYTE) -1) ? -1 : pTbl->m_iKey; + if (ppName != NULL) + *ppName = g_Tables[ixTbl].m_pName; + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetTableInfo + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables::GetColumnInfo. +// +HRESULT +RegMeta::GetColumnInfo( + ULONG ixTbl, // [IN] Which Table + ULONG ixCol, // [IN] Which Column in the table + ULONG *poCol, // [OUT] Offset of the column in the row. + ULONG *pcbCol, // [OUT] Size of a column, bytes. + ULONG *pType, // [OUT] Type of the column. + const char **ppName) // [OUT] Name of the Column. +{ + HRESULT hr = S_OK; + CMiniTableDef *pTbl = NULL; + CMiniColDef *pCol = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + // These are for dumping metadata information. + // We probably don't need to do any lock here. + + if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables()) + IfFailGo(E_INVALIDARG); + pTbl = &m_pStgdb->m_MiniMd.m_TableDefs[ixTbl]; + if (ixCol >= pTbl->m_cCols) + IfFailGo(E_INVALIDARG); + pCol = &pTbl->m_pColDefs[ixCol]; + if (poCol != NULL) + *poCol = pCol->m_oColumn; + if (pcbCol != NULL) + *pcbCol = pCol->m_cbColumn; + if (pType != NULL) + *pType = pCol->m_Type; + if (ppName != NULL) + *ppName = g_Tables[ixTbl].m_pColNames[ixCol]; + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetColumnInfo + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables::GetCodedTokenInfo. +// +HRESULT +RegMeta::GetCodedTokenInfo( + ULONG ixCdTkn, // [IN] Which kind of coded token. + ULONG *pcTokens, // [OUT] Count of tokens. + ULONG **ppTokens, // [OUT] List of tokens. + const char **ppName) // [OUT] Name of the CodedToken. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + // These are for dumping metadata information. + // We probably don't need to do any lock here. + + // Validate arguments. + if (ixCdTkn >= CDTKN_COUNT) + IfFailGo(E_INVALIDARG); + + if (pcTokens != NULL) + *pcTokens = g_CodedTokens[ixCdTkn].m_cTokens; + if (ppTokens != NULL) + *ppTokens = (ULONG*)g_CodedTokens[ixCdTkn].m_pTokens; + if (ppName != NULL) + *ppName = g_CodedTokens[ixCdTkn].m_pName; + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetCodedTokenInfo + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables::GetRow. +// +HRESULT +RegMeta::GetRow( + ULONG ixTbl, // [IN] Which table. + ULONG rid, // [IN] Which row. + void **ppRow) // [OUT] Put pointer to row here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + // These are for dumping metadata information. + // We probably don't need to do any lock here. + + // Validate arguments. + if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables()) + IfFailGo(E_INVALIDARG); + if (rid == 0 || rid > m_pStgdb->m_MiniMd.m_Schema.m_cRecs[ixTbl]) + IfFailGo(E_INVALIDARG); + + // Get the row. + IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, rid, ppRow)); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetRow + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables::GetColumn. +// +HRESULT +RegMeta::GetColumn( + ULONG ixTbl, // [IN] Which table. + ULONG ixCol, // [IN] Which column. + ULONG rid, // [IN] Which row. + ULONG *pVal) // [OUT] Put the column contents here. +{ + HRESULT hr = S_OK; + CMiniColDef *pCol = NULL; + CMiniTableDef *pTbl = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + // These are for dumping metadata information. + // We probably don't need to do any lock here. + + void *pRow = NULL; // Row with data. + + // Validate arguments. + if (ixTbl >= m_pStgdb->m_MiniMd.GetCountTables()) + IfFailGo(E_INVALIDARG); + pTbl = &m_pStgdb->m_MiniMd.m_TableDefs[ixTbl]; + if (ixCol >= pTbl->m_cCols) + IfFailGo(E_INVALIDARG); + if (rid == 0 || rid > m_pStgdb->m_MiniMd.m_Schema.m_cRecs[ixTbl]) + IfFailGo(E_INVALIDARG); + + // Get the row. + IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, rid, &pRow)); + + // Is column a token column? + pCol = &pTbl->m_pColDefs[ixCol]; + if (pCol->m_Type <= iCodedTokenMax) + { + *pVal = m_pStgdb->m_MiniMd.GetToken(ixTbl, ixCol, pRow); + } + else + { + *pVal = m_pStgdb->m_MiniMd.GetCol(ixTbl, ixCol, pRow); + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetColumn + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables2::GetMetaDataStorage. +// +HRESULT +RegMeta::GetMetaDataStorage( + const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB'). + ULONG *pcbMd) // [OUT] put size of the stream here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + hr = m_pStgdb->GetRawData(ppvMd, pcbMd); + + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetMetaDataStorage + +// -------------------------------------------------------------------------------------- +// +// Implements public API code:IMetaDataTables2::GetMetaDataStreamInfo. +// +HRESULT +RegMeta::GetMetaDataStreamInfo( + ULONG ix, // [IN] Stream ordinal desired. + const char **ppchName, // [OUT] put pointer to stream name here. + const void **ppv, // [OUT] put pointer to MD stream here. + ULONG *pcb) // [OUT] put size of the stream here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + REGMETA_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + hr = m_pStgdb->GetRawStreamInfo(ix, ppchName, ppv, pcb); + + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetMetaDataStreamInfo diff --git a/src/coreclr/md/compiler/regmeta_import.cpp b/src/coreclr/md/compiler/regmeta_import.cpp new file mode 100644 index 00000000000..93e2a500466 --- /dev/null +++ b/src/coreclr/md/compiler/regmeta_import.cpp @@ -0,0 +1,1200 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: RegMeta_IMetaDataImport.cpp +// + +// +// Methods of code:RegMeta class which implement public API interfaces: +// * code:IMetaDataImport +// * code:IMetaDataImport2 +// +// ====================================================================================== + +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" +#include "filtermanager.h" +#include "mdperf.h" +#include "switches.h" +#include "posterror.h" +#include "stgio.h" +#include "sstring.h" + +#include <metamodelrw.h> + +#define DEFINE_CUSTOM_NODUPCHECK 1 +#define DEFINE_CUSTOM_DUPCHECK 2 +#define SET_CUSTOM 3 + +#if defined(_DEBUG) && defined(_TRACE_REMAPS) +#define LOGGING +#endif +#include <log.h> + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +//***************************************************************************** +// determine if a token is valid or not +//***************************************************************************** +BOOL RegMeta::IsValidToken( // true if tk is valid token + mdToken tk) // [IN] token to be checked +{ + BOOL fRet = FALSE; + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_VOIDRET; + + LOCKREADNORET(); + + // If acquiring the lock failed... + IfFailGo(hr); + + fRet = m_pStgdb->m_MiniMd._IsValidToken(tk); + +ErrExit: + END_ENTRYPOINT_VOIDRET; + return fRet; +} // RegMeta::IsValidToken + +//***************************************************************************** +// Close an enumerator. +//***************************************************************************** +void STDMETHODCALLTYPE RegMeta::CloseEnum( + HCORENUM hEnum) // The enumerator. +{ + BEGIN_CLEANUP_ENTRYPOINT; + + LOG((LOGMD, "RegMeta::CloseEnum(0x%08x)\n", hEnum)); + + // No need to lock this function. + HENUMInternal *pmdEnum = reinterpret_cast<HENUMInternal *> (hEnum); + + if (pmdEnum == NULL) + return; + + HENUMInternal::DestroyEnum(pmdEnum); + END_CLEANUP_ENTRYPOINT; +} // RegMeta::CloseEnum + +//***************************************************************************** +// Query the count of items represented by an enumerator. +//***************************************************************************** +HRESULT CountEnum( + HCORENUM hEnum, // The enumerator. + ULONG *pulCount) // Put the count here. +{ + HENUMInternal *pmdEnum = reinterpret_cast<HENUMInternal *> (hEnum); + HRESULT hr = S_OK; + + // No need to lock this function. + + LOG((LOGMD, "RegMeta::CountEnum(0x%08x, 0x%08x)\n", hEnum, pulCount)); + START_MD_PERF(); + + _ASSERTE( pulCount ); + + if (pmdEnum == NULL) + { + *pulCount = 0; + goto ErrExit; + } + + if (pmdEnum->m_tkKind == (TBL_MethodImpl << 24)) + { + // Number of tokens must always be a multiple of 2. + _ASSERTE(! (pmdEnum->m_ulCount % 2) ); + // There are two entries in the Enumerator for each MethodImpl. + *pulCount = pmdEnum->m_ulCount / 2; + } + else + *pulCount = pmdEnum->m_ulCount; +ErrExit: + STOP_MD_PERF(CountEnum); + return hr; +} // ::CountEnum + +STDMETHODIMP RegMeta::CountEnum( + HCORENUM hEnum, // The enumerator. + ULONG *pulCount) // Put the count here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = ::CountEnum(hEnum, pulCount); + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::CountEnum + +//***************************************************************************** +// Reset an enumerator to any position within the enumerator. +//***************************************************************************** +STDMETHODIMP RegMeta::ResetEnum( + HCORENUM hEnum, // The enumerator. + ULONG ulPos) // Seek position. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal *pmdEnum = reinterpret_cast<HENUMInternal *> (hEnum); + + // No need to lock this function. + + LOG((LOGMD, "RegMeta::ResetEnum(0x%08x, 0x%08x)\n", hEnum, ulPos)); + START_MD_PERF(); + + if (pmdEnum == NULL) + goto ErrExit; + + pmdEnum->u.m_ulCur = pmdEnum->u.m_ulStart + ulPos; + +ErrExit: + STOP_MD_PERF(ResetEnum); + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::ResetEnum + +//***************************************************************************** +// Enumerate Sym.TypeDef. +//***************************************************************************** +STDMETHODIMP RegMeta::EnumTypeDefs( + HCORENUM *phEnum, // Pointer to the enumerator. + mdTypeDef rTypeDefs[], // Put TypeDefs here. + ULONG cMax, // Max TypeDefs to put. + ULONG *pcTypeDefs) // Put # put here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + HENUMInternal *pEnum; + + LOG((LOGMD, "RegMeta::EnumTypeDefs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, rTypeDefs, cMax, pcTypeDefs)); + START_MD_PERF(); + LOCKREAD(); + + + if ( *ppmdEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + if (pMiniMd->HasDelete() && + ((m_OptionValue.m_ImportOption & MDImportOptionAllTypeDefs) == 0)) + { + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtTypeDef, &pEnum) ); + + // add all Types to the dynamic array if name is not _Delete + for (ULONG index = 2; index <= pMiniMd->getCountTypeDefs(); index ++ ) + { + TypeDefRec *pRec; + IfFailGo(pMiniMd->GetTypeDefRecord(index, &pRec)); + LPCSTR szTypeDefName; + IfFailGo(pMiniMd->getNameOfTypeDef(pRec, &szTypeDefName)); + if (IsDeletedName(szTypeDefName)) + { + continue; + } + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtTypeDef) ) ); + } + } + else + { + // create the enumerator + IfFailGo( HENUMInternal::CreateSimpleEnum( + mdtTypeDef, + 2, + pMiniMd->getCountTypeDefs() + 1, + &pEnum) ); + } + + // set the output parameter + *ppmdEnum = pEnum; + } + else + { + pEnum = *ppmdEnum; + } + + // we can only fill the minimun of what caller asked for or what we have left + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rTypeDefs, pcTypeDefs); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumTypeDefs); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumTypeDefs + +//***************************************************************************** +// Enumerate Sym.InterfaceImpl where Coclass == td +//***************************************************************************** +STDMETHODIMP RegMeta::EnumInterfaceImpls( + HCORENUM *phEnum, // Pointer to the enum. + mdTypeDef td, // TypeDef to scope the enumeration. + mdInterfaceImpl rImpls[], // Put InterfaceImpls here. + ULONG cMax, // Max InterfaceImpls to put. + ULONG *pcImpls) // Put # put here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + HENUMInternal *pEnum; + InterfaceImplRec *pRec; + ULONG index; + + LOG((LOGMD, "RegMeta::EnumInterfaceImpls(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, td, rImpls, cMax, pcImpls)); + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + + if ( *ppmdEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + if ( pMiniMd->IsSorted( TBL_InterfaceImpl ) ) + { + IfFailGo(pMiniMd->getInterfaceImplsForTypeDef(RidFromToken(td), &ridEnd, &ridStart)); + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtInterfaceImpl, ridStart, ridEnd, &pEnum) ); + } + else + { + // table is not sorted so we have to create dynmaic array + // create the dynamic enumerator + // + ridStart = 1; + ridEnd = pMiniMd->getCountInterfaceImpls() + 1; + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtInterfaceImpl, &pEnum) ); + + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->GetInterfaceImplRecord(index, &pRec)); + if ( td == pMiniMd->getClassOfInterfaceImpl(pRec) ) + { + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtInterfaceImpl) ) ); + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + else + { + pEnum = *ppmdEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rImpls, pcImpls); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + STOP_MD_PERF(EnumInterfaceImpls); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumInterfaceImpls + +STDMETHODIMP RegMeta::EnumGenericParams(HCORENUM *phEnum, mdToken tkOwner, + mdGenericParam rTokens[], ULONG cMaxTokens, ULONG *pcTokens) +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + HENUMInternal *pEnum; + GenericParamRec *pRec; + ULONG index; + CMiniMdRW *pMiniMd = NULL; + + + LOG((LOGMD, "RegMeta::EnumGenericParams(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, tkOwner, rTokens, cMaxTokens, pcTokens)); + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + + // See if this version of the metadata can do Generics + if (!pMiniMd->SupportsGenerics()) + { + if (pcTokens) + *pcTokens = 0; + hr = S_FALSE; + goto ErrExit; + } + + + _ASSERTE(TypeFromToken(tkOwner) == mdtTypeDef || TypeFromToken(tkOwner) == mdtMethodDef); + + + if ( *ppmdEnum == 0 ) + { + // instantiating a new ENUM + + //@todo GENERICS: review this. Are we expecting a sorted table or not? + if ( pMiniMd->IsSorted( TBL_GenericParam ) ) + { + if (TypeFromToken(tkOwner) == mdtTypeDef) + { + IfFailGo(pMiniMd->getGenericParamsForTypeDef(RidFromToken(tkOwner), &ridEnd, &ridStart)); + } + else + { + IfFailGo(pMiniMd->getGenericParamsForMethodDef(RidFromToken(tkOwner), &ridEnd, &ridStart)); + } + + IfFailGo( HENUMInternal::CreateSimpleEnum(mdtGenericParam, ridStart, ridEnd, &pEnum) ); + } + else + { + // table is not sorted so we have to create dynamic array + // create the dynamic enumerator + // + ridStart = 1; + ridEnd = pMiniMd->getCountGenericParams() + 1; + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum(mdtGenericParam, &pEnum) ); + + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->GetGenericParamRecord(index, &pRec)); + if ( tkOwner == pMiniMd->getOwnerOfGenericParam(pRec) ) + { + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtGenericParam) ) ); + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + else + { + pEnum = *ppmdEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + STOP_MD_PERF(EnumGenericPars); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::EnumGenericParams + +STDMETHODIMP RegMeta::EnumMethodSpecs( + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdToken tkOwner, // [IN] MethodDef or MemberRef whose MethodSpecs are requested + mdMethodSpec rTokens[], // [OUT] Put MethodSpecs here. + ULONG cMaxTokens, // [IN] Max tokens to put. + ULONG *pcTokens) // [OUT] Put actual count here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + HENUMInternal *pEnum; + MethodSpecRec *pRec; + ULONG index; + CMiniMdRW *pMiniMd = NULL; + + LOG((LOGMD, "RegMeta::EnumMethodSpecs(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, tkOwner, rTokens, cMaxTokens, pcTokens)); + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + // See if this version of the metadata can do Generics + if (!pMiniMd->SupportsGenerics()) + { + if (pcTokens) + *pcTokens = 0; + hr = S_FALSE; + goto ErrExit; + } + + + _ASSERTE(RidFromToken(tkOwner)==0 || TypeFromToken(tkOwner) == mdtMethodDef || TypeFromToken(tkOwner) == mdtMemberRef); + + + if ( *ppmdEnum == 0 ) + { + // instantiating a new ENUM + + if(RidFromToken(tkOwner)==0) // enumerate all MethodSpecs + { + ridStart = 1; + ridEnd = pMiniMd->getCountMethodSpecs() + 1; + + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtMethodSpec, ridStart, ridEnd, &pEnum) ); + } + else + { + //@todo GENERICS: review this. Are we expecting a sorted table or not? + if ( pMiniMd->IsSorted( TBL_MethodSpec ) ) + { + if (TypeFromToken(tkOwner) == mdtMemberRef) + { + IfFailGo(pMiniMd->getMethodSpecsForMemberRef(RidFromToken(tkOwner), &ridEnd, &ridStart)); + } + else + { + IfFailGo(pMiniMd->getMethodSpecsForMethodDef(RidFromToken(tkOwner), &ridEnd, &ridStart)); + } + + IfFailGo( HENUMInternal::CreateSimpleEnum(mdtMethodSpec, ridStart, ridEnd, &pEnum) ); + } + else + { + // table is not sorted so we have to create dynamic array + // create the dynamic enumerator + // + ridStart = 1; + ridEnd = pMiniMd->getCountMethodSpecs() + 1; + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum(mdtMethodSpec, &pEnum) ); + + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->GetMethodSpecRecord(index, &pRec)); + if ( tkOwner == pMiniMd->getMethodOfMethodSpec(pRec) ) + { + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtMethodSpec) ) ); + } + } + } + } + // set the output parameter + *ppmdEnum = pEnum; + } + else + { + pEnum = *ppmdEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + STOP_MD_PERF(EnumMethodSpecs); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumMethodSpecs() + +STDMETHODIMP RegMeta::EnumGenericParamConstraints( + HCORENUM *phEnum, // [IN|OUT] Pointer to the enum. + mdGenericParam tkOwner, // [IN] GenericParam whose constraints are requested + mdGenericParamConstraint rTokens[], // [OUT] Put GenericParamConstraints here. + ULONG cMaxTokens, // [IN] Max GenericParamConstraints to put. + ULONG *pcTokens) // [OUT] Put # of tokens here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG ridStart; + ULONG ridEnd; + HENUMInternal *pEnum; + GenericParamConstraintRec *pRec; + ULONG index; + CMiniMdRW *pMiniMd = NULL; + + LOG((LOGMD, "RegMeta::EnumGenericParamConstraints(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, tkOwner, rTokens, cMaxTokens, pcTokens)); + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + + if(TypeFromToken(tkOwner) != mdtGenericParam) + IfFailGo(META_E_BAD_INPUT_PARAMETER); + + // See if this version of the metadata can do Generics + if (!pMiniMd->SupportsGenerics()) + { + if (pcTokens) + *pcTokens = 0; + hr = S_FALSE; + goto ErrExit; + } + + if ( *ppmdEnum == 0 ) + { + // instantiating a new ENUM + + //<TODO> GENERICS: review this. Are we expecting a sorted table or not? </TODO> + if ( pMiniMd->IsSorted( TBL_GenericParamConstraint ) ) + { + IfFailGo(pMiniMd->getGenericParamConstraintsForGenericParam(RidFromToken(tkOwner), &ridEnd, &ridStart)); + IfFailGo( HENUMInternal::CreateSimpleEnum(mdtGenericParamConstraint, ridStart, ridEnd, &pEnum) ); + } + else + { + // table is not sorted so we have to create dynamic array + // create the dynamic enumerator + // + ridStart = 1; + ridEnd = pMiniMd->getCountGenericParamConstraints() + 1; + + IfFailGo( HENUMInternal::CreateDynamicArrayEnum(mdtGenericParamConstraint, &pEnum)); + + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(index, &pRec)); + if ( tkOwner == pMiniMd->getOwnerOfGenericParamConstraint(pRec)) + { + IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, + mdtGenericParamConstraint))); + } + } + } + + // set the output parameter + *ppmdEnum = pEnum; + } + else + { + pEnum = *ppmdEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + STOP_MD_PERF(EnumGenericParamConstraints); + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +//***************************************************************************** +// Enumerate Sym.TypeRef +//***************************************************************************** +STDMETHODIMP RegMeta::EnumTypeRefs( + HCORENUM *phEnum, // Pointer to the enumerator. + mdTypeRef rTypeRefs[], // Put TypeRefs here. + ULONG cMax, // Max TypeRefs to put. + ULONG *pcTypeRefs) // Put # put here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); + ULONG cTotal; + HENUMInternal *pEnum = *ppmdEnum; + + + + LOG((LOGMD, "RegMeta::EnumTypeRefs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + phEnum, rTypeRefs, cMax, pcTypeRefs)); + START_MD_PERF(); + LOCKREAD(); + + if ( pEnum == 0 ) + { + // instantiating a new ENUM + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + cTotal = pMiniMd->getCountTypeRefs(); + + IfFailGo( HENUMInternal::CreateSimpleEnum( mdtTypeRef, 1, cTotal + 1, &pEnum) ); + + // set the output parameter + *ppmdEnum = pEnum; + } + + // fill the output token buffer + hr = HENUMInternal::EnumWithCount(pEnum, cMax, rTypeRefs, pcTypeRefs); + +ErrExit: + HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + + + STOP_MD_PERF(EnumTypeRefs); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::EnumTypeRefs() + +//***************************************************************************** +// Given a namespace and a class name, return the typedef +//***************************************************************************** +STDMETHODIMP RegMeta::FindTypeDefByName(// S_OK or error. + LPCWSTR wzTypeDef, // [IN] Name of the Type. + mdToken tkEnclosingClass, // [IN] Enclosing class. + mdTypeDef *ptd) // [OUT] Put the TypeDef token here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW + + LOG((LOGMD, "{%08x} RegMeta::FindTypeDefByName(%S, 0x%08x, 0x%08x)\n", + this, MDSTR(wzTypeDef), tkEnclosingClass, ptd)); + START_MD_PERF(); + LOCKREAD(); + + + if (wzTypeDef == NULL) + IfFailGo(E_INVALIDARG); + PREFIX_ASSUME(wzTypeDef != NULL); + LPSTR szTypeDef; + UTF8STR(wzTypeDef, szTypeDef); + LPCSTR szNamespace; + LPCSTR szName; + + _ASSERTE(ptd); + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef || + TypeFromToken(tkEnclosingClass) == mdtTypeRef || + IsNilToken(tkEnclosingClass)); + + // initialize output parameter + *ptd = mdTypeDefNil; + + ns::SplitInline(szTypeDef, szNamespace, szName); + hr = ImportHelper::FindTypeDefByName(&(m_pStgdb->m_MiniMd), + szNamespace, + szName, + tkEnclosingClass, + ptd); +ErrExit: + + STOP_MD_PERF(FindTypeDefByName); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::FindTypeDefByName() + +//***************************************************************************** +// Get values from Sym.Module +//***************************************************************************** +STDMETHODIMP RegMeta::GetScopeProps( + __out_ecount_opt (cchName) LPWSTR szName, // Put name here + ULONG cchName, // Size in chars of name buffer + ULONG *pchName, // Put actual length of name here + GUID *pmvid) // Put MVID here +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + ModuleRec *pModuleRec; + + + LOG((LOGMD, "RegMeta::GetScopeProps(%S, 0x%08x, 0x%08x, 0x%08x)\n", + MDSTR(szName), cchName, pchName, pmvid)); + START_MD_PERF(); + LOCKREAD(); + + // there is only one module record + IfFailGo(pMiniMd->GetModuleRecord(1, &pModuleRec)); + + if (pmvid != NULL) + { + IfFailGo(pMiniMd->getMvidOfModule(pModuleRec, pmvid)); + } + // This call has to be last to set 'hr', so CLDB_S_TRUNCATION is not rewritten with S_OK + if (szName || pchName) + IfFailGo( pMiniMd->getNameOfModule(pModuleRec, szName, cchName, pchName) ); +ErrExit: + + STOP_MD_PERF(GetScopeProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetScopeProps() + +//***************************************************************************** +// Get the token for a Scope's (primary) module record. +//***************************************************************************** +STDMETHODIMP RegMeta::GetModuleFromScope(// S_OK. + mdModule *pmd) // [OUT] Put mdModule token here. +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::GetModuleFromScope(0x%08x)\n", pmd)); + START_MD_PERF(); + + _ASSERTE(pmd); + + // No need to lock this function. + + *pmd = TokenFromRid(1, mdtModule); + + STOP_MD_PERF(GetModuleFromScope); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetModuleFromScope() + +//***************************************************************************** +// Given a token, is it (or its parent) global? +//***************************************************************************** +HRESULT RegMeta::IsGlobal( // S_OK ir error. + mdToken tk, // [IN] Type, Field, or Method token. + int *pbGlobal) // [OUT] Put 1 if global, 0 otherwise. +{ + HRESULT hr=S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + mdToken tkParent; // Parent of field or method. + + LOG((LOGMD, "RegMeta::GetTokenForGlobalType(0x%08x, %08x)\n", tk, pbGlobal)); + //START_MD_PERF(); + + // No need to lock this function. + + if (!IsValidToken(tk)) + IfFailGo(E_INVALIDARG); + + switch (TypeFromToken(tk)) + { + case mdtTypeDef: + *pbGlobal = IsGlobalMethodParentToken(tk); + break; + + case mdtFieldDef: + IfFailGo( pMiniMd->FindParentOfFieldHelper(tk, &tkParent) ); + *pbGlobal = IsGlobalMethodParentToken(tkParent); + break; + + case mdtMethodDef: + IfFailGo( pMiniMd->FindParentOfMethodHelper(tk, &tkParent) ); + *pbGlobal = IsGlobalMethodParentToken(tkParent); + break; + + case mdtProperty: + IfFailGo( pMiniMd->FindParentOfPropertyHelper(tk, &tkParent) ); + *pbGlobal = IsGlobalMethodParentToken(tkParent); + break; + + case mdtEvent: + IfFailGo( pMiniMd->FindParentOfEventHelper(tk, &tkParent) ); + *pbGlobal = IsGlobalMethodParentToken(tkParent); + break; + + // Anything else is NOT global. + default: + *pbGlobal = FALSE; + } + +ErrExit: + //STOP_MD_PERF(GetModuleFromScope); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::IsGlobal() + +//***************************************************************************** +// return flags for a given class +//***************************************************************************** +HRESULT +RegMeta::GetTypeDefProps( + mdTypeDef td, // [IN] TypeDef token for inquiry. + __out_ecount_opt (cchTypeDef) LPWSTR szTypeDef, // [OUT] Put name here. + ULONG cchTypeDef, // [IN] size of name buffer in wide chars. + ULONG *pchTypeDef, // [OUT] put size of name (wide chars) here. + DWORD *pdwTypeDefFlags, // [OUT] Put flags here. + mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + TypeDefRec *pTypeDefRec; + BOOL fTruncation = FALSE; // Was there name truncation? + + LOG((LOGMD, "{%08x} RegMeta::GetTypeDefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + this, td, szTypeDef, cchTypeDef, pchTypeDef, + pdwTypeDefFlags, ptkExtends)); + START_MD_PERF(); + LOCKREAD(); + + if (TypeFromToken(td) != mdtTypeDef) + { + hr = S_FALSE; + goto ErrExit; + } + if (td == mdTypeDefNil) + { // Backward compatibility with CLR 2.0 implementation + if (pdwTypeDefFlags != NULL) + *pdwTypeDefFlags = 0; + if (ptkExtends != NULL) + *ptkExtends = mdTypeRefNil; + if (pchTypeDef != NULL) + *pchTypeDef = 1; + if ((szTypeDef != NULL) && (cchTypeDef > 0)) + szTypeDef[0] = 0; + + hr = S_OK; + goto ErrExit; + } + + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + + if ((szTypeDef != NULL) || (pchTypeDef != NULL)) + { + LPCSTR szNamespace; + LPCSTR szName; + + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzNamespace, szNamespace); + IfNullGo(wzNamespace); + + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzName, szName); + IfNullGo(wzName); + + if (szTypeDef != NULL) + { + fTruncation = !(ns::MakePath(szTypeDef, cchTypeDef, wzNamespace, wzName)); + } + if (pchTypeDef != NULL) + { + if (fTruncation || (szTypeDef == NULL)) + { + *pchTypeDef = ns::GetFullLength(wzNamespace, wzName); + } + else + { + *pchTypeDef = (ULONG)(wcslen(szTypeDef) + 1); + } + } + } + if (pdwTypeDefFlags != NULL) + { // caller wants type flags + *pdwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec); + } + if (ptkExtends != NULL) + { + *ptkExtends = pMiniMd->getExtendsOfTypeDef(pTypeDefRec); + + // take care of the 0 case + if (RidFromToken(*ptkExtends) == 0) + { + *ptkExtends = mdTypeRefNil; + } + } + + if (fTruncation && (hr == S_OK)) + { + if ((szTypeDef != NULL) && (cchTypeDef > 0)) + { // null-terminate the truncated output string + szTypeDef[cchTypeDef - 1] = W('\0'); + } + hr = CLDB_S_TRUNCATION; + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + STOP_MD_PERF(GetTypeDefProps); + + return hr; +} // RegMeta::GetTypeDefProps + +//***************************************************************************** +// Retrieve information about an implemented interface. +//***************************************************************************** +STDMETHODIMP RegMeta::GetInterfaceImplProps( // S_OK or error. + mdInterfaceImpl iiImpl, // [IN] InterfaceImpl token. + mdTypeDef *pClass, // [OUT] Put implementing class token here. + mdToken *ptkIface) // [OUT] Put implemented interface token here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd = NULL; + InterfaceImplRec *pIIRec = NULL; + + + + LOG((LOGMD, "RegMeta::GetInterfaceImplProps(0x%08x, 0x%08x, 0x%08x)\n", + iiImpl, pClass, ptkIface)); + START_MD_PERF(); + LOCKREAD(); + + _ASSERTE(TypeFromToken(iiImpl) == mdtInterfaceImpl); + + pMiniMd = &(m_pStgdb->m_MiniMd); + IfFailGo(pMiniMd->GetInterfaceImplRecord(RidFromToken(iiImpl), &pIIRec)); + + if (pClass) + { + *pClass = pMiniMd->getClassOfInterfaceImpl(pIIRec); + } + if (ptkIface) + { + *ptkIface = pMiniMd->getInterfaceOfInterfaceImpl(pIIRec); + } + +ErrExit: + STOP_MD_PERF(GetInterfaceImplProps); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::GetInterfaceImplProps() + +//***************************************************************************** +// Retrieve information about a TypeRef. +//***************************************************************************** +STDMETHODIMP +RegMeta::GetTypeRefProps( + mdTypeRef tr, // The class ref token. + mdToken *ptkResolutionScope, // Resolution scope, ModuleRef or AssemblyRef. + __out_ecount_opt (cchTypeRef) LPWSTR szTypeRef, // Put the name here. + ULONG cchTypeRef, // Size of the name buffer, wide chars. + ULONG *pchTypeRef) // Put actual size of name here. +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW *pMiniMd; + TypeRefRec *pTypeRefRec; + BOOL fTruncation = FALSE; // Was there name truncation? + + LOG((LOGMD, "RegMeta::GetTypeRefProps(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + tr, ptkResolutionScope, szTypeRef, cchTypeRef, pchTypeRef)); + + START_MD_PERF(); + LOCKREAD(); + + if (TypeFromToken(tr) != mdtTypeRef) + { + hr = S_FALSE; + goto ErrExit; + } + if (tr == mdTypeRefNil) + { // Backward compatibility with CLR 2.0 implementation + if (ptkResolutionScope != NULL) + *ptkResolutionScope = mdTokenNil; + if (pchTypeRef != NULL) + *pchTypeRef = 1; + if ((szTypeRef != NULL) && (cchTypeRef > 0)) + szTypeRef[0] = 0; + + hr = S_OK; + goto ErrExit; + } + + pMiniMd = &(m_pStgdb->m_MiniMd); + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec)); + + if (ptkResolutionScope != NULL) + { + *ptkResolutionScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); + } + + if ((szTypeRef != NULL) || (pchTypeRef != NULL)) + { + LPCSTR szNamespace; + LPCSTR szName; + + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzNamespace, szNamespace); + IfNullGo(wzNamespace); + + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzName, szName); + IfNullGo(wzName); + + if (szTypeRef != NULL) + { + fTruncation = !(ns::MakePath(szTypeRef, cchTypeRef, wzNamespace, wzName)); + } + if (pchTypeRef != NULL) + { + if (fTruncation || (szTypeRef == NULL)) + { + *pchTypeRef = ns::GetFullLength(wzNamespace, wzName); + } + else + { + *pchTypeRef = (ULONG)(wcslen(szTypeRef) + 1); + } + } + } + if (fTruncation && (hr == S_OK)) + { + if ((szTypeRef != NULL) && (cchTypeRef > 0)) + { // null-terminate the truncated output string + szTypeRef[cchTypeRef - 1] = W('\0'); + } + hr = CLDB_S_TRUNCATION; + } + +ErrExit: + STOP_MD_PERF(GetTypeRefProps); + END_ENTRYPOINT_NOTHROW; + return hr; +} // RegMeta::GetTypeRefProps + +//***************************************************************************** +// Given a TypeRef name, return the typeref +//***************************************************************************** +STDMETHODIMP RegMeta::FindTypeRef( // S_OK or error. + mdToken tkResolutionScope, // [IN] Resolution Scope. + LPCWSTR wzTypeName, // [IN] Name of the TypeRef. + mdTypeRef *ptk) // [OUT] Put the TypeRef token here. +{ + HRESULT hr = S_OK; // A result. + + BEGIN_ENTRYPOINT_NOTHROW; + + LPUTF8 szFullName; + LPCUTF8 szNamespace; + LPCUTF8 szName; + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + + _ASSERTE(wzTypeName && ptk); + + + + LOG((LOGMD, "RegMeta::FindTypeRef(0x%8x, %ls, 0x%08x)\n", + tkResolutionScope, MDSTR(wzTypeName), ptk)); + START_MD_PERF(); + LOCKREAD(); + + // Convert the name to UTF8. + PREFIX_ASSUME(wzTypeName != NULL); // caller might pass NULL, but they'll AV. + UTF8STR(wzTypeName, szFullName); + ns::SplitInline(szFullName, szNamespace, szName); + + // Look up the name. + hr = ImportHelper::FindTypeRefByName(pMiniMd, tkResolutionScope, + szNamespace, + szName, + ptk); +ErrExit: + + STOP_MD_PERF(FindTypeRef); + END_ENTRYPOINT_NOTHROW; + + return hr; +} // STDMETHODIMP RegMeta::FindTypeRef() + +//******************************************************************************* +// Find a given param of a Method. +//******************************************************************************* +HRESULT RegMeta::_FindParamOfMethod( // S_OK or error. + mdMethodDef md, // [IN] The owning method of the param. + ULONG iSeq, // [IN] The sequence # of the param. + mdParamDef *pParamDef) // [OUT] Put ParamDef token here. +{ + HRESULT hr; + ParamRec *pParamRec; + RID ridStart, ridEnd; + RID pmRid; + + _ASSERTE(TypeFromToken(md) == mdtMethodDef && pParamDef); + + // get the methoddef record + MethodRec *pMethodRec; + IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + + // figure out the start rid and end rid of the parameter list of this methoddef + ridStart = m_pStgdb->m_MiniMd.getParamListOfMethod(pMethodRec); + IfFailRet(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(md), &ridEnd)); + + // loop through each param + // <TODO>@consider: parameters are sorted by sequence. Maybe a binary search? + //</TODO> + for (; ridStart < ridEnd; ridStart++) + { + IfFailRet(m_pStgdb->m_MiniMd.GetParamRid(ridStart, &pmRid)); + IfFailRet(m_pStgdb->m_MiniMd.GetParamRecord(pmRid, &pParamRec)); + if (iSeq == m_pStgdb->m_MiniMd.getSequenceOfParam(pParamRec)) + { + // parameter has the sequence number matches what we are looking for + *pParamDef = TokenFromRid(pmRid, mdtParamDef); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // HRESULT RegMeta::_FindParamOfMethod() + +//******************************************************************************* +// Given the signature, return the token for signature. +//******************************************************************************* +HRESULT RegMeta::_GetTokenFromSig( // S_OK or error. + PCCOR_SIGNATURE pvSig, // [IN] Signature to define. + ULONG cbSig, // [IN] Size of signature data. + mdSignature *pmsig) // [OUT] returned signature token. +{ + HRESULT hr = S_OK; + + _ASSERTE(pmsig); + + if (CheckDups(MDDupSignature)) + { + hr = ImportHelper::FindStandAloneSig(&(m_pStgdb->m_MiniMd), pvSig, cbSig, pmsig); + if (SUCCEEDED(hr)) + { + if (IsENCOn()) + return S_OK; + else + return META_S_DUPLICATE; + } + else if (hr != CLDB_E_RECORD_NOTFOUND) + IfFailGo(hr); + } + + // Create a new record. + StandAloneSigRec *pSigRec; + RID iSigRec; + + IfFailGo(m_pStgdb->m_MiniMd.AddStandAloneSigRecord(&pSigRec, &iSigRec)); + + // Set output parameter. + *pmsig = TokenFromRid(iSigRec, mdtSignature); + + // Save signature. + IfFailGo(m_pStgdb->m_MiniMd.PutBlob(TBL_StandAloneSig, StandAloneSigRec::COL_Signature, + pSigRec, pvSig, cbSig)); + IfFailGo(UpdateENCLog(*pmsig)); +ErrExit: + return hr; +} // RegMeta::_GetTokenFromSig diff --git a/src/coreclr/md/compiler/regmeta_vm.cpp b/src/coreclr/md/compiler/regmeta_vm.cpp new file mode 100644 index 00000000000..04b69c7d5cc --- /dev/null +++ b/src/coreclr/md/compiler/regmeta_vm.cpp @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +//***************************************************************************** + +// +// RegMeta.cpp +// +// Implementation for meta data public interface methods for full version. +// +//***************************************************************************** +#include "stdafx.h" +#include "regmeta.h" +#include "metadata.h" +#include "corerror.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "importhelper.h" +#include "filtermanager.h" +#include "mdperf.h" +#include "switches.h" +#include "posterror.h" +#include "stgio.h" +#include "sstring.h" + +#include <metamodelrw.h> + + + + +#define DEFINE_CUSTOM_NODUPCHECK 1 +#define DEFINE_CUSTOM_DUPCHECK 2 +#define SET_CUSTOM 3 + +#if defined(_DEBUG) && defined(_TRACE_REMAPS) +#define LOGGING +#endif +#include <log.h> + +#ifdef _MSC_VER +#pragma warning(disable: 4102) +#endif + +//***************************************************************************** +// Call this after initialization is complete. +//***************************************************************************** +HRESULT RegMeta::AddToCache() +{ +#if defined(FEATURE_METADATA_IN_VM) + HRESULT hr = S_OK; + + // The ref count must be > 0 before the module is published, else another + // thread could find, use, and release the module, causing it to be deleted + // before this thread gets a chance to addref. + _ASSERTE(GetRefCount() > 0); + // add this RegMeta to the loaded module list. + m_bCached = true; + IfFailGo(LOADEDMODULES::AddModuleToLoadedList(this)); +ErrExit: + if (FAILED(hr)) + { + _ASSERTE(!LOADEDMODULES::IsEntryInList(this)); + m_bCached = false; + } + return hr; +#else // FEATURE_METADATA_IN_VM + return S_OK; +#endif // FEATURE_METADATA_IN_VM +} // RegMeta::AddToCache + + +//***************************************************************************** +// Search the cached RegMetas for a given scope. +//***************************************************************************** +HRESULT RegMeta::FindCachedReadOnlyEntry( + LPCWSTR szName, // Name of the desired file. + DWORD dwOpenFlags, // Flags the new file is opened with. + RegMeta **ppMeta) // Put found RegMeta here. +{ +#if defined(FEATURE_METADATA_IN_VM) + return LOADEDMODULES::FindCachedReadOnlyEntry(szName, dwOpenFlags, ppMeta); +#else // FEATURE_METADATA_IN_VM + // No cache support in standalone version. + *ppMeta = NULL; + return S_FALSE; +#endif // FEATURE_METADATA_IN_VM +} // RegMeta::FindCachedReadOnlyEntry + + +#ifdef FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// Helper function to startup the EE +// +// Notes: +// This is called by code:RegMeta.DefineSecurityAttributeSet. +//***************************************************************************** +HRESULT RegMeta::StartupEE() +{ + UNREACHABLE_MSG_RET("About to CoCreateInstance! This code should not be " + "reachable or needs to be reimplemented for CoreCLR!"); +} + +#endif //FEATURE_METADATA_EMIT_ALL + +#ifdef FEATURE_METADATA_EMIT + +//***************************************************************************** +// Persist a set of security custom attributes into a set of permission set +// blobs on the same class or method. +// +// Notes: +// Only in the full version because this is an emit operation. +//***************************************************************************** +HRESULT RegMeta::DefineSecurityAttributeSet(// Return code. + mdToken tkObj, // [IN] Class or method requiring security attributes. + COR_SECATTR rSecAttrs[], // [IN] Array of security attribute descriptions. + ULONG cSecAttrs, // [IN] Count of elements in above array. + ULONG *pulErrorAttr) // [OUT] On error, index of attribute causing problem. +{ + return E_NOTIMPL; +} // RegMeta::DefineSecurityAttributeSet + +#endif //FEATURE_METADATA_EMIT + + +//***************************************************************************** +// Implementation of IMetaDataImport::ResolveTypeRef to resolve a typeref across scopes. +// +// Arguments: +// tr - typeref within this scope to resolve +// riid - interface on ppIScope to support +// ppIScope - out-parameter to get metadata scope for typedef (*ptd) +// ptd - out-parameter to get typedef that the ref resolves to. +// +// Notes: +// TypeDefs define a type within a scope. TypeRefs refer to type-defs in other scopes +// and allow you to import a type from another scope. This function attempts to determine +// which type-def a type-ref points to. +// +// This resolve (type-ref, this cope) --> (type-def=*ptd, other scope=*ppIScope) +// +// However, this resolution requires knowing what modules have been loaded, which is not decided +// until runtime via loader / fusion policy. Thus this interface can't possibly be correct since +// it doesn't have that knowledge. Furthermore, when inspecting metadata from another process +// (such as a debugger inspecting the debuggee's metadata), this API can be truly misleading. +// +// This API usage should be avoided. +// +//***************************************************************************** +STDMETHODIMP +RegMeta::ResolveTypeRef( + mdTypeRef tr, + REFIID riid, + IUnknown ** ppIScope, + mdTypeDef * ptd) +{ +#ifdef FEATURE_METADATA_IN_VM + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + TypeRefRec * pTypeRefRec; + WCHAR wzNameSpace[_MAX_PATH]; + CMiniMdRW * pMiniMd = NULL; + + LOG((LOGMD, "{%08x} RegMeta::ResolveTypeRef(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + this, tr, riid, ppIScope, ptd)); + + START_MD_PERF(); + LOCKREAD(); + + pMiniMd = &(m_pStgdb->m_MiniMd); + + _ASSERTE((ppIScope != NULL) && (ptd != NULL)); + + // Init the output values. + *ppIScope = NULL; + *ptd = 0; + + if (IsNilToken(tr)) + { + if (ptd != NULL) + { + *ptd = mdTypeDefNil; + } + + if (ppIScope != NULL) + { + *ppIScope = NULL; + } + + STOP_MD_PERF(ResolveTypeRef); + hr = E_INVALIDARG; + goto ErrExit; + } + + if (TypeFromToken(tr) == mdtTypeDef) + { + // Shortcut when we receive a TypeDef token + *ptd = tr; + STOP_MD_PERF(ResolveTypeRef); + hr = this->QueryInterface(riid, (void **)ppIScope); + goto ErrExit; + } + + // Get the class ref row. + _ASSERTE(TypeFromToken(tr) == mdtTypeRef); + + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, wzNameSpace, lengthof(wzNameSpace), NULL)); + if (hr != NOERROR) + { + _ASSERTE(hr == CLDB_S_TRUNCATION); + // Truncate the namespace string + wzNameSpace[lengthof(wzNameSpace) - 1] = 0; + } + + //*********************** + // before we go off to CORPATH, check the loaded modules! + //*********************** + if (LOADEDMODULES::ResolveTypeRefWithLoadedModules( + tr, + this, + pMiniMd, + riid, + ppIScope, + ptd) == NOERROR) + { + // Done!! We found one match among the loaded modules. + goto ErrExit; + } + + IfFailGo(META_E_CANNOTRESOLVETYPEREF); + +ErrExit: + STOP_MD_PERF(ResolveTypeRef); + END_ENTRYPOINT_NOTHROW; + + return hr; +#else // FEATURE_METADATA_IN_VM + return E_NOTIMPL; +#endif // FEATURE_METADATA_IN_VM +} // RegMeta::ResolveTypeRef + + + +// Full version handles metadata caching, which Release() needs to coordinate with. +// Thus Release() is in a satellite lib. +ULONG RegMeta::Release() +{ + BEGIN_CLEANUP_ENTRYPOINT; + +#if defined(FEATURE_METADATA_IN_VM) + _ASSERTE(!m_bCached || LOADEDMODULES::IsEntryInList(this)); +#else + _ASSERTE(!m_bCached); +#endif // FEATURE_METADATA_IN_VM + BOOL bCached = m_bCached; + ULONG cRef = InterlockedDecrement(&m_cRef); + // NOTE: 'this' may be unsafe after this point, if the module is cached, and + // another thread finds the module in the cache, releases it, and deletes it + // before we get around to deleting it. (That's why we must make a local copy + // of m_bCached.) + // If no references left... + if (cRef == 0) + { + if (!bCached) + { // If the module is not (was not) cached, no other thread can have + // discovered the module, so this thread can now safely delete it. + delete this; + } +#if defined(FEATURE_METADATA_IN_VM) + else if (LOADEDMODULES::RemoveModuleFromLoadedList(this)) + { // If the module was cached, RemoveModuleFromLoadedList() will try to + // safely un-publish the module, and if it succeeds, no other thread + // has (or will) discover the module, so this thread can delete it. + m_bCached = false; + delete this; + } +#endif // FEATURE_METADATA_IN_VM + } + END_CLEANUP_ENTRYPOINT + + return cRef; +} // RegMeta::Release diff --git a/src/coreclr/md/compiler/stdafx.h b/src/coreclr/md/compiler/stdafx.h new file mode 100644 index 00000000000..01636b82aca --- /dev/null +++ b/src/coreclr/md/compiler/stdafx.h @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// stdafx.h +// + +// +// Precompiled headers. +// +//***************************************************************************** +#ifndef __STDAFX_H_ +#define __STDAFX_H_ + +#include <crtwrap.h> +#include <winwrap.h> +#include <utilcode.h> + +#include <cor.h> +#include <corpriv.h> + +#include "../hotdata/hotheap.h" +#include <metamodelro.h> +#include <liteweightstgdb.h> + +#include "nsutilpriv.h" + +#include "utsem.h" + +#endif // __STDAFX_H_ diff --git a/src/coreclr/md/compiler/verifylayouts.cpp b/src/coreclr/md/compiler/verifylayouts.cpp new file mode 100644 index 00000000000..24f9498e3d9 --- /dev/null +++ b/src/coreclr/md/compiler/verifylayouts.cpp @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// VerifyLayouts.cpp +// + +// +// Make sure that layouts of MD data structures doesn't change accidentally +// +//***************************************************************************** + +#include "stdafx.h" +#include "verifylayouts.h" diff --git a/src/coreclr/md/compressedinteger.h b/src/coreclr/md/compressedinteger.h new file mode 100644 index 00000000000..e6ee51ab217 --- /dev/null +++ b/src/coreclr/md/compressedinteger.h @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: CompressedInteger.h +// + +// +// Class code:MetaData::CompressedInteger provides secure access to a compressed integer (as defined in CLI +// ECMA specification). The integer is compressed into 1, 2 or 4 bytes. See code:CompressedInteger#Format +// for full format description. +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class provides secure access to a compressed integer (as defined in CLI ECMA specification). The +// integer is compressed into 1, 2 or 4 bytes. See code:CompressedInteger#Format for full format description. +// +class CompressedInteger +{ +// #Format +// +// The format/encoding of compressed integer is (defined in ECMA CLI specification): +// The encoding is 1, 2 or 4 bytes long and depends on the first byte value. If the first byte is (binary): +// * 0xxx xxxx ... then it's 1 byte long and the value is 0xxx xxxx. +// * 10xx xxxx ... then it's 2 bytes long and the value is 00xx xxxx yyyy yyyy, where yyyy yyyy is the +// second byte. Though values smaller than code:const_Max1Byte are technically invalid +// when encoded with 2 bytes. +// * 110x xxxx ... then it's 4 bytes long and the value is 000x xxxx yyyy yyyy zzzz zzzz wwww wwww, where +// yyyy yyyy is the 2nd byte, zzzz zzzz is the 3rd byte and wwww wwww is the 4th byte. +// Though values smaller than code:const_Max2Bytes are technically invalid when encoded +// with 4 bytes. +// * 111x xxxx ... then it's invalid encoding. +// +// Note: Some encodings are invalid, but CLR accepts them (see code:DataBlob::GetCompressedU), +// e.g. 1000 0000 0000 0000 (0x8000) encodes 0 while correct/valid encoding is 0000 0000 (0x00). +// +private: + // This class has only static methods and shouldn't be instantiated. + CompressedInteger() {} + +public: + static const UINT32 const_MaxEncodingSize = 4; + + static const UINT32 const_Max1Byte = 0x7f; + static const UINT32 const_Max2Bytes = 0x3fff; + static const UINT32 const_Max4Bytes = 0x1fffffff; + + static const UINT32 const_Max = const_Max4Bytes; + +public: + // + // Operations + // + + // Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills + // *pcbEncodingSize with 1, 2 or 4. + // Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize + // then. + __checkReturn + __success(return) + static inline BOOL GetEncodingSize( + UINT32 nValue, + __out UINT32 *pcbEncodingSize); + // Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills + // *pcbEncodingSize with 1, 2 or 4 and *pnEncodedValue with the encoded value. + // Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize + // nor *pnEncodedValue then. + __success(return) + static inline BOOL Encode( + UINT32 nValue, + __out UINT32 *pnEncodedValue, + __out UINT32 *pcbEncodingSize); + +}; // class CompressedInteger + +}; // namespace MetaData + +#include "compressedinteger.inl" diff --git a/src/coreclr/md/compressedinteger.inl b/src/coreclr/md/compressedinteger.inl new file mode 100644 index 00000000000..acdc504b686 --- /dev/null +++ b/src/coreclr/md/compressedinteger.inl @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: CompressedInteger.inl +// + +// +// Class code:MetaData::CompressedInteger provides secure access to a compressed integer (as defined in CLI +// ECMA specification). The integer is compressed into 1, 2 or 4 bytes. See code:CompressedInteger#Format +// for full format description. +// +// ====================================================================================== + +#pragma once + +#include "compressedinteger.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills +// *pcbEncodingSize with 1, 2 or 4. +// Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize then. +// +__checkReturn +//static +inline +BOOL +CompressedInteger::GetEncodingSize( + UINT32 nValue, + __out UINT32 *pcbEncodingSize) +{ + // Does it fit into 1-byte encoding? + if (nValue <= const_Max1Byte) + { // The value fits into 1 byte (binary format 0xxx xxxx) + *pcbEncodingSize = 1; + return TRUE; + } + // Does it fit into 2-bytes encoding? + if (nValue <= const_Max2Bytes) + { // The value fits into 2 bytes (binary format 10xx xxxx yyyy yyyy) + *pcbEncodingSize = 2; + return TRUE; + } + // Does it fit into 4-bytes encoding? + if (nValue <= const_Max4Bytes) + { // The value fits into 4 bytes (binary format 110x xxxx yyyy yyyy zzzz zzzz wwww wwww) + *pcbEncodingSize = 4; + return TRUE; + } + // The value cannot be encoded as compressed integer + return FALSE; +} // CompressedInteger::GetEncodingSize + +// -------------------------------------------------------------------------------------- +// +// Returns TRUE if the value (nValue) fits into 1-byte, 2-bytes or 4-bytes encoding and fills +// *pcbEncodingSize with 1, 2 or 4 and *pnEncodedValue with the encoded value. +// Returns FALSE if the value cannot be encoded as compressed integer, doesn't fill *pcbEncodingSize +// nor *pnEncodedValue then. +// +__checkReturn +//static +inline +BOOL +CompressedInteger::Encode( + UINT32 nValue, + __out UINT32 *pnEncodedValue, + __out UINT32 *pcbEncodingSize) +{ + // Does it fit into 1-byte encoding? + if (nValue <= const_Max1Byte) + { // The value fits into 1 byte (binary format 0xxx xxxx) + *pnEncodedValue = nValue; + *pcbEncodingSize = 1; + return TRUE; + } + // Does it fit into 2-bytes encoding? + if (nValue <= const_Max2Bytes) + { // The value fits into 2 bytes (binary format 10xx xxxx yyyy yyyy) + *pnEncodedValue = 0x8000 | nValue; + *pcbEncodingSize = 2; + return TRUE; + } + // Does it fit into 4-bytes encoding? + if (nValue <= const_Max4Bytes) + { // The value fits into 4 bytes (binary format 110x xxxx yyyy yyyy zzzz zzzz wwww wwww) + *pnEncodedValue = 0xC0000000 | nValue; + *pcbEncodingSize = 4; + return TRUE; + } + // The value cannot be encoded as compressed integer + return FALSE; +} // CompressedInteger::Encode + +}; // namespace MetaData diff --git a/src/coreclr/md/datablob.h b/src/coreclr/md/datablob.h new file mode 100644 index 00000000000..ed8c949f268 --- /dev/null +++ b/src/coreclr/md/datablob.h @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: DataBlob.h +// + +// +// Class code:MetaData::DataBlob provides secure access to a block of memory from MetaData (i.e. with fixed +// endianess). +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class provides secure access to a block of memory. +// +class DataBlob +{ +private: + // + // Private data + // + + // The memory block of size code:m_cbSize. Can be non-NULL even if code:m_cbSize is 0. + __field_bcount(m_cbSize) + BYTE *m_pbData; + // Size of the memory block starting at code:m_pbData. If it is 0, then value of code:m_pbData can be + // anything (incl. NULL). + UINT32 m_cbSize; + +public: + // + // Initialization + // + + // Creates empty memory block. + inline DataBlob(); + // Creates memory block (pbData, of size cbSize). + inline DataBlob( + __in_bcount_opt(cbSize) BYTE *pbData, + UINT32 cbSize); + // Creates memory block copy. + inline DataBlob( + const DataBlob &source); + // Initializes memory block to empty data. The object could be already initialzied. + inline void Clear(); + // Initializes memory block to data (pbData, of size cbSize). The object should be empty before. + inline void Init( + __in_bcount_opt(cbSize) BYTE *pbData, + UINT32 cbSize); + + // + // Getters + // + + //#PeekUx_Functions + // Reads the U1/U2/U4/U8 from the data blob without skipping the read data. + // Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then. + // Returns TRUE otherwise, fills *pnValue, but doesn't move the memory block (doesn't skip the read + // data). + __checkReturn __success(return) inline BOOL PeekU1(__out BYTE *pnValue) const; + __checkReturn __success(return) inline BOOL PeekU2(__out UINT16 *pnValue) const; + __checkReturn __success(return) inline BOOL PeekU4(__out UINT32 *pnValue) const; + __checkReturn __success(return) inline BOOL PeekU8(__out UINT64 *pnValue) const; + + //#GetUx_Functions + // Reads the U1/U2/U4/U8 from the data blob and skips the read data. + // Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then. + // Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data. + __checkReturn __success(return) inline BOOL GetU1(__out BYTE *pnValue); + __checkReturn __success(return) inline BOOL GetU2(__out UINT16 *pnValue); + __checkReturn __success(return) inline BOOL GetU4(__out UINT32 *pnValue); + __checkReturn __success(return) inline BOOL GetU8(__out UINT64 *pnValue); + + // Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size + // in *pcbCompressedValueSize) from the data blob without skipping the read data. + // Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte + // 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value + // *pcbCompressedValueSize then. + // Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4), but + // doesn't move the memory block (doesn't skip the read data). + __checkReturn + __success(return) + inline BOOL PeekCompressedU( + __out UINT32 *pnValue, + __out UINT32 *pcbCompressedValueSize); + // Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) from the data blob + // and skips the read data. + // Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte + // 111? ????), doesn't initialize the value *pnValue then. + // Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data. + __checkReturn + __success(return) + inline BOOL GetCompressedU(__out UINT32 *pnValue); + // Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size + // in *pcbCompressedValueSize) from the data blob and skips the read data. + // Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte + // 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value + // *pcbCompressedValueSize then. + // Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4) and moves + // the memory block behind the read data. + __checkReturn + __success(return) + inline BOOL GetCompressedU( + __out UINT32 *pnValue, + __out UINT32 *pcbCompressedValueSize); + + // Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns the data as + // *pData). + // Returns FALSE if there's not enough data in the blob, clears *pData then. + // Returns TRUE otherwise, fills *pData with the "read" data and moves the memory block behind the + // "read" data. + __checkReturn + __success(return) + inline BOOL GetDataOfSize( + UINT32 cbDataSize, + __out DataBlob *pData); + + // Checks if there's at least cbDataSize bytes in the represented memory block. + // Returns TRUE if there's >= cbDataSize bytes. Returns FALSE otherwise. + inline BOOL ContainsData(UINT32 cbDataSize) const + { return cbDataSize <= m_cbSize; } +/* + // Checks if there's at least cbDataSize1 + cbDataSize2 bytes in the represented memory block (and that + // the sum doesn't overflow). + // Returns TRUE if there's >= cbDataSize1 + cbDataSize2 bytes. + // Returns FALSE otherwise and if cbDataSize1 + cbDataSize2 overflows. + inline BOOL ContainsData_2Parts( + UINT32 cbDataSize1, + UINT32 cbDataSize2) const; + // Checks if there's valid compressed integer (1, 2 or 4 bytes of format + // code:DataBlob#CompressedIntegerFormat) in the data blob. + // Returns: + // * 0 ... if there's valid compressed integer. + // * 1 ... if there's not enough data in the data blob, but the encoding is correct. + // * 2 ... if the integer encoding is invalid (starts with byte 111x xxxx byte), but there are at least + // 4 bytes in the data blob left. + // * 3 ... if there's not enough data in the data blob and the integer encoding is invalid (starts with + // 111x xxx byte). + inline int ValidateCompressedU() const; +*/ + + // Returns TRUE if the represented memory is empty. + inline BOOL IsEmpty() const + { return (m_cbSize == 0); } + // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0). + // Note: Should be used exceptionally. Try to use other operations instead. + inline BYTE *GetDataPointer() + { return m_pbData; } + // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0). + // Note: Should be used exceptionally. Try to use other operations instead. + inline const BYTE *GetDataPointer() const + { return m_pbData; } + // Gets pointer right behind the represented data buffer (can be random pointer if size of the data is + // 0). + inline const BYTE *GetDataPointerBehind() const + { return ((m_cbSize == 0) ? NULL : (m_pbData + m_cbSize)); } + // Gets the size of represented memory. + inline UINT32 GetSize() const + { return m_cbSize; } + //BOOL SkipBytes(UINT32 cbSize); + +public: + // + // Operations + // + + // Truncates the buffer to exact size (cbSize). + // Returns FALSE if there's less than cbSize data represented. + // Returns TRUE otherwise and truncates the represented data size to cbSize. + __checkReturn + __success(return) + inline BOOL TruncateToExactSize(UINT32 cbSize); + // Truncates the buffer by size (cbSize). + // Returns FALSE if there's less than cbSize data represented. + // Returns TRUE otherwise and truncates the represented data size by cbSize. + __checkReturn + __success(return) + inline BOOL TruncateBySize(UINT32 cbSize); + +#ifdef _DEBUG + // Returns U1 value at offset (nOffset). Fires an assert if the offset is behind the end of represented + // data. + inline BYTE Debug_GetByteAtOffset(UINT32 nOffset) const; +#endif //_DEBUG + +public: + // + // Setters + // + + // Writes compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) to the data blob + // and skips the written data. + // Returns FALSE if there's not enough data in the blob or the value cannot be encoded as compressed + // integer (bigger than code:CompressedInteger::const_Max). + // Returns TRUE on success and moves the memory block behind the written data. + __checkReturn + __success(return) + inline BOOL StoreCompressedU(UINT32 nValue); + + // Writes data from *pSource to the data blob and skips the written data. + // Returns FALSE if there's not enough data in the blob. + // Returns TRUE on success and moves memory block behind the written data. + __checkReturn + __success(return) + inline BOOL StoreData(__in const DataBlob *pSource); + +private: + // + // Helpers + // + + // Skips cbSize bytes in the represented memory block. The caller is responsible for making sure that + // the represented memory block contains at least cbSize bytes, otherwise there will be a security + // issue. + // Should be used only internally, never call it from outside of this class. + inline void SkipBytes_InternalInsecure(UINT32 cbSize); + +}; // class DataBlob + +}; // namespace MetaData + +#include "datablob.inl" diff --git a/src/coreclr/md/datablob.inl b/src/coreclr/md/datablob.inl new file mode 100644 index 00000000000..5f55af6cd5f --- /dev/null +++ b/src/coreclr/md/datablob.inl @@ -0,0 +1,580 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: DataBlob.inl +// + +// +// Class code:MetaData::DataBlob provides secure access to a block of memory from MetaData (i.e. with fixed +// endianess). +// +// ====================================================================================== + +#pragma once + +#include "datablob.h" +#include "compressedinteger.h" + +#include "debug_metadata.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Creates empty memory block. +// +inline +DataBlob::DataBlob() +{ + Clear(); +} // DataBlob::DataBlob + +// -------------------------------------------------------------------------------------- +// +// Creates memory block (pbData, of size cbSize). +// +inline +DataBlob::DataBlob( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize) +{ + m_pbData = pbData; + m_cbSize = cbSize; +} // DataBlob::DataBlob + +// -------------------------------------------------------------------------------------- +// +// Creates memory block copy. +// +inline +DataBlob::DataBlob( + const DataBlob &source) +{ + m_pbData = source.m_pbData; + m_cbSize = source.m_cbSize; +} // DataBlob::DataBlob + +#ifdef HOST_64BIT + #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00dbaadf00d) +#else //!HOST_64BIT + #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00d) +#endif //!HOST_64BIT + +// -------------------------------------------------------------------------------------- +// +// Initializes memory block to empty data. The object could be already initialzied. +// +inline +void +DataBlob::Clear() +{ + m_cbSize = 0; + // For debugging purposes let's put invalid non-NULL pointer here + INDEBUG_MD(m_pbData = const_pbBadFood); +} // DataBlob::Clear + +#undef const_pbBadFood + +// -------------------------------------------------------------------------------------- +// +// Initializes memory block to data (pbData, of size cbSize). The object should be empty before. +// +inline +void +DataBlob::Init( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize) +{ + m_pbData = pbData; + m_cbSize = cbSize; +} // DataBlob::Init + +// -------------------------------------------------------------------------------------- +// +// #PeekUx_Functions +// +// Reads the U1/U2/U4/U8 from the data blob without skipping the read data. +// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then. +// Returns TRUE otherwise, fills *pnValue, but doesn't move the memory block (doesn't skip the read data). +// + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU1(__out BYTE *pnValue) const +{ + if (m_cbSize < sizeof(BYTE)) + { + return FALSE; + } + *pnValue = *m_pbData; + return TRUE; +} // DataBlob::PeekU1 + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU2(__out UINT16 *pnValue) const +{ + if (m_cbSize < sizeof(UINT16)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL16(m_pbData); + return TRUE; +} // DataBlob::PeekU2 + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU4(__out UINT32 *pnValue) const +{ + if (m_cbSize < sizeof(UINT32)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL32(m_pbData); + return TRUE; +} // DataBlob::PeekU4 + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU8(__out UINT64 *pnValue) const +{ + if (m_cbSize < sizeof(UINT64)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL64(m_pbData); + return TRUE; +} // DataBlob::PeekU8 + +// -------------------------------------------------------------------------------------- +// +// #GetUx_Functions +// +// Reads the U1/U2/U4/U8 from the data blob and skips the read data. +// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then. +// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data. +// + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU1(__out BYTE *pnValue) +{ + if (m_cbSize < sizeof(BYTE)) + { + return FALSE; + } + *pnValue = *m_pbData; + SkipBytes_InternalInsecure(sizeof(BYTE)); + return TRUE; +} // DataBlob::GetU1 + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU2(__out UINT16 *pnValue) +{ + if (m_cbSize < sizeof(UINT16)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL16(m_pbData); + SkipBytes_InternalInsecure(sizeof(UINT16)); + return TRUE; +} // DataBlob::GetU2 + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU4(__out UINT32 *pnValue) +{ + if (m_cbSize < sizeof(UINT32)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL32(m_pbData); + SkipBytes_InternalInsecure(sizeof(UINT32)); + return TRUE; +} // DataBlob::GetU4 + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU8(__out UINT64 *pnValue) +{ + if (m_cbSize < sizeof(UINT64)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL64(m_pbData); + SkipBytes_InternalInsecure(sizeof(UINT64)); + return TRUE; +} // DataBlob::GetU8 + +// -------------------------------------------------------------------------------------- +// +// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) from the data blob +// and skips the read data. +// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte +// 111? ????), doesn't initialize the value *pnValue then. +// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data. +// +__checkReturn +inline +BOOL +DataBlob::GetCompressedU(__out UINT32 *pnValue) +{ + UINT32 cbCompressedValueSize_Ignore; + return GetCompressedU(pnValue, &cbCompressedValueSize_Ignore); +} // DataBlob::GetCompressedU + +// -------------------------------------------------------------------------------------- +// +// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size +// in *pcbCompressedValueSize) from the data blob without skipping the read data. +// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte +// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value +// *pcbCompressedValueSize then. +// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4), but +// doesn't move the memory block (doesn't skip the read data). +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekCompressedU( + __out UINT32 *pnValue, + __out UINT32 *pcbCompressedValueSize) +{ + // This algorithm has to be in sync with code:CompressedInteger#Format encoding definition. + // + // Note that this algorithm accepts technically invalid encodings, e.g. + // encoding of value 0 is accepted as 0000 0000 (0x00, valid) and 1000 0000 0000 000 (0x8000, invalid). + + // Is there at least 1 byte? + if (m_cbSize < 1) + { // The data blob is empty, there's not compressed integer stored + return FALSE; + } + if ((*m_pbData & 0x80) == 0x00) + { // 0??? ???? + // The value is compressed into 1 byte + *pnValue = (UINT32)(*m_pbData); + *pcbCompressedValueSize = 1; + return TRUE; + } + // 1??? ???? + + if ((*m_pbData & 0x40) == 0x00) + { // 10?? ???? + // The value is compressed into 2 bytes + if (m_cbSize < 2) + { // The data blob is too short and doesn't contain 2 bytes needed for storing compressed integer + return FALSE; + } + *pnValue = + ((*m_pbData & 0x3f) << 8) | + *(m_pbData + 1); + *pcbCompressedValueSize = 2; + return TRUE; + } + // 11?? ???? + + if ((*m_pbData & 0x20) == 0x00) + { // 110? ???? + // The value is compressed into 4 bytes + if (m_cbSize < 4) + { // The data blob is too short and doesn't contain 4 bytes needed for storing compressed integer + return FALSE; + } + *pnValue = + ((*m_pbData & 0x1f) << 24) | + (*(m_pbData + 1) << 16) | + (*(m_pbData + 2) << 8) | + *(m_pbData + 3); + *pcbCompressedValueSize = 4; + return TRUE; + } + // 111? ???? + // Invalid encoding of the compressed integer + return FALSE; +} // DataBlob::PeekCompressedU + +// -------------------------------------------------------------------------------------- +// +// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size +// in *pcbCompressedValueSize) from the data blob and skips the read data. +// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte +// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value +// *pcbCompressedValueSize then. +// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4) and moves +// the memory block behind the read data. +// +__checkReturn +inline +BOOL +DataBlob::GetCompressedU( + __out UINT32 *pnValue, + __out UINT32 *pcbCompressedValueSize) +{ + // Read the compressed integer from withou skipping the read data + BOOL fReadResult = PeekCompressedU( + pnValue, + pcbCompressedValueSize); + // Was the compressed integer read? + if (fReadResult) + { // The compressed integer was read + // Skip the read data + SkipBytes_InternalInsecure(*pcbCompressedValueSize); + } + // Return the (original) read result + return fReadResult; +} // DataBlob::GetCompressedU + +// -------------------------------------------------------------------------------------- +// +// Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns the data as +// *pData). +// Returns FALSE if there's not enough data in the blob, clears *pData then. +// Returns TRUE otherwise, fills *pData with the "read" data and moves the memory block behind the +// "read" data. +// +__checkReturn +inline +BOOL +DataBlob::GetDataOfSize( + UINT32 cbDataSize, + __out DataBlob *pData) +{ + if (m_cbSize < cbDataSize) + { // There's not enough data in the memory block + pData->Clear(); + return FALSE; + } + // Fill the "read" data + pData->Init(m_pbData, cbDataSize); + SkipBytes_InternalInsecure(cbDataSize); + return TRUE; +} // DataBlob::GetDataOfSize + +/* +// -------------------------------------------------------------------------------------- +// +// Checks if there's at least cbDataSize1 + cbDataSize2 bytes in the represented memory block (and that +// the sum doesn't overflow). +// Returns TRUE if there's >= cbDataSize1 + cbDataSize2 bytes. +// Returns FALSE otherwise and if cbDataSize1 + cbDataSize2 overflows. +// +inline +BOOL +DataBlob::ContainsData_2Parts( + UINT32 cbDataSize1, + UINT32 cbDataSize2) const +{ + S_UINT32 cbDataSize = S_UINT32(cbDataSize1) + S_UITN32(cbDataSize2); + if (cbDataSize.IsOverflow()) + { + return FALSE; + } + return (cbDataSize.Value() <= m_cbSize); +} // DataBlob::ContainsData +*/ + +// -------------------------------------------------------------------------------------- +// +// Truncates the buffer to exact size (cbSize). +// Returns FALSE if there's less than cbSize data represented. +// Returns TRUE otherwise and truncates the represented data size to cbSize. +// +__checkReturn +inline +BOOL +DataBlob::TruncateToExactSize(UINT32 cbSize) +{ + // Check if there's at least cbSize data present + if (m_cbSize < cbSize) + { // There's less than cbSize data present + // Fail the operation + return FALSE; + } + // Truncate represented data to size cbSize + m_cbSize = cbSize; + return TRUE; +} // DataBlob::TruncateToExactSize + +// -------------------------------------------------------------------------------------- +// +// Truncates the buffer by size (cbSize). +// Returns FALSE if there's less than cbSize data represented. +// Returns TRUE otherwise and truncates the represented data size by cbSize. +// +__checkReturn +inline +BOOL +DataBlob::TruncateBySize(UINT32 cbSize) +{ + // Check if there's at least cbSize data present + if (m_cbSize < cbSize) + { // There's less than cbSize data present + // Fail the operation + return FALSE; + } + // Truncate represented data by size cbSize + m_cbSize -= cbSize; + return TRUE; +} // DataBlob::TruncateBySize + +#ifdef _DEBUG +// -------------------------------------------------------------------------------------- +// +// Returns U1 value at offset (nOffset). Fires an assert if the offset is behind the end of represented +// data. +// +inline +BYTE +DataBlob::Debug_GetByteAtOffset(UINT32 nOffset) const +{ + _ASSERTE(nOffset < m_cbSize); + return m_pbData[nOffset]; +} // DataBlob::Debug_GetByteAtOffset +#endif //_DEBUG + +// -------------------------------------------------------------------------------------- +// +// Writes compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) to the data blob +// and skips the written data. +// Returns FALSE if there's not enough data in the blob or the value cannot be encoded as compressed +// integer (bigger than code:CompressedInteger::const_Max). +// Returns TRUE on success and moves the memory block behind the written data. +// +__checkReturn +inline +BOOL +DataBlob::StoreCompressedU(UINT32 nValue) +{ + if (nValue <= CompressedInteger::const_Max1Byte) + { // The value fits into 1 byte + if (m_cbSize < 1) + { // The data blob is empty, we cannot store compressed integer as 1 byte + return FALSE; + } + *m_pbData = (BYTE)nValue; + SkipBytes_InternalInsecure(1); + return TRUE; + } + if (nValue <= CompressedInteger::const_Max2Bytes) + { // The value fits into 2 bytes + if (m_cbSize < 2) + { // The data blob is too short, we cannot store compressed integer as 2 bytes + return FALSE; + } + *m_pbData = (BYTE)(nValue >> 8) | 0x80; + *(m_pbData + 1) = (BYTE)(nValue & 0xff); + SkipBytes_InternalInsecure(2); + return TRUE; + } + if (nValue <= CompressedInteger::const_Max4Bytes) + { // The value fits into 4 bytes + if (m_cbSize < 4) + { // The data blob is too short, we cannot store compressed integer as 4 bytes + return FALSE; + } + *m_pbData = (BYTE)(nValue >> 24) | 0xC0; + *(m_pbData + 1) = (BYTE)((nValue >> 16) & 0xff); + *(m_pbData + 2) = (BYTE)((nValue >> 8) & 0xff); + *(m_pbData + 3) = (BYTE)(nValue & 0xff); + SkipBytes_InternalInsecure(4); + return TRUE; + } + // The value cannot be encoded as compressed integer + return FALSE; +} // DataBlob::StoreCompressedU + +// -------------------------------------------------------------------------------------- +// +// Writes data from *pSource to the data blob and skips the written data. +// Returns FALSE if there's not enough data in the blob. +// Returns TRUE on success and moves memory block behind the written data. +// +__checkReturn +inline +BOOL +DataBlob::StoreData(__in const DataBlob *pSource) +{ + // Check that we have enough space to store the *pSource data + if (m_cbSize < pSource->m_cbSize) + { // There's not enough space to store *pSource data + return FALSE; + } + // Copy the *pSource data to the data blob + memcpy(m_pbData, pSource->m_pbData, pSource->m_cbSize); + // Move the data blob behind copied/written data *pSource + m_pbData += pSource->m_cbSize; + m_cbSize -= pSource->m_cbSize; + + return TRUE; +} // DataBlob::StoreData + +// -------------------------------------------------------------------------------------- +// +// Skips cbSize bytes in the represented memory block. The caller is responsible for making sure that the +// represented memory block contains at least cbSize bytes, otherwise there will be a security issue. +// Should be used only internally, never call it from outside of this class. +// +inline +void +DataBlob::SkipBytes_InternalInsecure(UINT32 cbSize) +{ + // The caller is responsible for this check, just double check here + _ASSERTE(m_cbSize >= cbSize); + // Move the memory block by 'cbSize' bytes + m_pbData += cbSize; + m_cbSize -= cbSize; +} // DataBlob::SkipBytes_InternalInsecure + +}; // namespace MetaData diff --git a/src/coreclr/md/databuffer.h b/src/coreclr/md/databuffer.h new file mode 100644 index 00000000000..0aade1c1dfb --- /dev/null +++ b/src/coreclr/md/databuffer.h @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: DataBuffer.h +// + +// +// Class code:DataBuffer provides secure access to a block of memory. +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +// -------------------------------------------------------------------------------------- +// +// This class provides secure access to a block of memory. +// +class DataBuffer +{ +private: + // + // Private data + // + + // The memory block of size code:m_cbSize. Can be non-NULL even if code:m_cbSize is 0. + __field_bcount(m_cbSize) + BYTE *m_pbData; + // Size of the memory block starting at code:m_pbData. If it is 0, then value of code:m_pbData can be + // anything (incl. NULL). + UINT32 m_cbSize; + +public: + // + // Initialization + // + + // Creates empty memory block. + inline DataBuffer(); + // Creates memory block (pbData, of size cbSize). + inline DataBuffer( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize); + // Creates memory block copy. + inline DataBuffer( + const DataBuffer &source); + // Initializes memory block to empty data. The object could be already initialzied. + inline void Clear(); + // Initializes memory block to data (pbData, of size cbSize). The object should be empty before. + inline void Init( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize); + + // + // Getters + // + + // Reads data of type T without skipping the read data (returns pointer to the type in *ppTypeData). + // Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer + // *ppTypeData then. + // Returns TRUE otherwise, fills *ppTypeData with the "read" type start, but doesn't move the memory + // block (doesn't skip the "read" data). + template<class T> + __checkReturn + inline BOOL PeekData( + __deref_out T **ppTypeData); + // Reads data of type T at offset nOffset without skipping the read data (returns pointer to the type in + // *ppTypeData). + // Returns FALSE if there's not enough data (of size T) at offset nOffset in the buffer, doesn't + // initialize the pointer *ppTypeData then. + // Returns TRUE otherwise, fills *ppTypeData with the type start, but doesn't move the memory block + // (doesn't skip any "read" data). + template<class T> + __checkReturn + inline BOOL PeekDataAt( + UINT32 nOffset, + __deref_out T **ppTypeData); + // Reads data of type T and skips the data (instead of reading the bytes, returns pointer to the type in + // *ppTypeData). + // Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer + // *ppTypeData then. + // Returns TRUE otherwise, fills *ppTypeData with the "read" type start and moves the memory block + // behind the "read" type. + template<class T> + __checkReturn + inline BOOL GetData( + __deref_out T **ppTypeData); + // Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns pointer to + // the bytes in *ppbDataPointer). + // Returns FALSE if there's not enough data in the blob, doesn't initialize the pointer *ppbDataPointer + // then. + // Returns TRUE otherwise, fills *ppbDataPointer with the "read" data start and moves the memory block + // behind the "read" data. + __checkReturn + inline BOOL GetDataOfSize( + UINT32 cbDataSize, + __out_bcount(cbDataSize) BYTE **ppbDataPointer); + + // Returns TRUE if the represented memory is empty. + inline BOOL IsEmpty() const + { return (m_cbSize == 0); } + // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0). + // Note: Should be used exceptionally. Try to use other operations instead. + inline BYTE *GetDataPointer() + { return m_pbData; } + // Gets pointer to the represented data buffer (can be random pointer if size of the data is 0). + // Note: Should be used exceptionally. Try to use other operations instead. + inline const BYTE *GetDataPointer() const + { return m_pbData; } + // Gets pointer right behind the represented data buffer (can be random pointer if size of the data is + // 0). + inline const BYTE *GetDataPointerBehind() const + { return m_pbData + m_cbSize; } + // Gets the size of represented memory. + inline UINT32 GetSize() const + { return m_cbSize; } + //BOOL SkipBytes(UINT32 cbSize); + +public: + // + // Operations + // + + // Truncates the buffer to exact size (cbSize). + // Returns FALSE if there's less than cbSize data represented. + // Returns TRUE otherwise and truncates the represented data size to cbSize. + __checkReturn + inline BOOL TruncateToExactSize(UINT32 cbSize); + // Truncates the buffer by size (cbSize). + // Returns FALSE if there's less than cbSize data represented. + // Returns TRUE otherwise and truncates the represented data size by cbSize. + __checkReturn + inline BOOL TruncateBySize(UINT32 cbSize); + + // Skips the buffer to exact size (cbSize). + // Returns FALSE if there's less than cbSize data represented. + // Returns TRUE otherwise and skips data at the beggining, so that the result has size cbSize. + __checkReturn + inline BOOL SkipToExactSize(UINT32 cbSize); + +private: + // + // Helpers + // + + // Skips 'cbSize' bytes in the represented memory block. The caller is responsible for making sure that + // the represented memory block contains at least 'cbSize' bytes, otherwise there will be a security + // issue. + // Should be used only internally, never call it from outside of this class. + inline void SkipBytes_InternalInsecure(UINT32 cbSize); + +}; // class DataBuffer + +#include "databuffer.inl" diff --git a/src/coreclr/md/databuffer.inl b/src/coreclr/md/databuffer.inl new file mode 100644 index 00000000000..12d55c02f6b --- /dev/null +++ b/src/coreclr/md/databuffer.inl @@ -0,0 +1,273 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: DataBuffer.inl +// + +// +// Class code:DataBuffer provides secure access to a block of memory. +// +// ====================================================================================== + +#pragma once + +#include "databuffer.h" + +// -------------------------------------------------------------------------------------- +// +// Creates empty memory block. +// +inline +DataBuffer::DataBuffer() +{ + Clear(); +} // DataBuffer::DataBuffer + +// -------------------------------------------------------------------------------------- +// +// Creates memory block (pbData, of size cbSize). +// +inline +DataBuffer::DataBuffer( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize) +{ + m_pbData = pbData; + m_cbSize = cbSize; +} // DataBuffer::DataBuffer + +// -------------------------------------------------------------------------------------- +// +// Creates memory block copy. +// +inline +DataBuffer::DataBuffer( + const DataBuffer &source) +{ + m_pbData = source.m_pbData; + m_cbSize = source.m_cbSize; +} // DataBuffer::DataBuffer + +#ifdef HOST_64BIT + #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00dbaadf00d) +#else //!HOST_64BIT + #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00d) +#endif //!HOST_64BIT + +// -------------------------------------------------------------------------------------- +// +// Initializes memory block to empty data. The object could be already initialzied. +// +inline +void +DataBuffer::Clear() +{ + m_cbSize = 0; + // For debugging purposes let's put invalid non-NULL pointer here + INDEBUG_MD(m_pbData = const_pbBadFood); +} // DataBuffer::Clear + +#undef const_pbBadFood + +// -------------------------------------------------------------------------------------- +// +// Initializes memory block to data (pbData, of size cbSize). The object should be empty before. +// +inline +void +DataBuffer::Init( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize) +{ + _ASSERTE(IsEmpty()); + + m_pbData = pbData; + m_cbSize = cbSize; +} // DataBuffer::Init + +// -------------------------------------------------------------------------------------- +// +// Reads data of type T without skipping the read data (returns pointer to the type in *ppTypeData). +// Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer +// *ppTypeData then. +// Returns TRUE otherwise, fills *ppTypeData with the "read" type start, but doesn't move the memory +// block (doesn't skip the "read" data). +// +template<class T> +__checkReturn +inline +BOOL +DataBuffer::PeekData( + __deref_out T **ppTypeData) +{ + if (m_cbSize < sizeof(T)) + { // There's not enough data in the memory block + return FALSE; + } + // Fill the start of the "read" type + *ppTypeData = reinterpret_cast<T *>(m_pbData); + return TRUE; +} // DataBuffer::PeekData + +// -------------------------------------------------------------------------------------- +// +// Reads data of type T at offset nOffset without skipping the read data (returns pointer to the type in +// *ppTypeData). +// Returns FALSE if there's not enough data (of size T) at offset nOffset in the buffer, doesn't +// initialize the pointer *ppTypeData then. +// Returns TRUE otherwise, fills *ppTypeData with the type start, but doesn't move the memory block +// (doesn't skip any "read" data). +template<class T> +__checkReturn +inline +BOOL +DataBuffer::PeekDataAt( + UINT32 nOffset, + __deref_out T **ppTypeData) +{ + if (m_cbSize < nOffset) + { // The offset is not in the memory block + return FALSE; + } + if ((m_cbSize - nOffset) < sizeof(T)) + { // The type is not fully in the memory block + return FALSE; + } + // Fill the start of the "read" type + *ppTypeData = reinterpret_cast<T *>(m_pbData + nOffset); + return TRUE; +} // DataBuffer::PeekDataAt + +// -------------------------------------------------------------------------------------- +// +// Reads data of type T and skips the data (instead of reading the bytes, returns pointer to the type in +// *ppTypeData). +// Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer +// *ppTypeData then. +// Returns TRUE otherwise, fills *ppTypeData with the "read" type start and moves the memory block +// behind the "read" type. +// +template<class T> +__checkReturn +inline +BOOL +DataBuffer::GetData( + __deref_out T **ppTypeData) +{ + if (m_cbSize < sizeof(T)) + { // There's not enough data in the memory block + return FALSE; + } + // Fill the start of the "read" type + *ppTypeData = reinterpret_cast<T *>(m_pbData); + SkipBytes_InternalInsecure(sizeof(T)); + return TRUE; +} // DataBuffer::GetData + +// -------------------------------------------------------------------------------------- +// +// Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns pointer to +// the bytes in *ppbDataPointer). +// Returns FALSE if there's not enough data in the blob, doesn't initialize the pointer *ppbDataPointer +// then. +// Returns TRUE otherwise, fills *ppbDataPointer with the "read" data start and moves the memory block +// behind the "read" data. +// +__checkReturn +inline +BOOL +DataBuffer::GetDataOfSize( + UINT32 cbDataSize, + __out_bcount(cbDataSize) BYTE **ppbDataPointer) +{ + if (m_cbSize < cbDataSize) + { // There's not enough data in the memory block + return FALSE; + } + // Fill the start of the "read" data + *ppbDataPointer = m_pbData; + SkipBytes_InternalInsecure(cbDataSize); + return TRUE; +} // DataBuffer::GetDataOfSize + +// -------------------------------------------------------------------------------------- +// +// Truncates the buffer to exact size (cbSize). +// Returns FALSE if there's less than cbSize data represented. +// Returns TRUE otherwise and truncates the represented data size to cbSize. +// +__checkReturn +inline +BOOL +DataBuffer::TruncateToExactSize(UINT32 cbSize) +{ + // Check if there's at least cbSize data present + if (m_cbSize < cbSize) + { // There's less than cbSize data present + // Fail the operation + return FALSE; + } + // Truncate represented data to size cbSize + m_cbSize = cbSize; + return TRUE; +} // DataBuffer::TruncateToExactSize + +// -------------------------------------------------------------------------------------- +// +// Truncates the buffer by size (cbSize). +// Returns FALSE if there's less than cbSize data represented. +// Returns TRUE otherwise and truncates the represented data size by cbSize. +// +__checkReturn +inline +BOOL +DataBuffer::TruncateBySize(UINT32 cbSize) +{ + // Check if there's at least cbSize data present + if (m_cbSize < cbSize) + { // There's less than cbSize data present + // Fail the operation + return FALSE; + } + // Truncate represented data by size cbSize + m_cbSize -= cbSize; + return TRUE; +} // DataBuffer::TruncateBySize + +// -------------------------------------------------------------------------------------- +// +// Skips the buffer to size (cbSize). +// Returns FALSE if there's less than cbSize data represented. +// Returns TRUE otherwise and skips data at the beggining, so that the result has size cbSize. +// +__checkReturn +inline +BOOL +DataBuffer::SkipToExactSize(UINT32 cbSize) +{ + // Check if there's at least cbSize data present + if (m_cbSize < cbSize) + { // There's less than cbSize data present + // Fail the operation + return FALSE; + } + SkipBytes_InternalInsecure(m_cbSize - cbSize); + return TRUE; +} // DataBuffer::SkipToExactSize + +// -------------------------------------------------------------------------------------- +// +// Skips 'cbSize' bytes in the represented memory block. The caller is responsible for making sure that the +// represented memory block contains at least 'cbSize' bytes, otherwise there will be a security issue. +// Should be used only internally, never call it from outside of this class. +// +inline +void +DataBuffer::SkipBytes_InternalInsecure(UINT32 cbSize) +{ + // The caller is responsible for this check, just double check here + _ASSERTE(m_cbSize >= cbSize); + // Move the memory block by 'cbSize' bytes + m_pbData += cbSize; + m_cbSize -= cbSize; +} // DataBuffer::SkipBytes_InternalInsecure diff --git a/src/coreclr/md/datasource/CMakeLists.txt b/src/coreclr/md/datasource/CMakeLists.txt new file mode 100644 index 00000000000..75bdec0bc1b --- /dev/null +++ b/src/coreclr/md/datasource/CMakeLists.txt @@ -0,0 +1,25 @@ +set(MDDATASOURCE_SOURCES + api.cpp + datatargetreader.cpp + remotemdinternalrwsource.cpp + targettypes.cpp +) + +set(MDDATASOURCE_HEADERS + ../../inc/cor.h + ../../inc/corpriv.h + datatargetreader.h + remotemdinternalrwsource.h + targettypes.h +) + +convert_to_absolute_path(MDDATASOURCE_SOURCES ${MDDATASOURCE_SOURCES}) +convert_to_absolute_path(MDDATASOURCE_HEADERS ${MDDATASOURCE_HEADERS}) + +if (CLR_CMAKE_TARGET_WIN32) + list(APPEND MDDATASOURCE_SOURCES ${MDDATASOURCE_HEADERS}) +endif (CLR_CMAKE_TARGET_WIN32) + +add_library_clr(mddatasource_dbi STATIC ${MDDATASOURCE_SOURCES}) +set_target_properties(mddatasource_dbi PROPERTIES DBI_COMPONENT TRUE) +target_precompile_headers(mddatasource_dbi PRIVATE stdafx.h) diff --git a/src/coreclr/md/datasource/api.cpp b/src/coreclr/md/datasource/api.cpp new file mode 100644 index 00000000000..9d062d5f6b0 --- /dev/null +++ b/src/coreclr/md/datasource/api.cpp @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// api.cpp +// + +// +//***************************************************************************** + +#include "stdafx.h" +#include "remotemdinternalrwsource.h" + +HRESULT CreateRemoteMDInternalRWSource(TADDR mdInternalRWRemoteAddress, ICorDebugDataTarget* pDataTarget, DWORD defines, DWORD dataStructureVersion, IMDCustomDataSource** ppDataSource) +{ + HRESULT hr = S_OK; + RemoteMDInternalRWSource* pSource = new (nothrow) RemoteMDInternalRWSource(); + if (pSource == NULL) + return E_OUTOFMEMORY; + + hr = pSource->InitFromTarget(mdInternalRWRemoteAddress, pDataTarget, defines, dataStructureVersion); + if (SUCCEEDED(hr)) + { + hr = pSource->QueryInterface(IID_IMDCustomDataSource, (void**)ppDataSource); + } + if (FAILED(hr)) + { + delete pSource; + } + return hr; +} diff --git a/src/coreclr/md/datasource/datatargetreader.cpp b/src/coreclr/md/datasource/datatargetreader.cpp new file mode 100644 index 00000000000..0a14829b780 --- /dev/null +++ b/src/coreclr/md/datasource/datatargetreader.cpp @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#include "stdafx.h" +#include "datatargetreader.h" + + +DataTargetReader::DataTargetReader(CORDB_ADDRESS remoteAddressCursor, ICorDebugDataTarget* pDataTarget, DWORD targetDefines, DWORD mdStructuresVersion) +: m_remotePointerSize(0), +m_currentStructureAlign(1), +m_targetDefines(targetDefines), +m_mdStructuresVersion(mdStructuresVersion) +{ + m_remoteAddressCursor = remoteAddressCursor; + m_pDataTarget = pDataTarget; + m_pDataTarget->AddRef(); +} +DataTargetReader::DataTargetReader(const DataTargetReader & otherReader) +{ + m_pDataTarget = otherReader.m_pDataTarget; + m_pDataTarget->AddRef(); + m_remotePointerSize = otherReader.m_remotePointerSize; + m_remoteAddressCursor = otherReader.m_remoteAddressCursor; + m_targetDefines = otherReader.m_targetDefines; + m_mdStructuresVersion = otherReader.m_mdStructuresVersion; +} +DataTargetReader & DataTargetReader::operator=(const DataTargetReader & otherReader) +{ + if (this != &otherReader) + { + m_pDataTarget = otherReader.m_pDataTarget; + m_pDataTarget->AddRef(); + m_remotePointerSize = otherReader.m_remotePointerSize; + m_remoteAddressCursor = otherReader.m_remoteAddressCursor; + m_targetDefines = otherReader.m_targetDefines; + m_mdStructuresVersion = otherReader.m_mdStructuresVersion; + } + return *this; +} +DataTargetReader::~DataTargetReader() +{ + m_pDataTarget->Release(); + m_pDataTarget = NULL; +} + +HRESULT DataTargetReader::ReadPointer(CORDB_ADDRESS* pPointerValue) +{ + HRESULT hr = S_OK; + if (m_remotePointerSize == 0) + { + IfFailRet(GetRemotePointerSize(&m_remotePointerSize)); + } + _ASSERTE(m_remotePointerSize == 4 || m_remotePointerSize == 8); + *pPointerValue = 0; + if (m_remotePointerSize == 4) + return Read32((ULONG32*)pPointerValue); + else + return Read64((ULONG64*)pPointerValue); +} +HRESULT DataTargetReader::SkipPointer() +{ + HRESULT hr = S_OK; + if (m_remotePointerSize == 0) + { + IfFailRet(GetRemotePointerSize(&m_remotePointerSize)); + } + _ASSERTE(m_remotePointerSize == 4 || m_remotePointerSize == 8); + Align(m_remotePointerSize); + return SkipBytes(m_remotePointerSize); +} +HRESULT DataTargetReader::Read8(BYTE* pByteValue) +{ + return ReadBytes(pByteValue, 1); +} +HRESULT DataTargetReader::Skip8() +{ + return SkipBytes(1); +} +HRESULT DataTargetReader::Read32(ULONG32* pUlong32Value) +{ + Align(4); + return ReadBytes((BYTE*)pUlong32Value, sizeof(ULONG32)); +} +HRESULT DataTargetReader::Skip32() +{ + Align(4); + return SkipBytes(sizeof(ULONG32)); +} +HRESULT DataTargetReader::Read64(ULONG64* pUlong64Value) +{ + Align(8); + return ReadBytes((BYTE*)pUlong64Value, sizeof(ULONG64)); +} +HRESULT DataTargetReader::Skip64() +{ + Align(8); + return SkipBytes(sizeof(ULONG64)); +} + +HRESULT DataTargetReader::ReadBytes(BYTE* pBuffer, DWORD cbBuffer) +{ + HRESULT hr = S_OK; + ULONG32 cbTotalRead = 0; + CORDB_ADDRESS m_tempRemoteAddressCursor = m_remoteAddressCursor; + while (cbTotalRead < cbBuffer) + { + ULONG32 cbRead = 0; + if(FAILED(m_pDataTarget->ReadVirtual(m_remoteAddressCursor + cbTotalRead, + pBuffer + cbTotalRead, + cbBuffer - cbTotalRead, + &cbRead))) + return CORDBG_E_READVIRTUAL_FAILURE; + if (cbRead == 0) + return CORDBG_E_READVIRTUAL_FAILURE; + cbTotalRead += cbRead; + } + + // on success only, move the cursor + m_remoteAddressCursor += cbTotalRead; + return S_OK; +} +HRESULT DataTargetReader::SkipBytes(DWORD cbRead) +{ + m_remoteAddressCursor += cbRead; + return S_OK; +} + +#ifndef MAX +#define MAX(a,b) ((a)>(b) ? (a) : (b)) +#endif + +HRESULT DataTargetReader::Read(TargetObject* pTargetObject) +{ + ULONG32 previousAlign = m_currentStructureAlign; + m_currentStructureAlign = 1; + HRESULT hr = pTargetObject->ReadFrom(*this); + if (SUCCEEDED(hr)) + { + // increase the structure size to a multiple of the maximum alignment of any of its members + Align(m_currentStructureAlign); + } + m_currentStructureAlign = MAX(previousAlign, m_currentStructureAlign); + return hr; +} + +HRESULT DataTargetReader::ReadPointer(TargetObject* pTargetObject) +{ + HRESULT hr = S_OK; + CORDB_ADDRESS pointerValue; + IfFailRet(ReadPointer(&pointerValue)); + + DataTargetReader reader = CreateReaderAt(pointerValue); + return pTargetObject->ReadFrom(reader); +} + +void DataTargetReader::Align(DWORD alignmentBytes) +{ + m_remoteAddressCursor = AlignUp(m_remoteAddressCursor, alignmentBytes); + m_currentStructureAlign = MAX(m_currentStructureAlign, alignmentBytes); +} + +void DataTargetReader::AlignBase() +{ + // Align structs based on the largest field size + // This is the default for MSVC compilers + // This is forced on other platforms by the DAC_ALIGNAS macro + Align(m_currentStructureAlign); +} + +HRESULT DataTargetReader::GetRemotePointerSize(ULONG32* pPointerSize) +{ + HRESULT hr = S_OK; + CorDebugPlatform platform; + IfFailRet(m_pDataTarget->GetPlatform(&platform)); + if ((platform == CORDB_PLATFORM_WINDOWS_X86) || (platform == CORDB_PLATFORM_POSIX_X86) || (platform == CORDB_PLATFORM_MAC_X86)) + *pPointerSize = 4; + else if ((platform == CORDB_PLATFORM_WINDOWS_AMD64) || (platform == CORDB_PLATFORM_POSIX_AMD64) || (platform == CORDB_PLATFORM_MAC_AMD64)) + *pPointerSize = 8; + else if ((platform == CORDB_PLATFORM_WINDOWS_ARM) || (platform == CORDB_PLATFORM_POSIX_ARM)) + *pPointerSize = 4; + else if ((platform == CORDB_PLATFORM_WINDOWS_ARM64) || (platform == CORDB_PLATFORM_POSIX_ARM64)) + *pPointerSize = 8; + else + return CORDBG_E_UNSUPPORTED; + return S_OK; +} + +DWORD DataTargetReader::GetMDStructuresVersion() +{ + return m_mdStructuresVersion; +} + +BOOL DataTargetReader::IsDefined(DWORD define) +{ + return (m_targetDefines & define) == define; +} + +DataTargetReader DataTargetReader::CreateReaderAt(CORDB_ADDRESS remoteAddressCursor) +{ + DataTargetReader newReader(remoteAddressCursor, m_pDataTarget, m_targetDefines, m_mdStructuresVersion); + return newReader; +} diff --git a/src/coreclr/md/datasource/datatargetreader.h b/src/coreclr/md/datasource/datatargetreader.h new file mode 100644 index 00000000000..e1d0ee5d8ad --- /dev/null +++ b/src/coreclr/md/datasource/datatargetreader.h @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _MD_DATA_TARGET_READER_ +#define _MD_DATA_TARGET_READER_ + +#include "cor.h" +#include "cordebug.h" + +class DataTargetReader; + +class TargetObject +{ +public: + virtual HRESULT ReadFrom(DataTargetReader & reader) = 0; +}; + +class DataTargetReader +{ +public: + DataTargetReader(CORDB_ADDRESS remoteAddressCursor, ICorDebugDataTarget* pDataTarget, DWORD targetDefines, DWORD mdStructuresVersion); + DataTargetReader(const DataTargetReader & otherReader); + DataTargetReader & operator=(const DataTargetReader & rhs); + ~DataTargetReader(); + + HRESULT Read(TargetObject* pTargetObjectValue); + HRESULT ReadPointer(TargetObject* pTargetObjectValue); + HRESULT ReadPointer(CORDB_ADDRESS* pPointerValue); + HRESULT SkipPointer(); + HRESULT Read8(BYTE* pByteValue); + HRESULT Skip8(); + HRESULT Read32(ULONG32* pUlong32Value); + HRESULT Skip32(); + HRESULT Read64(ULONG64* pUlong64Value); + HRESULT Skip64(); + HRESULT ReadBytes(BYTE* pBuffer, DWORD cbBuffer); + HRESULT SkipBytes(DWORD cbBuffer); + void Align(DWORD alignmentBytes); + void AlignBase(); + + DataTargetReader CreateReaderAt(CORDB_ADDRESS remoteAddressCursor); + + DWORD GetMDStructuresVersion(); + BOOL IsDefined(DWORD define); + + +private: + HRESULT GetRemotePointerSize(ULONG32* pPointerSize); + ICorDebugDataTarget* m_pDataTarget; + ULONG32 m_remotePointerSize; + CORDB_ADDRESS m_remoteAddressCursor; + ULONG32 m_currentStructureAlign; + DWORD m_targetDefines; + DWORD m_mdStructuresVersion; +}; + +#endif // _MD_DATA_TARGET_READER_ diff --git a/src/coreclr/md/datasource/remotemdinternalrwsource.cpp b/src/coreclr/md/datasource/remotemdinternalrwsource.cpp new file mode 100644 index 00000000000..7effa8a67ff --- /dev/null +++ b/src/coreclr/md/datasource/remotemdinternalrwsource.cpp @@ -0,0 +1,240 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RemoteMDInternalRWSource.cpp +// + +// +//***************************************************************************** + +#include "stdafx.h" +#include "remotemdinternalrwsource.h" + +RemoteMDInternalRWSource::RemoteMDInternalRWSource() : +m_cRef(0) +{ + memset(&m_TableDefs, 0, sizeof(CMiniTableDef)*TBL_COUNT); + memset(&m_bSortable, 0, sizeof(BOOL)*TBL_COUNT); +} + +RemoteMDInternalRWSource::~RemoteMDInternalRWSource() +{ + for (int i = 0; i < TBL_COUNT; i++) + { + delete[] m_TableDefs[i].m_pColDefs; + } +} + +ULONG RemoteMDInternalRWSource::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +ULONG RemoteMDInternalRWSource::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + delete this; + return cRef; +} + +HRESULT RemoteMDInternalRWSource::QueryInterface(REFIID riid, void ** ppUnk) +{ + *ppUnk = 0; + if (riid == IID_IUnknown) + { + *ppUnk = static_cast<IUnknown*>(this); + } + else if (riid == IID_IMDCustomDataSource) + { + *ppUnk = static_cast<IMDCustomDataSource*>(this); + } + else + { + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +HRESULT _MarshalDataFromTargetStgPool(DataTargetReader & reader, const Target_StgPool & pool, MetaData::DataBlob* pBlob) +{ + HRESULT hr = S_OK; + ULONG32 dataSize = 0; + ULONG32 segmentCount = 0; + Target_StgPoolSeg curSeg = (Target_StgPoolSeg)pool; + + // The storage pool grows exponentially, and should reach 2GB in at most 64 segments. Allowing for 1000 + // adds a sizable risk mitigation factor in case my analysis was inaccurate or the algorithm changes in the future + // without corresponding changes here. + CORDB_ADDRESS segmentData[1000]; + ULONG32 segmentSize[1000]; + for (; segmentCount < 1000; segmentCount++) + { + // sanity check that each segment and the sum of all segments is less than 100M bytes + if (curSeg.m_cbSegNext > 100000000) + return CLDB_E_FILE_CORRUPT; + dataSize += curSeg.m_cbSegNext; + if (dataSize > 100000000) + return CLDB_E_FILE_CORRUPT; + segmentData[segmentCount] = curSeg.m_pSegData; + segmentSize[segmentCount] = curSeg.m_cbSegNext; + if (curSeg.m_pNextSeg == 0) + break; + + DataTargetReader segReader = reader.CreateReaderAt(curSeg.m_pNextSeg); + IfFailRet(segReader.Read(&curSeg)); + } + + //we exited the loop with a break, count should be one more than the last index + segmentCount++; + + // sanity check, no more than 1000 segments allowed + if (segmentCount > 1000) + return CLDB_E_FILE_CORRUPT; + + // things looked reasonable enough, marshal over the data + NewArrayHolder<BYTE> pData = new (nothrow) BYTE[dataSize]; + if (pData == NULL) + return E_OUTOFMEMORY; + BYTE* pCursor = pData; + for (ULONG32 i = 0; i < segmentCount; i++) + { + DataTargetReader segDataReader = reader.CreateReaderAt(segmentData[i]); + hr = segDataReader.ReadBytes(pCursor, segmentSize[i]); + if (FAILED(hr)) + { + return hr; + } + else + { + pCursor += segmentSize[i]; + } + } + pBlob->Init(pData, dataSize); + pData.SuppressRelease(); // our caller owns the buffer now + return S_OK; + +} + +HRESULT RemoteMDInternalRWSource::InitFromTarget(TADDR remoteMDInternalRWAddr, ICorDebugDataTarget* pDataTarget, DWORD defines, DWORD dataStructureVersion) +{ + HRESULT hr = S_OK; + DataTargetReader reader(remoteMDInternalRWAddr, pDataTarget, defines, dataStructureVersion); + IfFailRet(reader.Read(&m_targetData)); + + Target_CMiniMdSchema targetSchema = m_targetData.m_pStgdb.m_MiniMd.m_Schema; + m_Schema.m_ulReserved = targetSchema.m_ulReserved; + m_Schema.m_major = targetSchema.m_major; + m_Schema.m_minor = targetSchema.m_minor; + m_Schema.m_heaps = targetSchema.m_heaps; + m_Schema.m_rid = targetSchema.m_rid; + m_Schema.m_maskvalid = targetSchema.m_maskvalid; + m_Schema.m_sorted = targetSchema.m_sorted; + memcpy(m_Schema.m_cRecs, targetSchema.m_cRecs, sizeof(ULONG32)*TBL_COUNT); + m_Schema.m_ulExtra = targetSchema.m_ulExtra; + + for (int i = 0; i < TBL_COUNT; i++) + { + Target_CMiniTableDef* pTargetTableDef = &(m_targetData.m_pStgdb.m_MiniMd.m_TableDefs[i]); + m_TableDefs[i].m_cCols = pTargetTableDef->m_cCols; + m_TableDefs[i].m_iKey = pTargetTableDef->m_iKey; + m_TableDefs[i].m_cbRec = pTargetTableDef->m_cbRec; + m_TableDefs[i].m_pColDefs = new (nothrow) CMiniColDef[m_TableDefs[i].m_cCols]; + if (m_TableDefs[i].m_pColDefs == NULL) + return E_OUTOFMEMORY; + for (int j = 0; j < m_TableDefs[i].m_cCols; j++) + { + m_TableDefs[i].m_pColDefs[j].m_Type = pTargetTableDef->m_pColDefs[j].m_Type; + m_TableDefs[i].m_pColDefs[j].m_oColumn = pTargetTableDef->m_pColDefs[j].m_oColumn; + m_TableDefs[i].m_pColDefs[j].m_cbColumn = pTargetTableDef->m_pColDefs[j].m_cbColumn; + } + } + + IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_StringHeap, &m_StringHeap)); + m_StringHeapStorage = m_StringHeap.GetDataPointer(); + IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_BlobHeap, &m_BlobHeap)); + m_BlobHeapStorage = m_BlobHeap.GetDataPointer(); + IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_UserStringHeap, &m_UserStringHeap)); + m_UserStringHeapStorage = m_UserStringHeap.GetDataPointer(); + IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_GuidHeap, &m_GuidHeap)); + m_GuidHeapStorage = m_GuidHeap.GetDataPointer(); + + for (int i = 0; i < TBL_COUNT; i++) + { + IfFailRet(_MarshalDataFromTargetStgPool(reader, (Target_StgPool)m_targetData.m_pStgdb.m_MiniMd.m_Tables[i], &m_TableRecords[i])); + m_TableRecordsStorage[i] = m_TableRecords[i].GetDataPointer(); + m_bSortable[i] = m_targetData.m_pStgdb.m_MiniMd.m_bSortable[i]; + } + + if (m_targetData.m_pStgdb.m_pvMd != 0) + { + STORAGESIGNATURE sig = { 0 }; + DataTargetReader storageSigReader = reader.CreateReaderAt(m_targetData.m_pStgdb.m_pvMd); + storageSigReader.ReadBytes((BYTE*)&sig, sizeof(sig)); + if (sig.GetVersionStringLength() > 1000) + return CLDB_E_FILE_CORRUPT; + ULONG32 totalSigSize = offsetof(STORAGESIGNATURE, pVersion) + sig.GetVersionStringLength(); + m_SigStorage = new (nothrow)BYTE[totalSigSize]; + if (m_SigStorage == NULL) + return E_OUTOFMEMORY; + memcpy_s(m_SigStorage, totalSigSize, &sig, sizeof(sig)); + storageSigReader.ReadBytes(m_SigStorage + sizeof(sig), totalSigSize - sizeof(sig)); + m_Sig.Init(m_SigStorage, totalSigSize); + } + + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetSchema(CMiniMdSchema* pSchema) +{ + *pSchema = m_Schema; + return S_OK; +} +STDMETHODIMP RemoteMDInternalRWSource::GetTableDef(ULONG32 tableIndex, CMiniTableDef* pTableDef) +{ + *pTableDef = m_TableDefs[tableIndex]; + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetBlobHeap(MetaData::DataBlob* pBlobHeapData) +{ + *pBlobHeapData = m_BlobHeap; + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetGuidHeap(MetaData::DataBlob* pGuidHeapData) +{ + *pGuidHeapData = m_GuidHeap; + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetStringHeap(MetaData::DataBlob* pStringHeapData) +{ + *pStringHeapData = m_StringHeap; + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetUserStringHeap(MetaData::DataBlob* pUserStringHeapData) +{ + *pUserStringHeapData = m_UserStringHeap; + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetTableRecords(ULONG32 tableIndex, MetaData::DataBlob* pTableRecordData) +{ + *pTableRecordData = m_TableRecords[tableIndex]; + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetTableSortable(ULONG32 tableIndex, BOOL* pSortable) +{ + *pSortable = TRUE; + return S_OK; +} + +STDMETHODIMP RemoteMDInternalRWSource::GetStorageSignature(MetaData::DataBlob* pStorageSignature) +{ + *pStorageSignature = m_Sig; + return S_OK; +} diff --git a/src/coreclr/md/datasource/remotemdinternalrwsource.h b/src/coreclr/md/datasource/remotemdinternalrwsource.h new file mode 100644 index 00000000000..f2e98fc1ce9 --- /dev/null +++ b/src/coreclr/md/datasource/remotemdinternalrwsource.h @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RemoteMDInternalRWSource.h +// + +// +//***************************************************************************** + +#ifndef _REMOTE_MDINTERNALRW_SOURCE_ +#define _REMOTE_MDINTERNALRW_SOURCE_ + +#include "targettypes.h" + +class RemoteMDInternalRWSource : IMDCustomDataSource +{ +public: + RemoteMDInternalRWSource(); + virtual ~RemoteMDInternalRWSource(); + + //***************************************************************************** + // IUnknown methods + //***************************************************************************** + STDMETHODIMP QueryInterface(REFIID riid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + //***************************************************************************** + // IMDCustomDataSource methods + //***************************************************************************** + STDMETHODIMP GetSchema(CMiniMdSchema* pSchema); + STDMETHODIMP GetTableDef(ULONG32 tableIndex, CMiniTableDef* pTableDef); + STDMETHODIMP GetBlobHeap(MetaData::DataBlob* pBlobHeapData); + STDMETHODIMP GetGuidHeap(MetaData::DataBlob* pGuidHeapData); + STDMETHODIMP GetStringHeap(MetaData::DataBlob* pStringHeapData); + STDMETHODIMP GetUserStringHeap(MetaData::DataBlob* pUserStringHeapData); + STDMETHODIMP GetTableRecords(ULONG32 tableIndex, MetaData::DataBlob* pTableRecordData); + STDMETHODIMP GetTableSortable(ULONG32 tableIndex, BOOL* pSortable); + STDMETHODIMP GetStorageSignature(MetaData::DataBlob* pStorageSignature); + + //***************************************************************************** + // public non-COM methods + //***************************************************************************** + HRESULT InitFromTarget(TADDR remoteMDInternalRWAddress, ICorDebugDataTarget* pDataTarget, DWORD defines, DWORD dataStructureVersion); + +private: + Target_MDInternalRW m_targetData; + CMiniMdSchema m_Schema; + CMiniTableDef m_TableDefs[TBL_COUNT]; + MetaData::DataBlob m_StringHeap; + MetaData::DataBlob m_UserStringHeap; + MetaData::DataBlob m_BlobHeap; + MetaData::DataBlob m_GuidHeap; + MetaData::DataBlob m_TableRecords[TBL_COUNT]; + BOOL m_bSortable[TBL_COUNT]; + MetaData::DataBlob m_Sig; + + NewArrayHolder<BYTE> m_StringHeapStorage; + NewArrayHolder<BYTE> m_UserStringHeapStorage; + NewArrayHolder<BYTE> m_BlobHeapStorage; + NewArrayHolder<BYTE> m_GuidHeapStorage; + NewArrayHolder<BYTE> m_TableRecordsStorage[TBL_COUNT]; + NewArrayHolder<BYTE> m_SigStorage; + + volatile LONG m_cRef; +}; + + +#endif diff --git a/src/coreclr/md/datasource/stdafx.h b/src/coreclr/md/datasource/stdafx.h new file mode 100644 index 00000000000..1495c5f4636 --- /dev/null +++ b/src/coreclr/md/datasource/stdafx.h @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// stdafx.h +// + +// +// Precompiled headers. +// +//***************************************************************************** +#ifndef __STDAFX_H_ +#define __STDAFX_H_ + +#include <crtwrap.h> +#include <winwrap.h> +#include <utilcode.h> + +#include <cor.h> +#include <corpriv.h> + +#include <metamodelro.h> +#include <liteweightstgdb.h> + + +#endif // __STDAFX_H_ diff --git a/src/coreclr/md/datasource/targettypes.cpp b/src/coreclr/md/datasource/targettypes.cpp new file mode 100644 index 00000000000..60e3b7f4132 --- /dev/null +++ b/src/coreclr/md/datasource/targettypes.cpp @@ -0,0 +1,531 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// TargetTypes.cpp +// + +// +//***************************************************************************** + +#include "stdafx.h" +#include "targettypes.h" + + +Target_CMiniMdSchemaBase::Target_CMiniMdSchemaBase() : +m_ulReserved(0), +m_major(0), +m_minor(0), +m_heaps(0), +m_rid(0), +m_maskvalid(0), +m_sorted(0) +{} + +HRESULT Target_CMiniMdSchemaBase::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + reader.Align(8); // this type needs 8 byte alignment from m_maskvalid + IfFailRet(reader.Read32(&m_ulReserved)); + IfFailRet(reader.Read8(&m_major)); + IfFailRet(reader.Read8(&m_minor)); + IfFailRet(reader.Read8(&m_heaps)); + IfFailRet(reader.Read8(&m_rid)); + IfFailRet(reader.Read64(&m_maskvalid)); + IfFailRet(reader.Read64(&m_sorted)); + return S_OK; +} + +Target_CMiniMdSchema::Target_CMiniMdSchema() : +m_ulExtra(0) +{ + memset(&m_cRecs, 0, TBL_COUNT*sizeof(ULONG32)); +} + +HRESULT Target_CMiniMdSchema::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_CMiniMdSchemaBase::ReadFrom(reader)); + reader.AlignBase(); + for (int i = 0; i < TBL_COUNT; i++) + IfFailRet(reader.Read32(&(m_cRecs[i]))); + IfFailRet(reader.Read32(&m_ulExtra)); + return S_OK; +} + +Target_CMiniColDef::Target_CMiniColDef() : +m_Type(0), +m_oColumn(0), +m_cbColumn(0) +{ +} + +HRESULT Target_CMiniColDef::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.Read8(&m_Type)); + IfFailRet(reader.Read8(&m_oColumn)); + IfFailRet(reader.Read8(&m_cbColumn)); + return S_OK; +} + +Target_CMiniTableDef::Target_CMiniTableDef() : +m_pColDefs(NULL), +m_cCols(0), +m_iKey(0), +m_cbRec(0) +{} + +HRESULT Target_CMiniTableDef::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + CORDB_ADDRESS pColDefs = NULL; + IfFailRet(reader.ReadPointer(&pColDefs)); + IfFailRet(reader.Read8(&m_cCols)); + IfFailRet(reader.Read8(&m_iKey)); + IfFailRet(reader.Read8(&m_cbRec)); + + // sanity check before allocating + if (m_cCols > 100) + return CLDB_E_FILE_CORRUPT; + m_pColDefs = new (nothrow) Target_CMiniColDef[m_cCols]; + if (m_pColDefs == NULL) + return E_OUTOFMEMORY; + DataTargetReader colsReader = reader.CreateReaderAt(pColDefs); + for (int i = 0; i < m_cCols; i++) + { + IfFailRet(colsReader.Read(&m_pColDefs[i])); + } + + return S_OK; +} + +Target_CMiniMdBase::Target_CMiniMdBase() : +m_TblCount(0), +m_fVerifiedByTrustedSource(FALSE), +m_iStringsMask(0), +m_iGuidsMask(0), +m_iBlobsMask(0) +{} + +HRESULT Target_CMiniMdBase::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.SkipPointer()); // vtable + IfFailRet(reader.Read(&m_Schema)); + IfFailRet(reader.Read32(&m_TblCount)); + IfFailRet(reader.Read32((ULONG32*)&m_fVerifiedByTrustedSource)); + for (int i = 0; i < TBL_COUNT; i++) + IfFailRet(reader.Read(&(m_TableDefs[i]))); + IfFailRet(reader.Read32(&m_iStringsMask)); + IfFailRet(reader.Read32(&m_iGuidsMask)); + IfFailRet(reader.Read32(&m_iBlobsMask)); + return S_OK; +} + +Target_MapSHash::Target_MapSHash() : +m_table(0), +m_tableSize(0), +m_tableCount(0), +m_tableOccupied(0), +m_tableMax(0) +{} + +HRESULT Target_MapSHash::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.ReadPointer(&m_table)); + IfFailRet(reader.Read32(&m_tableSize)); + IfFailRet(reader.Read32(&m_tableCount)); + IfFailRet(reader.Read32(&m_tableOccupied)); + IfFailRet(reader.Read32(&m_tableMax)); + return S_OK; +} + + + +Target_StgPoolSeg::Target_StgPoolSeg() : +m_pSegData(0), +m_pNextSeg(0), +m_cbSegSize(0), +m_cbSegNext(0) +{} + +HRESULT Target_StgPoolSeg::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.ReadPointer(&m_pSegData)); + IfFailRet(reader.ReadPointer(&m_pNextSeg)); + IfFailRet(reader.Read32(&m_cbSegSize)); + IfFailRet(reader.Read32(&m_cbSegNext)); + return S_OK; +} + + +Target_CChainedHash::Target_CChainedHash() : +m_rgData(0), +m_iBuckets(0), +m_iSize(0), +m_iCount(0), +m_iMaxChain(0), +m_iFree(0) +{} + +HRESULT Target_CChainedHash::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.SkipPointer()); // __vfptr + IfFailRet(reader.ReadPointer(&m_rgData)); + IfFailRet(reader.Read32(&m_iBuckets)); + IfFailRet(reader.Read32(&m_iSize)); + IfFailRet(reader.Read32(&m_iCount)); + IfFailRet(reader.Read32(&m_iMaxChain)); + IfFailRet(reader.Read32(&m_iFree)); + return S_OK; +} + +Target_CStringPoolHash::Target_CStringPoolHash() : +m_Pool(0) +{} + +HRESULT Target_CStringPoolHash::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_CChainedHash::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.ReadPointer(&m_Pool)); + return S_OK; +} + +Target_CBlobPoolHash::Target_CBlobPoolHash() : +m_Pool(0) +{} + +HRESULT Target_CBlobPoolHash::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_CChainedHash::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.ReadPointer(&m_Pool)); + return S_OK; +} + + +Target_CGuidPoolHash::Target_CGuidPoolHash() : +m_Pool(0) +{ } + +HRESULT Target_CGuidPoolHash::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_CChainedHash::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.ReadPointer(&m_Pool)); + return S_OK; +} + +Target_HotHeap::Target_HotHeap() : +m_pHotHeapHeader(0) +{} + +HRESULT Target_HotHeap::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.ReadPointer(&m_pHotHeapHeader)); + return S_OK; +} + +HRESULT Target_StgPoolReadOnly::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.SkipPointer()); // __vfptr + IfFailRet(Target_StgPoolSeg::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.Read(&m_HotHeap)); + return S_OK; +} + +Target_StgPool::Target_StgPool() : +m_ulGrowInc(0), +m_pCurSeg(0), +m_cbCurSegOffset(0), +m_bFree(FALSE), +m_bReadOnly(FALSE), +m_nVariableAlignmentMask(0), +m_cbStartOffsetOfEdit(0), +m_fValidOffsetOfEdit(FALSE) +{} + +HRESULT Target_StgPool::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_StgPoolReadOnly::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.Read32(&m_ulGrowInc)); + IfFailRet(reader.ReadPointer(&m_pCurSeg)); + IfFailRet(reader.Read32(&m_cbCurSegOffset)); + ULONG32 bitField; + IfFailRet(reader.Read32(&bitField)); + m_bFree = (bitField & 0x1) != 0; + m_bReadOnly = (bitField & 0x2) != 0; + IfFailRet(reader.Read32(&m_nVariableAlignmentMask)); + IfFailRet(reader.Read32(&m_cbStartOffsetOfEdit)); + IfFailRet(reader.Read8((BYTE*)&m_fValidOffsetOfEdit)); + return S_OK; +} + +HRESULT Target_StgBlobPool::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_StgPool::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.Read(&m_Hash)); + return S_OK; +} + +Target_StgStringPool::Target_StgStringPool() : +m_bHash(FALSE) +{ +} + +HRESULT Target_StgStringPool::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_StgPool::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.Read(&m_Hash)); + IfFailRet(reader.Read8((BYTE*)&m_bHash)); + return S_OK; +} + +Target_StgGuidPool::Target_StgGuidPool() : +m_bHash(FALSE) +{} + +HRESULT Target_StgGuidPool::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_StgPool::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.Read(&m_Hash)); + IfFailRet(reader.Read8((BYTE*)&m_bHash)); + return S_OK; +} + +Target_RecordPool::Target_RecordPool() : +m_cbRec(0) +{} + +HRESULT Target_RecordPool::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_StgPool::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.Read32(&m_cbRec)); + return S_OK; +} + +Target_OptionValue::Target_OptionValue() : +m_DupCheck(0), +m_RefToDefCheck(0), +m_NotifyRemap(0), +m_UpdateMode(0), +m_ErrorIfEmitOutOfOrder(0), +m_ThreadSafetyOptions(0), +m_ImportOption(0), +m_LinkerOption(0), +m_GenerateTCEAdapters(0), +m_RuntimeVersion(0), +m_MetadataVersion(0), +m_MergeOptions(0), +m_InitialSize(0), +m_LocalRefPreservation(0) +{} + +HRESULT Target_OptionValue::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.Read32(&m_DupCheck)); + IfFailRet(reader.Read32(&m_RefToDefCheck)); + IfFailRet(reader.Read32(&m_NotifyRemap)); + IfFailRet(reader.Read32(&m_UpdateMode)); + IfFailRet(reader.Read32(&m_ErrorIfEmitOutOfOrder)); + IfFailRet(reader.Read32(&m_ThreadSafetyOptions)); + IfFailRet(reader.Read32(&m_ImportOption)); + IfFailRet(reader.Read32(&m_LinkerOption)); + IfFailRet(reader.Read32(&m_GenerateTCEAdapters)); + IfFailRet(reader.ReadPointer(&m_RuntimeVersion)); + IfFailRet(reader.Read32(&m_MetadataVersion)); + IfFailRet(reader.Read32(&m_MergeOptions)); + IfFailRet(reader.Read32(&m_InitialSize)); + IfFailRet(reader.Read32(&m_LocalRefPreservation)); + return S_OK; +} + +Target_CMiniMdRW::Target_CMiniMdRW() : +m_pMemberRefHash(0), +m_pMemberDefHash(0), +m_pNamedItemHash(0), +m_maxRid(0), +m_limRid(0), +m_maxIx(0), +m_limIx(0), +m_eGrow(0), +m_pHandler(0), +m_cbSaveSize(0), +m_fIsReadOnly(FALSE), +m_bPreSaveDone(FALSE), +m_bSaveCompressed(FALSE), +m_bPostGSSMod(FALSE), +m_pMethodMap(0), +m_pFieldMap(0), +m_pPropertyMap(0), +m_pEventMap(0), +m_pParamMap(0), +m_pFilterTable(0), +m_pHostFilter(0), +m_pTokenRemapManager(0), +dbg_m_pLock(0), +m_fMinimalDelta(FALSE), +m_rENCRecs(0) +{ + memset(&m_pLookUpHashs, 0, TBL_COUNT*sizeof(CORDB_ADDRESS)); + memset(&m_pVS, 0, TBL_COUNT*sizeof(CORDB_ADDRESS)); + memset(&m_bSortable, 0, TBL_COUNT*sizeof(BOOL)); +} + +HRESULT Target_CMiniMdRW::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_CMiniMdTemplate_CMiniMdRW::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.ReadPointer(&m_pMemberRefHash)); + IfFailRet(reader.ReadPointer(&m_pMemberDefHash)); + for (int i = 0; i < TBL_COUNT; i++) + IfFailRet(reader.ReadPointer(&m_pLookUpHashs[i])); + IfFailRet(reader.Read(&m_StringPoolOffsetHash)); + IfFailRet(reader.ReadPointer(&m_pNamedItemHash)); + IfFailRet(reader.Read32(&m_maxRid)); + IfFailRet(reader.Read32(&m_limRid)); + IfFailRet(reader.Read32(&m_maxIx)); + IfFailRet(reader.Read32(&m_limIx)); + IfFailRet(reader.Read32(&m_eGrow)); + for (int i = 0; i < TBL_COUNT; i++) + IfFailRet(reader.Read(&m_Tables[i])); + for (int i = 0; i < TBL_COUNT; i++) + IfFailRet(reader.ReadPointer(&m_pVS[i])); + IfFailRet(reader.Read(&m_StringHeap)); + IfFailRet(reader.Read(&m_BlobHeap)); + IfFailRet(reader.Read(&m_UserStringHeap)); + IfFailRet(reader.Read(&m_GuidHeap)); + IfFailRet(reader.ReadPointer(&m_pHandler)); + IfFailRet(reader.Read32(&m_cbSaveSize)); + ULONG32 bitField; + IfFailRet(reader.Read32(&bitField)); + m_fIsReadOnly = (bitField & 0x1) != 0; + m_bPreSaveDone = (bitField & 0x2) != 0; + m_bSaveCompressed = (bitField & 0x4) != 0; + m_bPostGSSMod = (bitField & 0x8) != 0; + IfFailRet(reader.ReadPointer(&m_pMethodMap)); + IfFailRet(reader.ReadPointer(&m_pFieldMap)); + IfFailRet(reader.ReadPointer(&m_pPropertyMap)); + IfFailRet(reader.ReadPointer(&m_pEventMap)); + IfFailRet(reader.ReadPointer(&m_pParamMap)); + IfFailRet(reader.ReadPointer(&m_pFilterTable)); + IfFailRet(reader.ReadPointer(&m_pHostFilter)); + IfFailRet(reader.ReadPointer(&m_pTokenRemapManager)); + IfFailRet(reader.Read(&m_OptionValue)); + IfFailRet(reader.Read(&m_StartupSchema)); + for (int i = 0; i < TBL_COUNT; i++) + IfFailRet(reader.Read8((BYTE*)&m_bSortable[i])); + if (reader.IsDefined(1)) // replace this with DEFINE__DEBUG + { + IfFailRet(reader.ReadPointer(&dbg_m_pLock)); + } + IfFailRet(reader.Read8((BYTE*)&m_fMinimalDelta)); + IfFailRet(reader.ReadPointer(&m_rENCRecs)); + return S_OK; +} + + + +Target_CLiteWeightStgdb_CMiniMdRW::Target_CLiteWeightStgdb_CMiniMdRW() : +m_pvMd(0), +m_cbMd(0) +{ +} + +HRESULT Target_CLiteWeightStgdb_CMiniMdRW::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.Read(&m_MiniMd)); + IfFailRet(reader.ReadPointer(&m_pvMd)); + IfFailRet(reader.Read32(&m_cbMd)); + return S_OK; +} + +Target_CLiteWeightStgdbRW::Target_CLiteWeightStgdbRW() : +m_cbSaveSize(0), +m_bSaveCompressed(FALSE), +m_pImage(0), +m_dwImageSize(0), +m_dwPEKind(0), +m_dwMachine(0), +m_pStreamList(0), +m_pNextStgdb(0), +m_eFileType(0), +m_wszFileName(0), +m_dwDatabaseLFT(0), +m_dwDatabaseLFS(0) +{} + +HRESULT Target_CLiteWeightStgdbRW::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(Target_CLiteWeightStgdb_CMiniMdRW::ReadFrom(reader)); + reader.AlignBase(); + IfFailRet(reader.Read32(&m_cbSaveSize)); + IfFailRet(reader.Read8((BYTE*)&m_bSaveCompressed)); + IfFailRet(reader.ReadPointer(&m_pImage)); + IfFailRet(reader.Read32(&m_dwImageSize)); + IfFailRet(reader.Read32(&m_dwPEKind)); + IfFailRet(reader.Read32(&m_dwMachine)); + IfFailRet(reader.ReadPointer(&m_pStreamList)); + IfFailRet(reader.ReadPointer(&m_pNextStgdb)); + IfFailRet(reader.Read32(&m_eFileType)); + IfFailRet(reader.ReadPointer(&m_wszFileName)); + IfFailRet(reader.Read32(&m_dwDatabaseLFT)); + IfFailRet(reader.Read32(&m_dwDatabaseLFS)); + IfFailRet(reader.ReadPointer(&m_pStgIO)); + return S_OK; +} + + + +Target_MDInternalRW::Target_MDInternalRW() : +m_tdModule(0), +m_cRefs(0), +m_fOwnStgdb(FALSE), +m_pUnk(0), +m_pUserUnk(0), +m_pIMetaDataHelper(0), +m_pSemReadWrite(0), +m_fOwnSem(FALSE) +{ +} + +HRESULT Target_MDInternalRW::ReadFrom(DataTargetReader & reader) +{ + HRESULT hr = S_OK; + IfFailRet(reader.SkipPointer()); // IMDInternalImportENC vtable + IfFailRet(reader.SkipPointer()); // IMDCommon vtable + IfFailRet(reader.ReadPointer(&m_pStgdb)); + IfFailRet(reader.Read32(&m_tdModule)); + IfFailRet(reader.Read32(&m_cRefs)); + IfFailRet(reader.Read8((BYTE*)&m_fOwnStgdb)); + IfFailRet(reader.ReadPointer(&m_pUnk)); + IfFailRet(reader.ReadPointer(&m_pUserUnk)); + IfFailRet(reader.ReadPointer(&m_pIMetaDataHelper)); + IfFailRet(reader.ReadPointer(&m_pSemReadWrite)); + IfFailRet(reader.Read8((BYTE*)&m_fOwnSem)); + return S_OK; +} diff --git a/src/coreclr/md/datasource/targettypes.h b/src/coreclr/md/datasource/targettypes.h new file mode 100644 index 00000000000..a51e8e99b1a --- /dev/null +++ b/src/coreclr/md/datasource/targettypes.h @@ -0,0 +1,359 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// TargetTypes.h +// + +// +//***************************************************************************** + +#ifndef _MD_TARGET_TYPES_ +#define _MD_TARGET_TYPES_ + +#include "datatargetreader.h" + +class Target_CMiniMdSchemaBase : public TargetObject +{ +public: + Target_CMiniMdSchemaBase(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + ULONG32 m_ulReserved; + BYTE m_major; + BYTE m_minor; + BYTE m_heaps; + BYTE m_rid; + ULONG64 m_maskvalid; + ULONG64 m_sorted; +}; + +class Target_CMiniMdSchema : public Target_CMiniMdSchemaBase +{ +public: + Target_CMiniMdSchema(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + ULONG32 m_cRecs[TBL_COUNT]; + ULONG32 m_ulExtra; +}; + +class Target_CMiniColDef : public TargetObject +{ +public: + Target_CMiniColDef(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + BYTE m_Type; + BYTE m_oColumn; + BYTE m_cbColumn; +}; + +class Target_CMiniTableDef : public TargetObject +{ +public: + Target_CMiniTableDef(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + NewArrayHolder<Target_CMiniColDef> m_pColDefs; + BYTE m_cCols; + BYTE m_iKey; + BYTE m_cbRec; + +private: + // don't copy this type - avoids needing to deep copy m_pColDefs + Target_CMiniTableDef(const Target_CMiniTableDef & rhs) { _ASSERTE(!"Don't copy"); } + Target_CMiniTableDef & operator=(const Target_CMiniTableDef &) + { + _ASSERTE(!"Don't copy"); + return *this; + } +}; + +class Target_CMiniMdBase : public TargetObject +{ +public: + Target_CMiniMdBase(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + Target_CMiniMdSchema m_Schema; + ULONG32 m_TblCount; + BOOL m_fVerifiedByTrustedSource; + Target_CMiniTableDef m_TableDefs[TBL_COUNT]; + + ULONG32 m_iStringsMask; + ULONG32 m_iGuidsMask; + ULONG32 m_iBlobsMask; +}; + +class Target_CMiniMdTemplate_CMiniMdRW : public Target_CMiniMdBase +{ +}; + +class Target_MapSHash : public TargetObject +{ +public: + Target_MapSHash(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_table; + ULONG32 m_tableSize; + ULONG32 m_tableCount; + ULONG32 m_tableOccupied; + ULONG32 m_tableMax; +}; + +class Target_CChainedHash : public TargetObject +{ +public: + Target_CChainedHash(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_rgData; + ULONG32 m_iBuckets; + ULONG32 m_iSize; + ULONG32 m_iCount; + ULONG32 m_iMaxChain; + ULONG32 m_iFree; +}; + +class Target_CStringPoolHash : public Target_CChainedHash +{ +public: + Target_CStringPoolHash(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_Pool; +}; + +class Target_StgPoolSeg : public TargetObject +{ +public: + Target_StgPoolSeg(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_pSegData; + CORDB_ADDRESS m_pNextSeg; + ULONG32 m_cbSegSize; + ULONG32 m_cbSegNext; +}; + +class Target_HotHeap : public TargetObject +{ +public: + Target_HotHeap(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_pHotHeapHeader; +}; + +class Target_StgPoolReadOnly : public Target_StgPoolSeg +{ +public: + virtual HRESULT ReadFrom(DataTargetReader & reader); + + Target_HotHeap m_HotHeap; +}; + +class Target_StgPool : public Target_StgPoolReadOnly +{ +public: + Target_StgPool(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + ULONG32 m_ulGrowInc; + CORDB_ADDRESS m_pCurSeg; + ULONG32 m_cbCurSegOffset; + BOOL m_bFree; + BOOL m_bReadOnly; + ULONG32 m_nVariableAlignmentMask; + ULONG32 m_cbStartOffsetOfEdit; + BOOL m_fValidOffsetOfEdit; +}; + +class Target_StgStringPool : public Target_StgPool +{ +public: + Target_StgStringPool(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + Target_CStringPoolHash m_Hash; + BOOL m_bHash; +}; + +class Target_StringHeapRW : public Target_StgStringPool +{ +}; + +class Target_CBlobPoolHash : public Target_CChainedHash +{ +public: + Target_CBlobPoolHash(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_Pool; +}; + +class Target_StgBlobPool : public Target_StgPool +{ +public: + virtual HRESULT ReadFrom(DataTargetReader & reader); + + Target_CBlobPoolHash m_Hash; +}; + +class Target_BlobHeapRW : public Target_StgBlobPool +{ +}; + +class Target_CGuidPoolHash : public Target_CChainedHash +{ +public: + Target_CGuidPoolHash(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_Pool; +}; + +class Target_StgGuidPool : public Target_StgPool +{ +public: + Target_StgGuidPool(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + Target_CGuidPoolHash m_Hash; + BOOL m_bHash; +}; + +class Target_GuidHeapRW : public Target_StgGuidPool +{ +}; + +class Target_RecordPool : public Target_StgPool +{ +public: + Target_RecordPool(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + ULONG32 m_cbRec; +}; + +class Target_TableRW : public Target_RecordPool +{ +}; + +class Target_OptionValue : public TargetObject +{ +public: + Target_OptionValue(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + ULONG32 m_DupCheck; + ULONG32 m_RefToDefCheck; + ULONG32 m_NotifyRemap; + ULONG32 m_UpdateMode; + ULONG32 m_ErrorIfEmitOutOfOrder; + ULONG32 m_ThreadSafetyOptions; + ULONG32 m_ImportOption; + ULONG32 m_LinkerOption; + ULONG32 m_GenerateTCEAdapters; + CORDB_ADDRESS m_RuntimeVersion; + ULONG32 m_MetadataVersion; + ULONG32 m_MergeOptions; + ULONG32 m_InitialSize; + ULONG32 m_LocalRefPreservation; +}; + +class Target_CMiniMdRW : public Target_CMiniMdTemplate_CMiniMdRW +{ +public: + Target_CMiniMdRW(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + CORDB_ADDRESS m_pMemberRefHash; + CORDB_ADDRESS m_pMemberDefHash; + CORDB_ADDRESS m_pLookUpHashs[TBL_COUNT]; + Target_MapSHash m_StringPoolOffsetHash; + CORDB_ADDRESS m_pNamedItemHash; + ULONG32 m_maxRid; + ULONG32 m_limRid; + ULONG32 m_maxIx; + ULONG32 m_limIx; + ULONG32 m_eGrow; + Target_TableRW m_Tables[TBL_COUNT]; + CORDB_ADDRESS m_pVS[TBL_COUNT]; + Target_StringHeapRW m_StringHeap; + Target_BlobHeapRW m_BlobHeap; + Target_BlobHeapRW m_UserStringHeap; + Target_GuidHeapRW m_GuidHeap; + CORDB_ADDRESS m_pHandler; + ULONG32 m_cbSaveSize; + BOOL m_fIsReadOnly; + BOOL m_bPreSaveDone; + BOOL m_bSaveCompressed; + BOOL m_bPostGSSMod; + CORDB_ADDRESS m_pMethodMap; + CORDB_ADDRESS m_pFieldMap; + CORDB_ADDRESS m_pPropertyMap; + CORDB_ADDRESS m_pEventMap; + CORDB_ADDRESS m_pParamMap; + CORDB_ADDRESS m_pFilterTable; + CORDB_ADDRESS m_pHostFilter; + CORDB_ADDRESS m_pTokenRemapManager; + Target_OptionValue m_OptionValue; + Target_CMiniMdSchema m_StartupSchema; + BYTE m_bSortable[TBL_COUNT]; + CORDB_ADDRESS dbg_m_pLock; + BOOL m_fMinimalDelta; + CORDB_ADDRESS m_rENCRecs; +}; + +class Target_CLiteWeightStgdb_CMiniMdRW : public TargetObject +{ +public: + Target_CLiteWeightStgdb_CMiniMdRW(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + Target_CMiniMdRW m_MiniMd; + CORDB_ADDRESS m_pvMd; + ULONG32 m_cbMd; +}; + +class Target_CLiteWeightStgdbRW : public Target_CLiteWeightStgdb_CMiniMdRW +{ +public: + Target_CLiteWeightStgdbRW(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + ULONG32 m_cbSaveSize; + BOOL m_bSaveCompressed; + CORDB_ADDRESS m_pImage; + ULONG32 m_dwImageSize; + ULONG32 m_dwPEKind; + ULONG32 m_dwMachine; + CORDB_ADDRESS m_pStreamList; + CORDB_ADDRESS m_pNextStgdb; + ULONG32 m_eFileType; + CORDB_ADDRESS m_wszFileName; + ULONG32 m_dwDatabaseLFT; + ULONG32 m_dwDatabaseLFS; + CORDB_ADDRESS m_pStgIO; +}; + +class Target_MDInternalRW : public TargetObject +{ +public: + Target_MDInternalRW(); + virtual HRESULT ReadFrom(DataTargetReader & reader); + + Target_CLiteWeightStgdbRW m_pStgdb; + ULONG32 m_tdModule; + ULONG32 m_cRefs; + BOOL m_fOwnStgdb; + CORDB_ADDRESS m_pUnk; + CORDB_ADDRESS m_pUserUnk; + CORDB_ADDRESS m_pIMetaDataHelper; + CORDB_ADDRESS m_pSemReadWrite; + BOOL m_fOwnSem; +}; + +#endif diff --git a/src/coreclr/md/debug_metadata.h b/src/coreclr/md/debug_metadata.h new file mode 100644 index 00000000000..7f98c4c182b --- /dev/null +++ b/src/coreclr/md/debug_metadata.h @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: Debug_MetaData.h +// + +// +// This file defines special macros for debugging MetaData (even in special retail builds). +// The level of debugging is set by these (input) macros: +// * code:#_DEBUG_METADATA +// * code:#_DEBUG_MDSCHEMA +// +// +// #_DEBUG_METADATA +// _DEBUG_METADATA ... Enables debugging information in MetaData implementation. It's useful for debugging +// retail builds in MetaData (when using CHK build is too slow). +// Note: Enabled by default if _DEBUG is defined (see code:#DefaultSetting_DEBUG_METADATA), can be +// enabled externally/explicitly also in retail builds (without _DEBUG defined). +// +// Defines macros (see code:#Macros_DEBUG_METADATA): +// * code:#INDEBUG_MD +// * code:#COMMA_INDEBUG_MD +// * code:#INDEBUG_MD_COMMA +// +// #_DEBUG_MDSCHEMA +// _DEBUG_MDSCHEMA ... Enables additional debugging of MetaData schema. +// Note: Allowed to be enabled only if _DEBUG is defined (see code:#Check_DEBUG_MDSCHEMA). +// +// Defines macros (see code:#Macros_DEBUG_MDSCHEMA): +// * code:#_ASSERTE_MDSCHEMA +// +// ====================================================================================== + +#pragma once + +// Include for REGUTIL class used in Debug_ReportError +#include <utilcode.h> + +// -------------------------------------------------------------------------------------- +//#DefaultSetting_DEBUG_METADATA +// +// Enable _DEBUG_METADATA by default if _DEBUG is defined (code:#_DEBUG_METADATA). +// +#ifdef _DEBUG + #define _DEBUG_METADATA +#endif //_DEBUG + +// -------------------------------------------------------------------------------------- +//#Macros_DEBUG_METADATA +// +// Define macros for MetaData implementation debugging (see code:#_DEBUG_METADATA). +// +#ifdef _DEBUG_METADATA + //#INDEBUG_MD + #define INDEBUG_MD(expr) expr + //#COMMA_INDEBUG_MD + #define COMMA_INDEBUG_MD(expr) , expr + //#INDEBUG_MD_COMMA + #define INDEBUG_MD_COMMA(expr) expr, + + #define Debug_ReportError(strMessage) \ + do { \ + if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0)) \ + { _ASSERTE_MSG(FALSE, (strMessage)); } \ + } while(0) + #define Debug_ReportInternalError(strMessage) _ASSERTE_MSG(FALSE, (strMessage)) +#else //!_DEBUG_METADATA + #define INDEBUG_MD(expr) + #define COMMA_INDEBUG_MD(expr) + #define INDEBUG_MD_COMMA(expr) + + #define Debug_ReportError(strMessage) + #define Debug_ReportInternalError(strMessage) _ASSERTE(!(strMessage)) +#endif //!_DEBUG_METADATA + +// -------------------------------------------------------------------------------------- +//#Check_DEBUG_MDSCHEMA +// +// Check that _DEBUG_MDSCHEMA is defined only if _DEBUG is defined (see code:#_DEBUG_MDSCHEMA). +// +#ifdef _DEBUG_MDSCHEMA + #ifndef _DEBUG + #error _DEBUG_MDSCHEMA is defined while _DEBUG is not defined. + #endif //!_DEBUG +#endif //_DEBUG_MDSCHEMA + +// -------------------------------------------------------------------------------------- +//#Macros_DEBUG_MDSCHEMA +// +// Define macros for MetaData schema debugging (see code:#_DEBUG_MDSCHEMA). +// +#ifdef _DEBUG_MDSCHEMA + //#_ASSERTE_MDSCHEMA + // This assert is useful only to catch errors in schema (tables and columns) definitions. It is useful e.g. + // for verifying consistency between table record classes (e.g. code:MethodDefRecord) and columns' + // offsets/sizes as defined in code:ColumnDefinition. + #define _ASSERTE_MDSCHEMA(expr) _ASSERTE(expr) +#else //!_DEBUG_MDSCHEMA + #define _ASSERTE_MDSCHEMA(expr) +#endif //!_DEBUG_MDSCHEMA diff --git a/src/coreclr/md/enc/CMakeLists.txt b/src/coreclr/md/enc/CMakeLists.txt new file mode 100644 index 00000000000..6bd2518d868 --- /dev/null +++ b/src/coreclr/md/enc/CMakeLists.txt @@ -0,0 +1,71 @@ +set(MDRUNTIMERW_SOURCES + liteweightstgdbrw.cpp + metamodelenc.cpp + metamodelrw.cpp + peparse.cpp + rwutil.cpp + stgio.cpp + stgtiggerstorage.cpp + stgtiggerstream.cpp + mdinternalrw.cpp + pdbheap.cpp +) + +set(MDRUNTIMERW_HEADERS + ../../inc/corhdr.h + ../../inc/metadata.h + ../../inc/mdfileformat.h + ../../inc/pedecoder.h + ../../inc/pedecoder.inl + ../../inc/posterror.h + ../../inc/sstring.h + ../../inc/sstring.inl + ../compiler/importhelper.h + ../compiler/regmeta.h + ../hotdata/hotdataformat.h + ../inc/liteweightstgdb.h + ../inc/mdinternalrw.h + ../inc/mdlog.h + ../inc/metadatahash.h + ../inc/metamodel.h + ../inc/metamodelro.h + ../inc/metamodelrw.h + ../inc/pdbheap.h + ../inc/portablepdbmdds.h + ../inc/portablepdbmdi.h + ../inc/rwutil.h + ../inc/stgio.h + ../inc/stgtiggerstorage.h + ../inc/stgtiggerstream.h + ../inc/streamutil.h + ../runtime/mdinternalro.h +) + +if (CLR_CMAKE_TARGET_WIN32) + list(APPEND MDRUNTIMERW_SOURCES ${MDRUNTIMERW_HEADERS}) +endif(CLR_CMAKE_TARGET_WIN32) + +convert_to_absolute_path(MDRUNTIMERW_HEADERS ${MDRUNTIMERW_HEADERS}) +convert_to_absolute_path(MDRUNTIMERW_SOURCES ${MDRUNTIMERW_SOURCES}) + +add_library_clr(mdruntimerw_dac ${MDRUNTIMERW_SOURCES}) +set_target_properties(mdruntimerw_dac PROPERTIES DAC_COMPONENT TRUE) +target_precompile_headers(mdruntimerw_dac PRIVATE stdafx.h) + +add_library_clr(mdruntimerw_wks_obj OBJECT ${MDRUNTIMERW_SOURCES}) +target_compile_definitions(mdruntimerw_wks_obj PRIVATE FEATURE_METADATA_EMIT_ALL) +target_precompile_headers(mdruntimerw_wks_obj PRIVATE stdafx.h) +add_library(mdruntimerw_wks INTERFACE) +target_sources(mdruntimerw_wks INTERFACE $<TARGET_OBJECTS:mdruntimerw_wks_obj>) + +add_library_clr(mdruntimerw-dbi ${MDRUNTIMERW_SOURCES}) +set_target_properties(mdruntimerw-dbi PROPERTIES DBI_COMPONENT TRUE) +target_precompile_headers(mdruntimerw-dbi PRIVATE stdafx.h) + +add_library_clr(mdruntimerw_crossgen ${MDRUNTIMERW_SOURCES}) +set_target_properties(mdruntimerw_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) +target_precompile_headers(mdruntimerw_crossgen PRIVATE stdafx.h) + +add_library_clr(mdruntimerw_ppdb ${MDRUNTIMERW_SOURCES}) +target_compile_definitions(mdruntimerw_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB) +target_precompile_headers(mdruntimerw_ppdb PRIVATE stdafx.h) diff --git a/src/coreclr/md/enc/liteweightstgdbrw.cpp b/src/coreclr/md/enc/liteweightstgdbrw.cpp new file mode 100644 index 00000000000..236c051706c --- /dev/null +++ b/src/coreclr/md/enc/liteweightstgdbrw.cpp @@ -0,0 +1,1323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** + +// +// LiteWeightStgdb.cpp +// +// This contains definition of class CLiteWeightStgDB. This is light weight +// read-only implementation for accessing compressed meta data format. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header. + +#include "metamodelrw.h" +#include "liteweightstgdb.h" + +// include stgdatabase.h for GUID_POOL_STREAM definition +// #include "stgdatabase.h" + +// include StgTiggerStorage for TiggerStorage definition +#include "stgtiggerstorage.h" +#include "stgio.h" +#include "pedecoder.h" + +#include <log.h> + + +#ifndef TYPELIB_SIG +#define TYPELIB_SIG_MSFT 0x5446534D // MSFT +#define TYPELIB_SIG_SLTG 0x47544C53 // SLTG +#endif + +//***************************************************************************** +// Checks the given storage object to see if it is an NT PE image. +//***************************************************************************** +int _IsNTPEImage( // true if file is NT PE image. + StgIO *pStgIO) // Storage object. +{ + LONG lfanew=0; // Offset in DOS header to NT header. + ULONG lSignature=0; // For NT header signature. + HRESULT hr; + + // Read DOS header to find the NT header offset. + if (FAILED(hr = pStgIO->Seek(60, FILE_BEGIN)) || + FAILED(hr = pStgIO->Read(&lfanew, sizeof(LONG), 0))) + { + return (false); + } + + // Seek to the NT header and read the signature. + if (FAILED(hr = pStgIO->Seek(VAL32(lfanew), FILE_BEGIN)) || + FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) || + FAILED(hr = pStgIO->Seek(0, FILE_BEGIN))) + { + return (false); + } + + // If the signature is a match, then we have a PE format. + if (lSignature == VAL32(IMAGE_NT_SIGNATURE)) + return (true); + else + return (false); +} + +BOOL _GetFileTypeForPathExt(StgIO * pStgIO, FILETYPE * piType) +{ + // Avoid confusion. + *piType = pStgIO->GetFileType(); + + // All file types except .obj have a signature built in. You should + // not get to this code for those file types unless that file is corrupt, + // or someone has changed a format without updating this code. + _ASSERTE((*piType == FILETYPE_UNKNOWN) || (*piType == FILETYPE_NTOBJ) || (*piType == FILETYPE_TLB)); + + // If we found a type, then you're ok. + return (*piType != FILETYPE_UNKNOWN); +} + +HRESULT _GetFileTypeForPath(StgIO *pStgIO, FILETYPE *piType) +{ + ULONG lSignature=0; + HRESULT hr; + + // Assume native file. + *piType = FILETYPE_CLB; + + // Need to read signature to see what type it is. + if (!(pStgIO->GetFlags() & DBPROP_TMODEF_CREATE)) + { + if (FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) || + FAILED(hr = pStgIO->Seek(0, FILE_BEGIN))) + { + return (hr); + } + lSignature = VAL32(lSignature); + if (lSignature == STORAGE_MAGIC_SIG) + *piType = FILETYPE_CLB; + else if ((WORD) lSignature ==IMAGE_DOS_SIGNATURE && _IsNTPEImage(pStgIO)) + *piType = FILETYPE_NTPE; + else if (lSignature == TYPELIB_SIG_MSFT || lSignature == TYPELIB_SIG_SLTG) + *piType = FILETYPE_TLB; + else if (!_GetFileTypeForPathExt(pStgIO, piType)) + return CLDB_E_FILE_CORRUPT; + } + return S_OK; +} + +//***************************************************************************** +// Prepare to go away. +//***************************************************************************** +CLiteWeightStgdbRW::~CLiteWeightStgdbRW() +{ + // Free up this stacks reference on the I/O object. + if (m_pStgIO != NULL) + { + m_pStgIO->Release(); + m_pStgIO = NULL; + } + + if (m_pStreamList != NULL) + { + delete m_pStreamList; + } + + if (m_wszFileName != NULL) + { + delete [] m_wszFileName; + } +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + if (m_pPdbHeap != NULL) + { + delete m_pPdbHeap; + m_pPdbHeap = NULL; + } +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB +} + +//***************************************************************************** +// Open an in-memory metadata section for read +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::InitOnMem( + ULONG cbData, // count of bytes in pData + LPCVOID pData, // points to meta data section in memory + int bReadOnly) // If true, read-only. +{ + StgIO *pStgIO = NULL; // For file i/o. + HRESULT hr = NOERROR; + + if ((pStgIO = new (nothrow) StgIO) == 0) + IfFailGo( E_OUTOFMEMORY); + + // Open the storage based on the pbData and cbData + IfFailGo( pStgIO->Open( + NULL, // filename + STGIO_READ, + pData, + cbData, + NULL, // IStream* + NULL) // LPSecurityAttributes + ); + + IfFailGo( InitFileForRead(pStgIO, bReadOnly) ); + +ErrExit: + if (SUCCEEDED(hr)) + { + m_pStgIO = pStgIO; + } + else + { + if (pStgIO) + pStgIO->Release(); + } + return hr; +} // CLiteWeightStgdbRW::InitOnMem + + +//***************************************************************************** +// Given an StgIO, opens compressed streams and do proper initialization. +// This is a helper for other Init functions. +//***************************************************************************** +__checkReturn +HRESULT +CLiteWeightStgdbRW::InitFileForRead( + StgIO * pStgIO, // For file i/o. + int bReadOnly) // If read-only open. +{ + TiggerStorage * pStorage = NULL; + void * pvData; + ULONG cbData; + HRESULT hr = NOERROR; + + // Allocate a new storage object which has IStorage on it. + pStorage = new (nothrow) TiggerStorage(); + IfNullGo(pStorage); + + // Init the storage object on the backing storage. + OptionValue ov; + IfFailGo(m_MiniMd.GetOption(&ov)); + IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion)); + + // Save pointers to header structure for version string. + _ASSERTE((m_pvMd == NULL) && (m_cbMd == 0)); + IfFailGo(pStorage->GetHeaderPointer(&m_pvMd, &m_cbMd)); + + // Check to see if this is a minimal metadata + if (SUCCEEDED(pStorage->OpenStream(MINIMAL_MD_STREAM, &cbData, &pvData))) + { + m_MiniMd.m_fMinimalDelta = TRUE; + } + + // Load the string pool. + if (SUCCEEDED(hr = pStorage->OpenStream(STRING_POOL_STREAM, &cbData, &pvData))) + { + // String pool has to end with a null-terminator, therefore we don't have to check string pool + // content on access. + // Shrink size of the pool to the last null-terminator found. + while (cbData != 0) + { + if (((LPBYTE)pvData)[cbData - 1] == 0) + { // We have found last null terminator + break; + } + // Shrink size of the pool + cbData--; + Debug_ReportError("String heap/pool does not end with null-terminator ... shrinking the heap."); + } + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, pvData, cbData, bReadOnly)); + } + else + { + if (hr != STG_E_FILENOTFOUND) + { + IfFailGo(hr); + } + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, NULL, 0, bReadOnly)); + } + + // Load the user string blob pool. + if (SUCCEEDED(hr = pStorage->OpenStream(US_BLOB_POOL_STREAM, &cbData, &pvData))) + { + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, pvData, cbData, bReadOnly)); + } + else + { + if (hr != STG_E_FILENOTFOUND) + { + IfFailGo(hr); + } + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, NULL, 0, bReadOnly)); + } + + // Load the guid pool. + if (SUCCEEDED(hr = pStorage->OpenStream(GUID_POOL_STREAM, &cbData, &pvData))) + { + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, pvData, cbData, bReadOnly)); + } + else + { + if (hr != STG_E_FILENOTFOUND) + { + IfFailGo(hr); + } + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, NULL, 0, bReadOnly)); + } + + // Load the blob pool. + if (SUCCEEDED(hr = pStorage->OpenStream(BLOB_POOL_STREAM, &cbData, &pvData))) + { + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, pvData, cbData, bReadOnly)); + } + else + { + if (hr != STG_E_FILENOTFOUND) + { + IfFailGo(hr); + } + IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, NULL, 0, bReadOnly)); + } + + // Open the metadata. + hr = pStorage->OpenStream(COMPRESSED_MODEL_STREAM, &cbData, &pvData); + if (hr == STG_E_FILENOTFOUND) + { + IfFailGo(pStorage->OpenStream(ENC_MODEL_STREAM, &cbData, &pvData)); + } + IfFailGo(m_MiniMd.InitOnMem(pvData, cbData, bReadOnly)); + IfFailGo(m_MiniMd.PostInit(0)); + +ErrExit: + if (pStorage != NULL) + { + delete pStorage; + } + return hr; +} // CLiteWeightStgdbRW::InitFileForRead + +//***************************************************************************** +// Open a metadata section for read +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::OpenForRead( + LPCWSTR szDatabase, // Name of database. + void *pbData, // Data to open on top of, 0 default. + ULONG cbData, // How big is the data. + DWORD dwFlags) // Flags for the open. +{ + LPCWSTR pNoFile=W(""); // Constant for empty file name. + StgIO *pStgIO = NULL; // For file i/o. + HRESULT hr; + + m_pImage = NULL; + m_dwImageSize = 0; + m_eFileType = FILETYPE_UNKNOWN; + // szDatabase, and pbData are mutually exclusive. Only one may be + // non-NULL. Having both NULL means empty stream creation. + // + _ASSERTE(!(szDatabase && (pbData))); + _ASSERTE(!(pbData && (szDatabase))); + + // Open on memory needs there to be something to work with. + if (pbData && cbData == 0) + IfFailGo(CLDB_E_NO_DATA); + + // Make sure we have a path to work with. + if (!szDatabase) + szDatabase = pNoFile; + + // Sanity check the name lentgh. + if (!IsValidFileNameLength(szDatabase)) + { + IfFailGo(E_INVALIDARG); + } + + // If we have storage to work with, init it and get type. + if (*szDatabase || pbData) + { + // Allocate a storage instance to use for i/o. + if ((pStgIO = new (nothrow) StgIO) == 0) + IfFailGo( E_OUTOFMEMORY ); + + DBPROPMODE dmOpenFlags = DBPROP_TMODEF_READ; + + // If we're taking ownership of this memory..... + if (IsOfTakeOwnership(dwFlags)) + { + dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_SHAREDMEM); + } +#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES + if (IsOfTrustedImage(dwFlags)) + dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_TRYLOADLIBRARY); +#endif + + // Open the storage so we can read the signature if there is already data. + IfFailGo( pStgIO->Open(szDatabase, + dmOpenFlags, + pbData, + cbData, + 0, // IStream* + NULL) ); + + // Determine the type of file we are working with. + IfFailGo( _GetFileTypeForPath(pStgIO, &m_eFileType) ); + } + + // Check for default type. + if (m_eFileType == FILETYPE_CLB) + { + // If user wanted us to make a local copy of the data, do that now. + if (IsOfCopyMemory(dwFlags)) + IfFailGo(pStgIO->LoadFileToMemory()); + + // Try the native .clb file. + IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) ); + } + // PE/COFF executable/object format. This requires us to find the .clb + // inside the binary before doing the Init. + else if (m_eFileType == FILETYPE_NTPE || m_eFileType == FILETYPE_NTOBJ) + { + //<TODO>@FUTURE: Ideally the FindImageMetaData function + //@FUTURE: would take the pStgIO and map only the part of the file where + //@FUTURE: our data lives, leaving the rest alone. This would be smaller + //@FUTURE: working set for us.</TODO> + void *ptr; + ULONG cbSize; + + // Map the entire binary for the FindImageMetaData function. + IfFailGo( pStgIO->MapFileToMem(ptr, &cbSize) ); + + // Find the .clb inside of the content. + if (m_eFileType == FILETYPE_NTPE) + { + m_pImage = ptr; + m_dwImageSize = cbSize; + hr = FindImageMetaData(ptr, + cbSize, + pStgIO->GetMemoryMappedType() == MTYPE_IMAGE, + &ptr, + &cbSize); + } + else + { + _ASSERTE(pStgIO->GetMemoryMappedType() != MTYPE_IMAGE); + hr = FindObjMetaData(ptr, cbSize, &ptr, &cbSize); + } + // Was the metadata found inside the PE file? + if (FAILED(hr)) + { + if (hr == E_OUTOFMEMORY) + IfFailGo(E_OUTOFMEMORY); + + // No clb in the PE, assume it is a type library. + m_eFileType = FILETYPE_TLB; + + // Let the caller deal with a TypeLib. + IfFailGo(hr); + } + else + { + // Metadata was found inside the file. + // Now reset the base of the stg object so that all memory accesses + // are relative to the .clb content. + // + IfFailGo( pStgIO->SetBaseRange(ptr, cbSize) ); + + // If user wanted us to make a local copy of the data, do that now. + if (IsOfCopyMemory(dwFlags)) + { + // Cache the PEKind, Machine. + GetPEKind(pStgIO->GetMemoryMappedType(), NULL, NULL); + // Copy the file into memory; releases the file. + IfFailGo(pStgIO->LoadFileToMemory()); + // No longer have the image. + m_pImage = NULL; + m_dwImageSize = 0; + } + + // Defer to the normal lookup. + IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) ); + } + } + else if (m_eFileType == FILETYPE_TLB) + { + // Let the caller deal with a TypeLib. + IfFailGo(CLDB_E_NO_DATA); + } + // This spells trouble, we need to handle all types we might find. + else + { + _ASSERTE(!"Unknown file type."); + IfFailGo( E_FAIL ); + } + + // Save off everything. + IfFailGo(SetFileName(szDatabase)); + + // If this was a file... + if (pbData == NULL) + { + WIN32_FILE_ATTRIBUTE_DATA faData; + if (!WszGetFileAttributesEx(szDatabase, GetFileExInfoStandard, &faData)) + IfFailGo(E_FAIL); + m_dwDatabaseLFS = faData.nFileSizeLow; + m_dwDatabaseLFT = faData.ftLastWriteTime.dwLowDateTime; + } + +ErrExit: + if (SUCCEEDED(hr)) + { + m_pStgIO = pStgIO; + } + else + { + if (pStgIO != NULL) + pStgIO->Release(); + } + return hr; +} + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE +// Open a metadata section for read/write +__checkReturn +HRESULT CLiteWeightStgdbRW::OpenForRead( + IMDCustomDataSource *pDataSource, // data to open on top of + DWORD dwFlags) // Flags for the open. +{ + LPCWSTR pNoFile = W(""); // Constant for empty file name. + StgIO *pStgIO = NULL; // For file i/o. + HRESULT hr; + + m_pImage = NULL; + m_dwImageSize = 0; + m_eFileType = FILETYPE_UNKNOWN; + + IfFailGo(m_MiniMd.InitOnCustomDataSource(pDataSource)); + IfFailGo(m_MiniMd.PostInit(0)); + + // Save off everything. + IfFailGo(SetFileName(pNoFile)); + +ErrExit: + return hr; +} +#endif + +// Read/Write versions. +//***************************************************************************** +// Init the Stgdb and its subcomponents. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::InitNew() +{ + InitializeLogging(); + LOG((LF_METADATA, LL_INFO10, "Metadata logging enabled\n")); +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + m_pPdbHeap = new PdbHeap(); +#endif + //<TODO>@FUTURE: should probably init the pools here instead of in the MiniMd.</TODO> + return m_MiniMd.InitNew(); +} + +//***************************************************************************** +// Determine what the size of the saved data will be. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetSaveSize(// S_OK or error. + CorSaveSize fSave, // Quick or accurate? + UINT32 *pcbSaveSize, // Put the size here. + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) // Profile data for working set optimization +{ + HRESULT hr = S_OK; // A result. + UINT32 cbTotal = 0; // The total size. + UINT32 cbSize = 0; // Size of a component. + + m_cbSaveSize = 0; + + // Allocate stream list if not already done. + if (m_pStreamList == NULL) + { + IfNullGo(m_pStreamList = new (nothrow) STORAGESTREAMLST); + } + else + { + m_pStreamList->Clear(); + } + + // Make sure the user string pool is not empty. An empty user string pool causes + // problems with edit and continue + + if (m_MiniMd.m_UserStringHeap.GetUnalignedSize() <= 1) + { + if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode) && + !m_MiniMd.IsMinimalDelta()) + { + BYTE rgData[] = {' ', 0, 0}; + UINT32 nIndex_Ignore; + IfFailGo(m_MiniMd.PutUserString( + MetaData::DataBlob(rgData, sizeof(rgData)), + &nIndex_Ignore)); + } + } + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + // [IMPORTANT]: + // Apparently, string pool must exist in the portable PDB metadata in order to be recognized + // by the VS debugger. For this purpose, we are adding a dummy " " value in cases when + // the string pool is empty. + // Interestingly enough, similar is done for user string pool (check above #549). + // TODO: do this in a more clever way, and check if/why/when this is necessary + if (m_MiniMd.m_StringHeap.GetUnalignedSize() <= 1) + { + if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode) && + !m_MiniMd.IsMinimalDelta()) + { + UINT32 nIndex_Ignore; + IfFailGo(m_MiniMd.m_StringHeap.AddString(" ", &nIndex_Ignore)); + } + } +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + + // If we're saving a delta metadata, figure out how much space it will take to + // save the minimal metadata stream (used only to identify that we have a delta + // metadata... nothing should be in that stream. + if ((m_MiniMd.m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateDelta) + { + IfFailGo(AddStreamToList(0, MINIMAL_MD_STREAM)); + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(MINIMAL_MD_STREAM, 0, &cbSize)); + cbTotal += cbSize; + } + + if (reorderingOptions & ReArrangeStringPool) + { + if (pProfileData != NULL) + { + UINT32 cbHotSize = 0; // Size of pool data. + UINT32 cbStream; // Size of just the stream. + DWORD bCompressed; // Will the stream be compressed data? + + // Ask the metadata to size its hot data. + IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData)); + cbStream = cbHotSize; + m_bSaveCompressed = bCompressed; + + if (cbHotSize != 0) + { + // Add this item to the save list. + IfFailGo(AddStreamToList(cbHotSize, HOT_MODEL_STREAM)); + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(HOT_MODEL_STREAM, cbHotSize, &cbHotSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + HOT_MODEL_STREAM, cbStream, cbHotSize)); + + cbTotal += cbHotSize; + } + } + + // get string pool save size + IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize)); + cbTotal += cbSize; + } + + // Query the MiniMd for its size. + IfFailGo(GetTablesSaveSize(fSave, &cbSize, reorderingOptions, pProfileData)); + cbTotal += cbSize; + + // Get the pools' sizes. + if( !(reorderingOptions & ReArrangeStringPool) ) + { + IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize)); + cbTotal += cbSize; + } + IfFailGo(GetPoolSaveSize(US_BLOB_POOL_STREAM, MDPoolUSBlobs, &cbSize)); + cbTotal += cbSize; + IfFailGo(GetPoolSaveSize(GUID_POOL_STREAM, MDPoolGuids, &cbSize)); + cbTotal += cbSize; + IfFailGo(GetPoolSaveSize(BLOB_POOL_STREAM, MDPoolBlobs, &cbSize)); + cbTotal += cbSize; +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + IfFailGo(GetPoolSaveSize(PDB_STREAM, NULL, &cbSize)); + cbTotal += cbSize; +#endif + + // Finally, ask the storage system to add fixed overhead it needs for the + // file format. The overhead of each stream has already be calculated as + // part of GetStreamSaveSize. What's left is the signature and header + // fixed size overhead. + IfFailGo(TiggerStorage::GetStorageSaveSize((ULONG *)&cbTotal, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize total is %d.\n", cbTotal)); + + // The list of streams that will be saved are now in the stream save list. + // Next step is to walk that list and fill out the correct offsets. This is + // done here so that the data can be streamed without fixing up the header. + TiggerStorage::CalcOffsets(m_pStreamList, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion); + + if (pcbSaveSize != NULL) + { + *pcbSaveSize = cbTotal; + } + + // Don't cache the value for the EnC case + if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode)) + m_cbSaveSize = cbTotal; + +ErrExit: + return hr; +} // CLiteWeightStgdbRW::GetSaveSize + +//***************************************************************************** +// Get the save size of one of the pools. Also adds the pool's stream to +// the list of streams to be saved. +//***************************************************************************** +__checkReturn +HRESULT +CLiteWeightStgdbRW::GetPoolSaveSize( + LPCWSTR szHeap, // Name of the heap stream. + int iPool, // The pool of which to get size. + UINT32 *pcbSaveSize) // Add pool data to this value. +{ + UINT32 cbSize = 0; // Size of pool data. + UINT32 cbStream; // Size of just the stream. + HRESULT hr = S_OK; + + *pcbSaveSize = 0; + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + // Treat PDB stream differently since we are not using StgPools + if (wcscmp(PDB_STREAM, szHeap) == 0) + { + if (m_pPdbHeap && !m_pPdbHeap->IsEmpty()) + { + cbSize = m_pPdbHeap->GetSize(); + IfFailGo(AddStreamToList(cbSize, szHeap)); + IfFailGo(TiggerStorage::GetStreamSaveSize(szHeap, cbSize, &cbSize)); + *pcbSaveSize = cbSize; + } + else + { + goto ErrExit; + } + } + else +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + { + // If there is no data, then don't bother. + if (m_MiniMd.IsPoolEmpty(iPool)) + return (S_OK); + + // Ask the pool to size its data. + IfFailGo(m_MiniMd.GetPoolSaveSize(iPool, &cbSize)); + cbStream = cbSize; + + // Add this item to the save list. + IfFailGo(AddStreamToList(cbSize, szHeap)); + + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(szHeap, cbSize, &cbSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + szHeap, cbStream, cbSize)); + + // Give the size of the pool to the caller's total. + *pcbSaveSize = cbSize; + } + +ErrExit: + return hr; +} + +//***************************************************************************** +// Get the save size of the metadata tables. Also adds the tables stream to +// the list of streams to be saved. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetTablesSaveSize( + CorSaveSize fSave, + UINT32 *pcbSaveSize, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) // Add pool data to this value. +{ + UINT32 cbSize = 0; // Size of pool data. + UINT32 cbHotSize = 0; // Size of pool data. + UINT32 cbStream; // Size of just the stream. + DWORD bCompressed; // Will the stream be compressed data? + LPCWSTR szName; // What will the name of the pool be? + HRESULT hr; + + *pcbSaveSize = 0; + + if( !(reorderingOptions & ReArrangeStringPool) ) + { + if (pProfileData != NULL) + { + // Ask the metadata to size its hot data. + IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData)); + cbStream = cbHotSize; + m_bSaveCompressed = bCompressed; + + if (cbHotSize != 0) + { + szName = HOT_MODEL_STREAM; + + // Add this item to the save list. + IfFailGo(AddStreamToList(cbHotSize, szName)); + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbHotSize, &cbHotSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + szName, cbStream, cbHotSize)); + } + } + } + // Ask the metadata to size its data. + IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbSize, &bCompressed)); + cbStream = cbSize; + m_bSaveCompressed = bCompressed; + szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM; + + // Add this item to the save list. + IfFailGo(AddStreamToList(cbSize, szName)); + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbSize, &cbSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + szName, cbStream, cbSize)); + + // Give the size of the pool to the caller's total. + *pcbSaveSize = cbHotSize + cbSize; + +ErrExit: + return hr; +} // CLiteWeightStgdbRW::GetTablesSaveSize + +//***************************************************************************** +// Add a stream, and its size, to the list of streams to be saved. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::AddStreamToList( + UINT32 cbSize, + LPCWSTR szName) +{ + HRESULT hr = S_OK; + PSTORAGESTREAM pItem; // New item to allocate & fill. + + // Add a new item to the end of the list. + IfNullGo(pItem = m_pStreamList->Append()); + + // Fill out the data. + pItem->SetOffset(0); + pItem->SetSize((ULONG)cbSize); + pItem->SetName(szName); + +ErrExit: + return hr; +} + +//***************************************************************************** +// Save the data to a stream. A TiggerStorage sub-allocates streams within +// the stream. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::SaveToStream( + IStream *pIStream, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr = S_OK; // A result. + StgIO *pStgIO = 0; + TiggerStorage *pStorage = 0; + + // Allocate a storage subsystem and backing store. + IfNullGo(pStgIO = new (nothrow) StgIO); + IfNullGo(pStorage = new (nothrow) TiggerStorage); + + // Open around this stream for write. + IfFailGo(pStgIO->Open(W(""), + DBPROP_TMODEF_DFTWRITEMASK, + 0, 0, // pbData, cbData + pIStream, + 0)); // LPSecurityAttributes + OptionValue ov; + IfFailGo(m_MiniMd.GetOption(&ov)); + IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion)); + + // Save worker will do tables, pools. + IfFailGo(SaveToStorage(pStorage, reorderingOptions, pProfileData)); + +ErrExit: + if (pStgIO != NULL) + pStgIO->Release(); + if (pStorage != NULL) + delete pStorage; + return hr; +} // CLiteWeightStgdbRW::SaveToStream + +//***************************************************************************** +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::SaveToStorage( + TiggerStorage *pStorage, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr; // A result. + LPCWSTR szName; // Name of the tables stream. + IStream *pIStreamTbl = 0; + UINT32 cb; + UINT32 cbSaveSize = m_cbSaveSize; + + // Must call GetSaveSize to cache the streams up front. + // Don't trust cached values in the delta case... if there was a previous call to get + // a non-delta size, it will be incorrect. + if ((m_cbSaveSize == 0) || IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode)) + { + IfFailGo(GetSaveSize(cssAccurate, &cbSaveSize)); + } + + // Save the header of the data file. + IfFailGo(pStorage->WriteHeader(m_pStreamList, 0, NULL)); + + // If this is a minimal delta, write a stream marker + if (IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode)) + { + IfFailGo(pStorage->CreateStream(MINIMAL_MD_STREAM, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStreamTbl)); + pIStreamTbl->Release(); + pIStreamTbl = 0; + } + + if (pProfileData != NULL) + { + DWORD bCompressed; + UINT32 cbHotSize; + // Will the stream be compressed data? + + // Only create this additional stream if it will be non-empty + IfFailGo(m_MiniMd.GetSaveSize(cssAccurate, &cbHotSize, &bCompressed, reorderingOptions, pProfileData)); + + if (cbHotSize > 0) + { + // Create a stream and save the hot tables. + szName = HOT_MODEL_STREAM; + IfFailGo(pStorage->CreateStream(szName, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStreamTbl)); + IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, reorderingOptions, pProfileData)); + pIStreamTbl->Release(); + pIStreamTbl = 0; + } + } + + if (reorderingOptions & ReArrangeStringPool) + { + // Save the string pool before the tables when we do not have the string pool cache + IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings)); + } + + // Create a stream and save the tables. + szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM; + IfFailGo(pStorage->CreateStream(szName, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStreamTbl)); + IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, NoReordering, NULL)); + pIStreamTbl->Release(); + pIStreamTbl = 0; + + // Save the pools. + if (!(reorderingOptions & ReArrangeStringPool)) + { + // string pool must be saved after the tables when we have the string pool cache + IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings)); + } + IfFailGo(SavePool(US_BLOB_POOL_STREAM, pStorage, MDPoolUSBlobs)); + IfFailGo(SavePool(GUID_POOL_STREAM, pStorage, MDPoolGuids)); + IfFailGo(SavePool(BLOB_POOL_STREAM, pStorage, MDPoolBlobs)); +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + IfFailGo(SavePool(PDB_STREAM, pStorage, NULL)); +#endif + + // Write the header to disk. + OptionValue ov; + IfFailGo(m_MiniMd.GetOption(&ov)); + + IfFailGo(pStorage->WriteFinished(m_pStreamList, (ULONG *)&cb, IsENCDelta(ov.m_UpdateMode))); + + _ASSERTE(cbSaveSize == cb); + + // Let the Storage release some memory. + pStorage->ResetBackingStore(); + + IfFailGo(m_MiniMd.SaveDone()); + +ErrExit: + if (pIStreamTbl != NULL) + pIStreamTbl->Release(); + delete m_pStreamList; + m_pStreamList = 0; + m_cbSaveSize = 0; + return hr; +} // CLiteWeightStgdbRW::SaveToStorage + +//***************************************************************************** +// Save a pool of data out to a stream. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::SavePool( // Return code. + LPCWSTR szName, // Name of stream on disk. + TiggerStorage *pStorage, // The storage to put data in. + int iPool) // The pool to save. +{ + IStream *pIStream=0; // For writing. + HRESULT hr = S_OK; + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + // Treat PDB stream differently since we are not using StgPools + if (wcscmp(PDB_STREAM, szName) == 0) + { + if (m_pPdbHeap && !m_pPdbHeap->IsEmpty()) + { + IfFailGo(pStorage->CreateStream(szName, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStream)); + IfFailGo(m_pPdbHeap->SaveToStream(pIStream)); + } + else + { + goto ErrExit; + } + } + else +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + { + // If there is no data, then don't bother. + if (m_MiniMd.IsPoolEmpty(iPool)) + return (S_OK); + + // Create the new stream to hold this table and save it. + IfFailGo(pStorage->CreateStream(szName, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStream)); + IfFailGo(m_MiniMd.SavePoolToStream(iPool, pIStream)); + } + +ErrExit: + if (pIStream) + pIStream->Release(); + return hr; +} // CLiteWeightStgdbRW::SavePool + + +//***************************************************************************** +// Save the metadata to a file. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::Save( + LPCWSTR szDatabase, // Name of file to which to save. + DWORD dwSaveFlags) // Flags for the save. +{ + TiggerStorage * pStorage = NULL; // IStorage object. + StgIO * pStgIO = NULL; // Backing storage. + HRESULT hr = S_OK; + + if (m_wszFileName == NULL) + { + if (szDatabase == NULL) + { + // Make sure that a NULL is not passed in the first time around. + _ASSERTE(!"Not allowed to pass a NULL for filename on the first call to Save."); + return E_INVALIDARG; + } + else + { + // Save the file name. + IfFailGo(SetFileName(szDatabase)); + } + } + else if ((szDatabase != NULL) && (SString::_wcsicmp(szDatabase, m_wszFileName) != 0)) + { + // Save the file name. + IfFailGo(SetFileName(szDatabase)); + } + + // Sanity check the name. + if (!IsValidFileNameLength(m_wszFileName)) + { + IfFailGo(E_INVALIDARG); + } + + m_eFileType = FILETYPE_CLB; + + // Allocate a new storage object. + IfNullGo(pStgIO = new (nothrow) StgIO); + + // Create the output file. + IfFailGo(pStgIO->Open(m_wszFileName, + DBPROP_TMODEF_DFTWRITEMASK, + 0,0, // pbData, cbData + 0, // IStream* + 0)); // LPSecurityAttributes + + // Allocate an IStorage object to use. + IfNullGo(pStorage = new (nothrow) TiggerStorage); + + // Init the storage object on the i/o system. + OptionValue ov; + IfFailGo(m_MiniMd.GetOption(&ov)); + IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion)); + + // Save the data. + IfFailGo(SaveToStorage(pStorage)); + +ErrExit: + if (pStgIO != NULL) + pStgIO->Release(); + if (pStorage != NULL) + delete pStorage; + return hr; +} // CLiteWeightStgdbRW::Save + +//***************************************************************************** +// Pull the PEKind and Machine out of PE headers -- if we have PE headers. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetPEKind( // S_OK or error. + MAPPINGTYPE mtMapping, // The type of mapping the image has + DWORD *pdwPEKind, // [OUT] The kind of PE (0 - not a PE) + DWORD *pdwMachine) // [OUT] Machine as defined in NT header +{ + HRESULT hr = NOERROR; + DWORD dwPEKind=0; // Working copy of pe kind. + DWORD dwMachine=0; // Working copy of machine. + +#ifndef DACCESS_COMPILE + // Do we already have cached information? + if (m_dwPEKind != (DWORD)(-1)) + { + dwPEKind = m_dwPEKind; + dwMachine = m_dwMachine; + } + else if (m_pImage) + { + PEDecoder pe; + + // We need to use different PEDecoder initialization based on the type of data we give it. + // We use the one with a 'bool' as the second argument when dealing with a mapped file, + // and we use the one that takes a COUNT_T as the second argument when dealing with a + // flat file. + + if (mtMapping == MTYPE_IMAGE) + { + if (FAILED(pe.Init(m_pImage, false)) || + !pe.CheckNTHeaders()) + { + IfFailRet(COR_E_BADIMAGEFORMAT); + } + } + else + { + pe.Init(m_pImage, (COUNT_T)(m_dwImageSize)); + } + + if (pe.HasContents() && pe.HasNTHeaders()) + { + pe.GetPEKindAndMachine(&dwPEKind, &dwMachine); + + + // Cache entries. + m_dwPEKind = dwPEKind; + m_dwMachine = dwMachine; + } + else // if (pe.HasContents()... + { + hr = COR_E_BADIMAGEFORMAT; + } + } + else + { + hr = S_FALSE; + } +#endif + if (pdwPEKind) + *pdwPEKind = dwPEKind; + if (pdwMachine) + *pdwMachine = dwMachine; + + return hr; +} // CLiteWeightStgdbRW::GetPEKind + +//***************************************************************************** +// Low level access to the data. Intended for metainfo, and such. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetRawData( + const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB'). + ULONG *pcbMd) // [OUT] put size of the stream here. +{ +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + if (m_pStgIO == NULL) + return COR_E_NOTSUPPORTED; +#endif + + *ppvMd = (const void*) m_pStgIO->m_pData; + *pcbMd = m_pStgIO->m_cbData; + return S_OK; +} // CLiteWeightStgdbRW::GetRawData + +//***************************************************************************** +// Get info about the MD stream. +// Low level access to stream data. Intended for metainfo, and such. +//***************************************************************************** +__checkReturn +STDMETHODIMP +CLiteWeightStgdbRW::GetRawStreamInfo( + ULONG ix, // [IN] Stream ordinal desired. + const char **ppchName, // [OUT] put pointer to stream name here. + const void **ppv, // [OUT] put pointer to MD stream here. + ULONG *pcb) // [OUT] put size of the stream here. +{ + HRESULT hr = NOERROR; + STORAGEHEADER sHdr; // Header for the storage. + PSTORAGESTREAM pStream; // Pointer to each stream. + ULONG i; // Loop control. + void *pData; + ULONG cbData; + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + if (m_pStgIO == NULL) + IfFailGo(COR_E_NOTSUPPORTED); +#endif + + pData = m_pStgIO->m_pData; + cbData = m_pStgIO->m_cbData; + + // Validate the signature of the format, or it isn't ours. + IfFailGo(MDFormat::VerifySignature((PSTORAGESIGNATURE) pData, cbData)); + + // Get back the first stream. + pStream = MDFormat::GetFirstStream(&sHdr, pData); + if (pStream == NULL) + { + Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Check that the requested stream exists. + if (ix >= sHdr.GetiStreams()) + return S_FALSE; + + // Skip to the desired stream. + for (i = 0; i < ix; i++) + { + PSTORAGESTREAM pNext = pStream->NextStream(); + + // Check that stream header is within the buffer. + if (((LPBYTE)pStream >= ((LPBYTE)pData + cbData)) || + ((LPBYTE)pNext > ((LPBYTE)pData + cbData))) + { + Debug_ReportError("Stream header is not within MetaData block."); + hr = CLDB_E_FILE_CORRUPT; + goto ErrExit; + } + + // Check that the stream data starts and fits within the buffer. + // need two checks on size because of wraparound. + if ((pStream->GetOffset() > cbData) || + (pStream->GetSize() > cbData) || + ((pStream->GetSize() + pStream->GetOffset()) > cbData)) + { + Debug_ReportError("Stream data are not within MetaData block."); + hr = CLDB_E_FILE_CORRUPT; + goto ErrExit; + } + + // Pick off the next stream if there is one. + pStream = pNext; + } + + if (pStream != NULL) + { + *ppv = (const void *)((const BYTE *)pData + pStream->GetOffset()); + *pcb = pStream->GetSize(); + *ppchName = pStream->GetName(); + } + else + { + *ppv = NULL; + *pcb = 0; + *ppchName = NULL; + + // Invalid input to the method + hr = CLDB_E_FILE_CORRUPT; + } + +ErrExit: + return hr; +} // CLiteWeightStgdbRW::GetRawStreamInfo + +//======================================================================================= +// +// Set file name of this database (makes copy of the file name). +// +// Return value: S_OK or E_OUTOFMEMORY +// +__checkReturn +HRESULT +CLiteWeightStgdbRW::SetFileName( + const WCHAR * wszFileName) +{ + HRESULT hr = S_OK; + + if (m_wszFileName != NULL) + { + delete [] m_wszFileName; + m_wszFileName = NULL; + } + + if ((wszFileName == NULL) || (*wszFileName == 0)) + { // The new file name is empty + _ASSERTE(m_wszFileName == NULL); + + // No need to allocate anything, NULL means empty name + hr = S_OK; + goto ErrExit; + } + + // Size of the file name incl. null terminator + size_t cchFileName; + cchFileName = wcslen(wszFileName) + 1; + + // Allocate and copy the file name + m_wszFileName = new (nothrow) WCHAR[cchFileName]; + IfNullGo(m_wszFileName); + wcscpy_s(m_wszFileName, cchFileName, wszFileName); + +ErrExit: + return hr; +} // CLiteWeightStgdbRW::SetFileName + +//======================================================================================= +// +// Returns TRUE if wszFileName has valid path length (MAX_PATH or 32767 if prefixed with \\?\). +// +//static +BOOL +CLiteWeightStgdbRW::IsValidFileNameLength( + const WCHAR * wszFileName) +{ + return TRUE; +} // CLiteWeightStgdbRW::IsValidFileNameLength diff --git a/src/coreclr/md/enc/mdinternalrw.cpp b/src/coreclr/md/enc/mdinternalrw.cpp new file mode 100644 index 00000000000..511e529121e --- /dev/null +++ b/src/coreclr/md/enc/mdinternalrw.cpp @@ -0,0 +1,4069 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// =========================================================================== +// File: MDInternalRW.cpp +// + +// Notes: +// +// +// =========================================================================== +#include "stdafx.h" +#include "../runtime/mdinternalro.h" +#include "../compiler/regmeta.h" +#include "../compiler/importhelper.h" +#include "mdinternalrw.h" +#include "metamodelro.h" +#include "liteweightstgdb.h" + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +__checkReturn +HRESULT _GetFixedSigOfVarArg( // S_OK or error. + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob of COM+ method signature + ULONG cbSigBlob, // [IN] size of signature + CQuickBytes *pqbSig, // [OUT] output buffer for fixed part of VarArg Signature + ULONG *pcbSigBlob); // [OUT] number of bytes written to the above output buffer + +__checkReturn +HRESULT _FillMDDefaultValue( + BYTE bType, + void const *pValue, + ULONG cbValue, + MDDefaultValue *pMDDefaultValue); + + +//***************************************************************************** +// Serve as a delegator to call ImportHelper::MergeUpdateTokenInSig. Or we will +// need to include ImportHelper into our md\runtime directory. +//***************************************************************************** +__checkReturn +HRESULT TranslateSigHelper( // S_OK or error. + IMDInternalImport* pImport, // [IN] import scope. + IMDInternalImport* pAssemImport, // [IN] import assembly scope. + const void* pbHashValue, // [IN] hash value for the import assembly. + ULONG cbHashValue, // [IN] count of bytes in the hash value. + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit* pAssemEmit, // [IN] assembly emit scope. + IMetaDataEmit* emit, // [IN] emit interface + CQuickBytes* pqkSigEmit, // [OUT] buffer to hold translated signature + ULONG* pcbSig) // [OUT] count of bytes in the translated signature +{ +#ifdef FEATURE_METADATA_EMIT + IMetaModelCommon *pCommon = pImport->GetMetaModelCommon(); + RegMeta *pAssemEmitRM = static_cast<RegMeta*>(pAssemEmit); + RegMeta *pEmitRM = static_cast<RegMeta*>(emit); + + CMiniMdRW *pMiniMdAssemEmit = pAssemEmitRM ? &pAssemEmitRM->m_pStgdb->m_MiniMd : NULL; + CMiniMdRW *pMiniMdEmit = &(pEmitRM->m_pStgdb->m_MiniMd); + IMetaModelCommon *pCommonAssemImport = pAssemImport ? pAssemImport->GetMetaModelCommon() : NULL; + + return ImportHelper::MergeUpdateTokenInSig( + pMiniMdAssemEmit, // The assembly emit scope. + pMiniMdEmit, // The emit scope. + pCommonAssemImport, // Assembly scope where the signature is from. + pbHashValue, // Hash value for the import scope. + cbHashValue, // Size in bytes. + pCommon, // The scope where signature is from. + pbSigBlob, // signature from the imported scope + NULL, // Internal OID mapping structure. + pqkSigEmit, // [OUT] translated signature + NULL, // start from first byte of the signature + NULL, // don't care how many bytes consumed + pcbSig); // [OUT] total number of bytes write to pqkSigEmit + +#else //!FEATURE_METADATA_EMIT + // This API doesn't make sense without supporting public Emit APIs + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT +} // TranslateSigHelper + + +//***************************************************************************** +// Given an IMDInternalImport on a CMiniMd[RO], convert to CMiniMdRW. +//***************************************************************************** +__checkReturn +STDAPI ConvertRO2RW( + IUnknown *pRO, // [IN] The RO interface to convert. + REFIID riid, // [IN] The interface desired. + void **ppIUnk) // [OUT] Return interface on success. +{ + HRESULT hr = S_OK; // A result. + IMDInternalImportENC *pRW = 0; // To test the RW-ness of the input iface. + MDInternalRW *pInternalRW = 0; // Gets the new RW object. + MDInternalRO *pTrustedRO = 0; + + // Avoid confusion. + *ppIUnk = 0; + + // If the interface is already RW, done, just return. + if (pRO->QueryInterface(IID_IMDInternalImportENC, (void**)&pRW) == S_OK) + { + hr = pRO->QueryInterface(riid, ppIUnk); + goto ErrExit; + } + + // Create the new RW object. + pInternalRW = new (nothrow) MDInternalRW; + IfNullGo( pInternalRW ); + + // Init from the RO object. Convert as read-only; QI will make writable if + // so needed. + + // ! QI for IID_IUnknown will return MDInternalRO*. Not that COM guarantees such a thing but MDInternalRO knows about + IfFailGo( pRO->QueryInterface(IID_IUnknown, (void**)&pTrustedRO) ); + IfFailGo( pInternalRW->InitWithRO(pTrustedRO, true)); + IfFailGo( pInternalRW->QueryInterface(riid, ppIUnk) ); + +ErrExit: + if (pRW) + pRW->Release(); + if (pTrustedRO) + pTrustedRO->Release(); + // Clean up the object and [OUT] interface on error. + if (FAILED(hr)) + { + if (pInternalRW) + delete pInternalRW; + *ppIUnk = 0; + } + else if (pInternalRW) + pInternalRW->Release(); + + return hr; +} // ConvertRO2RW + + +//***************************************************************************** +// Helper to get the internal interface with RW format +//***************************************************************************** +__checkReturn +HRESULT GetInternalWithRWFormat( + LPVOID pData, + ULONG cbData, + DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC + REFIID riid, // [in] The interface desired. + void **ppIUnk) // [out] Return interface on success. +{ + MDInternalRW *pInternalRW = NULL; + HRESULT hr; + + *ppIUnk = 0; + pInternalRW = new (nothrow) MDInternalRW; + IfNullGo( pInternalRW ); + IfFailGo( pInternalRW->Init( + const_cast<void*>(pData), + cbData, + (flags == ofRead) ? true : false) ); + IfFailGo( pInternalRW->QueryInterface(riid, ppIUnk) ); +ErrExit: + if (FAILED(hr)) + { + if (pInternalRW) + delete pInternalRW; + *ppIUnk = 0; + } + else if ( pInternalRW ) + pInternalRW->Release(); + return hr; +} // GetInternalWithRWFormat + + +//***************************************************************************** +// This function returns a IMDInternalImport interface based on the given +// public import interface i.e IMetaDataEmit or IMetaDataImport. +//***************************************************************************** +__checkReturn +STDAPI GetMDInternalInterfaceFromPublic( + IUnknown *pIUnkPublic, // [IN] Given public interface. Must be QI of IUnknown + REFIID riid, // [in] The interface desired. + void **ppIUnkInternal) // [out] Return interface on success. +{ + HRESULT hr = S_OK; + ReleaseHolder<IGetIMDInternalImport> pGetIMDInternalImport; + + // IMDInternalImport is the only internal import interface currently supported by + // this function. + _ASSERTE(riid == IID_IMDInternalImport && pIUnkPublic && ppIUnkInternal); + + if (riid != IID_IMDInternalImport || pIUnkPublic == NULL || ppIUnkInternal == NULL) + IfFailGo(E_INVALIDARG); + IfFailGo( pIUnkPublic->QueryInterface(IID_IGetIMDInternalImport, &pGetIMDInternalImport)); + IfFailGo( pGetIMDInternalImport->GetIMDInternalImport((IMDInternalImport **)ppIUnkInternal)); + +ErrExit: + if (FAILED(hr)) + { + if (ppIUnkInternal) + *ppIUnkInternal = 0; + } + return hr; +} // GetMDInternalInterfaceFromPublic + + +//***************************************************************************** +// This function returns the requested public interface based on the given +// internal import interface. It is caller's responsibility to Release ppIUnkPublic +//***************************************************************************** +__checkReturn +STDAPI GetMDPublicInterfaceFromInternal( + void *pIUnkInternal, // [IN] Given internal interface. + REFIID riid, // [in] The interface desired. + void **ppIUnkPublic) // [out] Return interface on success. +{ + HRESULT hr = S_OK; + IMDInternalImport *pInternalImport = 0;; + IUnknown *pIUnkPublic = NULL; + OptionValue optVal = { MDDupAll, MDRefToDefDefault, MDNotifyDefault, MDUpdateFull, MDErrorOutOfOrderDefault , MDThreadSafetyOn}; + RegMeta *pMeta = 0; + bool isLockedForWrite = false; + + + _ASSERTE(pIUnkInternal && ppIUnkPublic); + *ppIUnkPublic = 0; + + IfFailGo(ConvertRO2RW((IUnknown*)pIUnkInternal, IID_IMDInternalImport, (void **)&pInternalImport)); + + pIUnkPublic = pInternalImport->GetCachedPublicInterface(TRUE); + if ( pIUnkPublic ) + { + // There is already a cached public interface. GetCachedPublicInterface already AddRef the returned + // public interface. We want to QueryInterface the riid... + // We are done! + hr = pIUnkPublic->QueryInterface(riid, ppIUnkPublic); + pIUnkPublic->Release(); + goto ErrExit; + } + + // grab the write lock when we are creating the corresponding regmeta for the public interface + _ASSERTE( pInternalImport->GetReaderWriterLock() != NULL ); + isLockedForWrite = true; + IfFailGo(pInternalImport->GetReaderWriterLock()->LockWrite()); + + // check again. Maybe someone else beat us to setting the public interface while we are waiting + // for the write lock. Don't need to grab the read lock since we already have the write lock. + *ppIUnkPublic = pInternalImport->GetCachedPublicInterface(FALSE); + if ( *ppIUnkPublic ) + { + // there is already a cached public interface. GetCachedPublicInterface already AddRef the returned + // public interface. + // We are done! + goto ErrExit; + } + + pMeta = new (nothrow) RegMeta(); + IfNullGo(pMeta); + IfFailGo(pMeta->SetOption(&optVal)); + IfFailGo( pMeta->InitWithStgdb((IUnknown*)pInternalImport, ((MDInternalRW*)pInternalImport)->GetMiniStgdb()) ); + IfFailGo( pMeta->QueryInterface(riid, ppIUnkPublic) ); + + // The following makes the public object and the internal object point to each other. + _ASSERTE( pMeta->GetReaderWriterLock() == NULL ); + IfFailGo( pMeta->SetCachedInternalInterface(pInternalImport) ); + IfFailGo( pInternalImport->SetCachedPublicInterface((IUnknown *) *ppIUnkPublic) ); + IfFailGo( pMeta->SetReaderWriterLock(pInternalImport->GetReaderWriterLock() )); + + // Add the new RegMeta to the cache. + IfFailGo( pMeta->AddToCache() ); + +ErrExit: + if (isLockedForWrite) + pInternalImport->GetReaderWriterLock()->UnlockWrite(); + + if (pInternalImport) + pInternalImport->Release(); + + if (FAILED(hr)) + { + if (pMeta) + delete pMeta; + *ppIUnkPublic = 0; + } + return hr; +} // GetMDPublicInterfaceFromInternal + +//***************************************************************************** +// Converts an internal MD import API into the read/write version of this API. +// This could support edit and continue, or modification of the metadata at +// runtime (say for profiling). +//***************************************************************************** +__checkReturn +STDAPI ConvertMDInternalImport( // S_OK, S_FALSE (no conversion), or error. + IMDInternalImport *pIMD, // [in] The metadata to be updated. + IMDInternalImport **ppIMD) // [out] Put the RW here. +{ + HRESULT hr; // A result. + IMDInternalImportENC *pENC = NULL; // ENC interface on the metadata. + + _ASSERTE(pIMD != NULL); + _ASSERTE(ppIMD != NULL); + + // Test whether the MD is already RW. + hr = pIMD->QueryInterface(IID_IMDInternalImportENC, (void**)&pENC); + if (FAILED(hr)) + { // Not yet RW, so do the conversion. + IfFailGo(ConvertRO2RW(pIMD, IID_IMDInternalImport, (void**)ppIMD)); + } + else + { // Already converted; give back same pointer. + *ppIMD = pIMD; + hr = S_FALSE; + } + +ErrExit: + if (pENC) + pENC->Release(); + return hr; +} // ConvertMDInternalImport + + + + + +//***************************************************************************** +// Constructor +//***************************************************************************** +MDInternalRW::MDInternalRW() + : m_pStgdb(NULL), + m_cRefs(1), + m_fOwnStgdb(false), + m_pUnk(NULL), + m_pUserUnk(NULL), + m_pIMetaDataHelper(NULL), + m_pSemReadWrite(NULL), + m_fOwnSem(false) +{ +} // MDInternalRW::MDInternalRW + + + +//***************************************************************************** +// Destructor +//***************************************************************************** +MDInternalRW::~MDInternalRW() +{ + HRESULT hr = S_OK; + + LOCKWRITENORET(); + + // This should have worked if we've cached the internal interface in the past + _ASSERTE(SUCCEEDED(hr) || m_pIMetaDataHelper == NULL || m_pIMetaDataHelper->GetCachedInternalInterface(false) == NULL); + + + if (SUCCEEDED(hr)) + { + + if (m_pIMetaDataHelper) + { + // The internal object is going away before the public object. + // If the internal object owns the reader writer lock, transfer the ownership + // to the public object and clear the cached internal interface from the public interface. + + m_pIMetaDataHelper->SetCachedInternalInterface(NULL); + m_pIMetaDataHelper = NULL; + m_fOwnSem = false; + + } + + UNLOCKWRITE(); + } + if (m_pSemReadWrite && m_fOwnSem) + delete m_pSemReadWrite; + + if ( m_pStgdb && m_fOwnStgdb ) + { + // we own the stgdb so we need to uninit and delete it. + m_pStgdb->Uninit(); + delete m_pStgdb; + } + if ( m_pUserUnk ) + m_pUserUnk->Release(); + if ( m_pUnk ) + m_pUnk->Release(); +} // MDInternalRW::~MDInternalRW + + +//***************************************************************************** +// Set or clear the cached public interfaces. +// NOTE:: Caller should take a Write lock on the reader writer lock. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::SetCachedPublicInterface(IUnknown * pUnk) +{ + IMetaDataHelper * pHelper = NULL; + HRESULT hr = S_OK; + + if (pUnk != NULL) + { + // Internal object and public regmeta should be one to one mapping!! + _ASSERTE(m_pIMetaDataHelper == NULL); + + IfFailRet(pUnk->QueryInterface(IID_IMetaDataHelper, (void **) &pHelper)); + _ASSERTE(pHelper != NULL); + + m_pIMetaDataHelper = pHelper; + pHelper->Release(); + } + else + { + // public object is going away before the internal object. If we don't own the + // reader writer lock, just take over the ownership. + m_fOwnSem = true; + m_pIMetaDataHelper = NULL; + } + return hr; +} // MDInternalRW::SetCachedPublicInterface + + +//***************************************************************************** +// Clear the cached public interfaces. +//***************************************************************************** +IUnknown * MDInternalRW::GetCachedPublicInterface(BOOL fWithLock) +{ + HRESULT hr = S_OK; + IUnknown * pRet = NULL; + if (fWithLock) + { + LOCKREAD(); + + pRet = m_pIMetaDataHelper; + if (pRet != NULL) + pRet->AddRef(); + } + else + { + pRet = m_pIMetaDataHelper; + if (pRet != NULL) + pRet->AddRef(); + } + +ErrExit: + return pRet; +} // MDInternalRW::GetCachedPublicInterface + + +//***************************************************************************** +// Get the Reader-Writer lock +//***************************************************************************** +UTSemReadWrite * MDInternalRW::GetReaderWriterLock() +{ + return getReaderWriterLock(); +} // MDInternalRW::GetReaderWriterLock + +//***************************************************************************** +// IUnknown +//***************************************************************************** +ULONG MDInternalRW::AddRef() +{ + return InterlockedIncrement(&m_cRefs); +} // MDInternalRW::AddRef + +ULONG MDInternalRW::Release() +{ + ULONG cRef; + + cRef = InterlockedDecrement(&m_cRefs); + if (cRef == 0) + { + LOG((LOGMD, "MDInternalRW(0x%08x)::destruction\n", this)); + delete this; + } + return cRef; +} // MDInternalRW::Release + +__checkReturn +HRESULT MDInternalRW::QueryInterface(REFIID riid, void **ppUnk) +{ + *ppUnk = 0; + + if (riid == IID_IUnknown) + *ppUnk = (IUnknown *) (IMDInternalImport *) this; + + else if (riid == IID_IMDInternalImport) + *ppUnk = (IMDInternalImport *) this; + + else if (riid == IID_IMDInternalImportENC) + *ppUnk = (IMDInternalImportENC *) this; + + else if (riid == IID_IMDCommon) + *ppUnk = (IMDCommon*)this; + + else + return E_NOINTERFACE; + AddRef(); + return S_OK; +} // MDInternalRW::QueryInterface + + +//***************************************************************************** +// Initialize +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::Init( + LPVOID pData, // points to meta data section in memory + ULONG cbData, // count of bytes in pData + int bReadOnly) // Is it open for read only? +{ + CLiteWeightStgdbRW * pStgdb = NULL; + HRESULT hr = NOERROR; + OptionValue optVal = { MDDupAll, MDRefToDefDefault, MDNotifyDefault, MDUpdateFull, MDErrorOutOfOrderDefault, MDThreadSafetyOn }; + + pStgdb = new (nothrow) CLiteWeightStgdbRW; + IfNullGo(pStgdb); + + m_pSemReadWrite = new (nothrow) UTSemReadWrite; + IfNullGo(m_pSemReadWrite); + IfFailGo(m_pSemReadWrite->Init()); + m_fOwnSem = true; + INDEBUG(pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + + IfFailGo(pStgdb->InitOnMem(cbData, (BYTE*)pData, bReadOnly)); + IfFailGo(pStgdb->m_MiniMd.SetOption(&optVal)); + m_tdModule = COR_GLOBAL_PARENT_TOKEN; + m_fOwnStgdb = true; + m_pStgdb = pStgdb; + +ErrExit: + // clean up upon errors + if (FAILED(hr) && (pStgdb != NULL)) + { + delete pStgdb; + } + return hr; +} // MDInternalRW::Init + + +//***************************************************************************** +// Initialize with an existing RegMeta. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::InitWithStgdb( + IUnknown *pUnk, // The IUnknow that owns the life time for the existing stgdb + CLiteWeightStgdbRW *pStgdb) // existing lightweight stgdb +{ + // m_fOwnSem should be false because this is the case where we create the internal interface given a public + // interface. + + m_tdModule = COR_GLOBAL_PARENT_TOKEN; + m_fOwnStgdb = false; + m_pStgdb = pStgdb; + + // remember the owner of the light weight stgdb + // AddRef it to ensure the lifetime + // + m_pUnk = pUnk; + m_pUnk->AddRef(); + return NOERROR; +} // MDInternalRW::InitWithStgdb + + +//***************************************************************************** +// Initialize with an existing RO format +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::InitWithRO( + MDInternalRO * pRO, + int bReadOnly) +{ + CLiteWeightStgdbRW * pStgdb = NULL; + HRESULT hr = NOERROR; + OptionValue optVal = { MDDupAll, MDRefToDefDefault, MDNotifyDefault, MDUpdateFull, MDErrorOutOfOrderDefault, MDThreadSafetyOn }; + + pStgdb = new (nothrow) CLiteWeightStgdbRW; + IfNullGo(pStgdb); + + m_pSemReadWrite = new (nothrow) UTSemReadWrite; + IfNullGo(m_pSemReadWrite); + IfFailGo(m_pSemReadWrite->Init()); + m_fOwnSem = true; + INDEBUG(pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + + IfFailGo(pStgdb->m_MiniMd.InitOnRO(&pRO->m_LiteWeightStgdb.m_MiniMd, bReadOnly)); + IfFailGo(pStgdb->m_MiniMd.SetOption(&optVal)); + m_tdModule = COR_GLOBAL_PARENT_TOKEN; + m_fOwnStgdb = true; + pStgdb->m_pvMd=pRO->m_LiteWeightStgdb.m_pvMd; + pStgdb->m_cbMd=pRO->m_LiteWeightStgdb.m_cbMd; + m_pStgdb = pStgdb; + +ErrExit: + // clean up upon errors + if (FAILED(hr) && (pStgdb != NULL)) + { + delete pStgdb; + } + return hr; +} // MDInternalRW::InitWithRO + + +#ifndef DACCESS_COMPILE +//***************************************************************************** +// Given a scope, determine whether imported from a typelib. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::TranslateSigWithScope( + IMDInternalImport* pAssemImport, // [IN] import assembly scope. + const void* pbHashValue, // [IN] hash value for the import assembly. + ULONG cbHashValue, // [IN] count of bytes in the hash value. + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit* pAssemEmit, // [IN] assembly emit scope. + IMetaDataEmit* emit, // [IN] emit interface + CQuickBytes* pqkSigEmit, // [OUT] buffer to hold translated signature + ULONG* pcbSig) // [OUT] count of bytes in the translated signature +{ + return TranslateSigHelper( + this, + pAssemImport, + pbHashValue, + cbHashValue, + pbSigBlob, + cbSigBlob, + pAssemEmit, + emit, + pqkSigEmit, + pcbSig); +} // MDInternalRW::TranslateSigWithScope + +__checkReturn +HRESULT MDInternalRW::GetTypeDefRefTokenInTypeSpec(// return S_FALSE if enclosing type does not have a token + mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at + mdToken *tkEnclosedToken) // [OUT] The enclosed type token +{ + return m_pStgdb->m_MiniMd.GetTypeDefRefTokenInTypeSpec(tkTypeSpec, tkEnclosedToken); +}// MDInternalRW::GetTypeDefRefTokenInTypeSpec + +//***************************************************************************** +// Given a scope, return the number of tokens in a given table +//***************************************************************************** +ULONG MDInternalRW::GetCountWithTokenKind( // return hresult + DWORD tkKind) // [IN] pass in the kind of token. +{ + ULONG ulCount = 0; + HRESULT hr = S_OK; + LOCKREAD(); + + switch (tkKind) + { + case mdtTypeDef: + ulCount = m_pStgdb->m_MiniMd.getCountTypeDefs(); + // Remove global typedef from the count of typedefs (and handle the case where there is no global typedef) + if (ulCount > 0) + ulCount--; + break; + case mdtTypeRef: + ulCount = m_pStgdb->m_MiniMd.getCountTypeRefs(); + break; + case mdtMethodDef: + ulCount = m_pStgdb->m_MiniMd.getCountMethods(); + break; + case mdtFieldDef: + ulCount = m_pStgdb->m_MiniMd.getCountFields(); + break; + case mdtMemberRef: + ulCount = m_pStgdb->m_MiniMd.getCountMemberRefs(); + break; + case mdtInterfaceImpl: + ulCount = m_pStgdb->m_MiniMd.getCountInterfaceImpls(); + break; + case mdtParamDef: + ulCount = m_pStgdb->m_MiniMd.getCountParams(); + break; + case mdtFile: + ulCount = m_pStgdb->m_MiniMd.getCountFiles(); + break; + case mdtAssemblyRef: + ulCount = m_pStgdb->m_MiniMd.getCountAssemblyRefs(); + break; + case mdtAssembly: + ulCount = m_pStgdb->m_MiniMd.getCountAssemblys(); + break; + case mdtCustomAttribute: + ulCount = m_pStgdb->m_MiniMd.getCountCustomAttributes(); + break; + case mdtModule: + ulCount = m_pStgdb->m_MiniMd.getCountModules(); + break; + case mdtPermission: + ulCount = m_pStgdb->m_MiniMd.getCountDeclSecuritys(); + break; + case mdtSignature: + ulCount = m_pStgdb->m_MiniMd.getCountStandAloneSigs(); + break; + case mdtEvent: + ulCount = m_pStgdb->m_MiniMd.getCountEvents(); + break; + case mdtProperty: + ulCount = m_pStgdb->m_MiniMd.getCountPropertys(); + break; + case mdtModuleRef: + ulCount = m_pStgdb->m_MiniMd.getCountModuleRefs(); + break; + case mdtTypeSpec: + ulCount = m_pStgdb->m_MiniMd.getCountTypeSpecs(); + break; + case mdtExportedType: + ulCount = m_pStgdb->m_MiniMd.getCountExportedTypes(); + break; + case mdtManifestResource: + ulCount = m_pStgdb->m_MiniMd.getCountManifestResources(); + break; + case mdtGenericParam: + ulCount = m_pStgdb->m_MiniMd.getCountGenericParams(); + break; + case mdtGenericParamConstraint: + ulCount = m_pStgdb->m_MiniMd.getCountGenericParamConstraints(); + break; + case mdtMethodSpec: + ulCount = m_pStgdb->m_MiniMd.getCountMethodSpecs(); + break; + default: +#ifdef _DEBUG + if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) + _ASSERTE(!"Invalid Blob Offset"); +#endif + ulCount = 0; + break; + } + +ErrExit: + + return ulCount; +} // MDInternalRW::GetCountWithTokenKind +#endif //!DACCESS_COMPILE + + +//******************************************************************************* +// Enumerator helpers +//******************************************************************************* + + +//***************************************************************************** +// enumerator init for typedef +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::EnumTypeDefInit( // return hresult + HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data +{ + HRESULT hr = NOERROR; + LOCKREAD(); + + _ASSERTE(phEnum); + + HENUMInternal::ZeroEnum(phEnum); + phEnum->m_tkKind = mdtTypeDef; + + if ( m_pStgdb->m_MiniMd.HasDelete() ) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + + phEnum->m_tkKind = mdtTypeDef; + for (ULONG index = 2; index <= m_pStgdb->m_MiniMd.getCountTypeDefs(); index ++ ) + { + TypeDefRec *pTypeDefRec; + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(index, &pTypeDefRec)); + LPCSTR szTypeDefName; + IfFailGo(m_pStgdb->m_MiniMd.getNameOfTypeDef(pTypeDefRec, &szTypeDefName)); + if (IsDeletedName(szTypeDefName)) + { + continue; + } + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(index, mdtTypeDef) ) ); + } + } + else + { + phEnum->m_EnumType = MDSimpleEnum; + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeDefs(); + + // Skip over the global model typedef + // + // phEnum->u.m_ulCur : the current rid that is not yet enumerated + // phEnum->u.m_ulStart : the first rid that will be returned by enumerator + // phEnum->u.m_ulEnd : the last rid that will be returned by enumerator + phEnum->u.m_ulStart = phEnum->u.m_ulCur = 2; + phEnum->u.m_ulEnd = phEnum->m_ulCount + 1; + if (phEnum->m_ulCount > 0) + phEnum->m_ulCount --; + } +ErrExit: + + return hr; +} // MDInternalRW::EnumTypeDefInit + +#ifndef DACCESS_COMPILE +//***************************************************************************** +// enumerator init for MethodImpl +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::EnumMethodImplInit( // return hresult + mdTypeDef td, // [IN] TypeDef over which to scope the enumeration. + HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens. + HENUMInternal *phEnumDecl) // [OUT] buffer to fill for enumerator data for MethodDecl tokens. +{ + HRESULT hr = NOERROR; + int ridCur; + mdToken tkMethodBody; + mdToken tkMethodDecl; + MethodImplRec *pRec; + HENUMInternal hEnum; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && !IsNilToken(td)); + _ASSERTE(phEnumBody && phEnumDecl); + + HENUMInternal::ZeroEnum(phEnumBody); + HENUMInternal::ZeroEnum(phEnumDecl); + HENUMInternal::ZeroEnum(&hEnum); + + HENUMInternal::InitDynamicArrayEnum(phEnumBody); + HENUMInternal::InitDynamicArrayEnum(phEnumDecl); + + phEnumBody->m_tkKind = (TBL_MethodImpl << 24); + phEnumDecl->m_tkKind = (TBL_MethodImpl << 24); + + // Get the range of rids. + IfFailGo( m_pStgdb->m_MiniMd.FindMethodImplHelper(td, &hEnum) ); + + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur)) + { + // Get the MethodBody and MethodDeclaration tokens for the current + // MethodImpl record. + IfFailGo(m_pStgdb->m_MiniMd.GetMethodImplRecord(ridCur, &pRec)); + tkMethodBody = m_pStgdb->m_MiniMd.getMethodBodyOfMethodImpl(pRec); + tkMethodDecl = m_pStgdb->m_MiniMd.getMethodDeclarationOfMethodImpl(pRec); + + // Add the Method body/declaration pairs to the Enum + IfFailGo( HENUMInternal::AddElementToEnum(phEnumBody, tkMethodBody ) ); + IfFailGo( HENUMInternal::AddElementToEnum(phEnumDecl, tkMethodDecl ) ); + } +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + return hr; +} // MDInternalRW::EnumMethodImplInit + +//***************************************************************************** +// get the number of MethodImpls in a scope +//***************************************************************************** +ULONG MDInternalRW::EnumMethodImplGetCount( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. +{ + _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl && + (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl); + _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum && + phEnumDecl->m_EnumType == MDDynamicArrayEnum); + _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount); + + return phEnumBody->m_ulCount; +} // MDInternalRW::EnumMethodImplGetCount + + +//***************************************************************************** +// enumerator for MethodImpl. +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::EnumMethodImplNext( // return hresult + HENUMInternal *phEnumBody, // [IN] input enum for MethodBody + HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl + mdToken *ptkBody, // [OUT] return token for MethodBody + mdToken *ptkDecl) // [OUT] return token for MethodDecl +{ + _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl && + (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl); + _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum && + phEnumDecl->m_EnumType == MDDynamicArrayEnum); + _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount); + _ASSERTE(ptkBody && ptkDecl); + + EnumNext(phEnumBody, ptkBody); + return EnumNext(phEnumDecl, ptkDecl) ? S_OK : S_FALSE; +} // MDInternalRW::EnumMethodImplNext + +//***************************************** +// Reset the enumerator to the beginning. +//***************************************** +void MDInternalRW::EnumMethodImplReset( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. +{ + _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl && + (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl); + _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum && + phEnumDecl->m_EnumType == MDDynamicArrayEnum); + _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount); + + EnumReset(phEnumBody); + EnumReset(phEnumDecl); +} // MDInternalRW::EnumMethodImplReset + + +//***************************************** +// Close the enumerator. +//***************************************** +void MDInternalRW::EnumMethodImplClose( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. +{ + _ASSERTE((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl && + (phEnumDecl->m_tkKind >> 24) == TBL_MethodImpl); + _ASSERTE(phEnumBody->m_EnumType == MDDynamicArrayEnum && + phEnumDecl->m_EnumType == MDDynamicArrayEnum); + _ASSERTE(phEnumBody->m_ulCount == phEnumDecl->m_ulCount); + + EnumClose(phEnumBody); + EnumClose(phEnumDecl); +} // MDInternalRW::EnumMethodImplClose +#endif //!DACCESS_COMPILE + +//****************************************************************************** +// enumerator for global functions +//****************************************************************************** +__checkReturn +HRESULT MDInternalRW::EnumGlobalFunctionsInit( // return hresult + HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data +{ + return EnumInit(mdtMethodDef, m_tdModule, phEnum); +} // MDInternalRW::EnumGlobalFunctionsInit + + +//****************************************************************************** +// enumerator for global fields +//****************************************************************************** +__checkReturn +HRESULT MDInternalRW::EnumGlobalFieldsInit( // return hresult + HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data +{ + return EnumInit(mdtFieldDef, m_tdModule, phEnum); +} // MDInternalRW::EnumGlobalFieldsInit + + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +//***************************************** +// Enumerator initializer +//***************************************** +__checkReturn +HRESULT MDInternalRW::EnumInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + mdToken tkParent, // [IN] token to scope the search + HENUMInternal *phEnum) // [OUT] the enumerator to fill +{ + HRESULT hr = S_OK; + ULONG ulStart, ulEnd, ulMax; + ULONG index; + LOCKREAD(); + + // Vars for query. + _ASSERTE(phEnum); + HENUMInternal::ZeroEnum(phEnum); + + // cache the tkKind and the scope + phEnum->m_tkKind = TypeFromToken(tkKind); + + TypeDefRec *pRec; + + phEnum->m_EnumType = MDSimpleEnum; + + switch (TypeFromToken(tkKind)) + { + case mdtFieldDef: + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec)); + ulStart = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(tkParent), &ulEnd)); + if ( m_pStgdb->m_MiniMd.HasDelete() ) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + FieldRec *pFieldRec; + RID fieldRid; + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &fieldRid)); + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRecord(index, &pFieldRec)); + LPCSTR szFieldName; + IfFailGo(m_pStgdb->m_MiniMd.getNameOfField(pFieldRec, &szFieldName)); + if (IsFdRTSpecialName(pFieldRec->GetFlags()) && IsDeletedName(szFieldName) ) + { + continue; + } + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &fieldRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(fieldRid, mdtFieldDef))); + } + } + else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Field)) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + RID fieldRid; + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(index, &fieldRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(fieldRid, mdtFieldDef))); + } + } + else + { + HENUMInternal::InitSimpleEnum( mdtFieldDef, ulStart, ulEnd, phEnum); + } + break; + + case mdtMethodDef: + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec)); + ulStart = m_pStgdb->m_MiniMd.getMethodListOfTypeDef(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(tkParent), &ulEnd)); + if ( m_pStgdb->m_MiniMd.HasDelete() ) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + MethodRec *pMethodRec; + RID methodRid; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(index, &methodRid)); + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(methodRid, &pMethodRec)); + LPCSTR szMethodName; + IfFailGo(m_pStgdb->m_MiniMd.getNameOfMethod(pMethodRec, &szMethodName)); + if (IsMdRTSpecialName(pMethodRec->GetFlags()) && IsDeletedName(szMethodName)) + { + continue; + } + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(index, &methodRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(methodRid, mdtMethodDef))); + } + } + else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Method)) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + RID methodRid; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRid(index, &methodRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(methodRid, mdtMethodDef))); + } + } + else + { + HENUMInternal::InitSimpleEnum( mdtMethodDef, ulStart, ulEnd, phEnum); + } + break; + + case mdtInterfaceImpl: + if (!m_pStgdb->m_MiniMd.IsSorted(TBL_InterfaceImpl) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_InterfaceImpl)) + { + // virtual sort table will be created! + // + CONVERT_READ_TO_WRITE_LOCK(); + } + + IfFailGo( m_pStgdb->m_MiniMd.GetInterfaceImplsForTypeDef(RidFromToken(tkParent), &ulStart, &ulEnd) ); + if ( m_pStgdb->m_MiniMd.IsSorted( TBL_InterfaceImpl ) ) + { + // These are index to InterfaceImpl table directly + HENUMInternal::InitSimpleEnum( mdtInterfaceImpl, ulStart, ulEnd, phEnum); + } + else + { + // These are index to VirtualSort table. Skip over one level direction. + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(m_pStgdb->m_MiniMd.GetInterfaceImplRid(index), mdtInterfaceImpl) ) ); + } + } + break; + + case mdtGenericParam: + //@todo: deal with non-sorted case. + + if (TypeFromToken(tkParent) == mdtTypeDef) + { + IfFailGo(m_pStgdb->m_MiniMd.getGenericParamsForTypeDef( + RidFromToken(tkParent), + &phEnum->u.m_ulEnd, + &(phEnum->u.m_ulStart))); + } + else + { + IfFailGo(m_pStgdb->m_MiniMd.getGenericParamsForMethodDef( + RidFromToken(tkParent), + &phEnum->u.m_ulEnd, + &(phEnum->u.m_ulStart))); + } + break; + + case mdtGenericParamConstraint: + if ( !m_pStgdb->m_MiniMd.IsSorted(TBL_GenericParamConstraint) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_GenericParamConstraint)) + { + // virtual sort table will be created! + // + CONVERT_READ_TO_WRITE_LOCK(); + } + + IfFailGo( m_pStgdb->m_MiniMd.GetGenericParamConstraintsForToken(RidFromToken(tkParent), &ulStart, &ulEnd) ); + if ( m_pStgdb->m_MiniMd.IsSorted( TBL_GenericParamConstraint ) ) + { + // These are index to GenericParamConstraint table directly + HENUMInternal::InitSimpleEnum( mdtGenericParamConstraint, ulStart, ulEnd, phEnum); + } + else + { + // These are index to VirtualSort table. Skip over one level direction. + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(m_pStgdb->m_MiniMd.GetGenericParamConstraintRid(index), mdtGenericParamConstraint) ) ); + } + } + break; + + case mdtProperty: + RID ridPropertyMap; + PropertyMapRec *pPropertyMapRec; + + // get the starting/ending rid of properties of this typedef + IfFailGo(m_pStgdb->m_MiniMd.FindPropertyMapFor(RidFromToken(tkParent), &ridPropertyMap)); + if (!InvalidRid(ridPropertyMap)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ulStart = m_pStgdb->m_MiniMd.getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &ulEnd)); + ulMax = m_pStgdb->m_MiniMd.getCountPropertys() + 1; + if(ulStart == 0) ulStart = 1; + if(ulEnd > ulMax) ulEnd = ulMax; + if(ulStart > ulEnd) ulStart = ulEnd; + if ( m_pStgdb->m_MiniMd.HasDelete() ) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + PropertyRec *pPropertyRec; + RID propertyRid; + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(index, &propertyRid)); + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord( + propertyRid, + &pPropertyRec)); + LPCSTR szPropertyName; + IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pPropertyRec, &szPropertyName)); + if (IsPrRTSpecialName(pPropertyRec->GetPropFlags()) && IsDeletedName(szPropertyName)) + { + continue; + } + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(index, &propertyRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(propertyRid, mdtProperty))); + } + } + else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Property)) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + RID propertyRid; + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(index, &propertyRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(propertyRid, mdtProperty))); + } + } + else + { + HENUMInternal::InitSimpleEnum( mdtProperty, ulStart, ulEnd, phEnum); + } + } + break; + + case mdtEvent: + RID ridEventMap; + EventMapRec *pEventMapRec; + + // get the starting/ending rid of events of this typedef + IfFailGo(m_pStgdb->m_MiniMd.FindEventMapFor(RidFromToken(tkParent), &ridEventMap)); + if (!InvalidRid(ridEventMap)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetEventMapRecord(ridEventMap, &pEventMapRec)); + ulStart = m_pStgdb->m_MiniMd.getEventListOfEventMap(pEventMapRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndEventListOfEventMap(ridEventMap, &ulEnd)); + ulMax = m_pStgdb->m_MiniMd.getCountEvents() + 1; + if(ulStart == 0) ulStart = 1; + if(ulEnd > ulMax) ulEnd = ulMax; + if(ulStart > ulEnd) ulStart = ulEnd; + if ( m_pStgdb->m_MiniMd.HasDelete() ) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + EventRec *pEventRec; + RID eventRid; + IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(index, &eventRid)); + IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(eventRid, &pEventRec)); + LPCSTR szEventName; + IfFailGo(m_pStgdb->m_MiniMd.getNameOfEvent(pEventRec, &szEventName)); + if (IsEvRTSpecialName(pEventRec->GetEventFlags()) && IsDeletedName(szEventName)) + { + continue; + } + IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(index, &eventRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(eventRid, mdtEvent))); + } + } + else if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Event)) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + RID eventRid; + IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(index, &eventRid)); + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(eventRid, mdtEvent) ) ); + } + } + else + { + HENUMInternal::InitSimpleEnum( mdtEvent, ulStart, ulEnd, phEnum); + } + } + break; + + case mdtParamDef: + _ASSERTE(TypeFromToken(tkParent) == mdtMethodDef); + + MethodRec *pMethodRec; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkParent), &pMethodRec)); + + // figure out the start rid and end rid of the parameter list of this methoddef + ulStart = m_pStgdb->m_MiniMd.getParamListOfMethod(pMethodRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(tkParent), &ulEnd)); + if (m_pStgdb->m_MiniMd.HasIndirectTable(TBL_Param)) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + RID paramRid; + IfFailGo(m_pStgdb->m_MiniMd.GetParamRid(index, ¶mRid)); + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(paramRid, mdtParamDef))); + } + } + else + { + HENUMInternal::InitSimpleEnum( mdtParamDef, ulStart, ulEnd, phEnum); + } + break; + + case mdtCustomAttribute: + if (!m_pStgdb->m_MiniMd.IsSorted(TBL_CustomAttribute) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_CustomAttribute)) + { + // CA's map table table will be sorted! + // + CONVERT_READ_TO_WRITE_LOCK(); + } + + IfFailGo( m_pStgdb->m_MiniMd.GetCustomAttributeForToken(tkParent, &ulStart, &ulEnd) ); + if ( m_pStgdb->m_MiniMd.IsSorted( TBL_CustomAttribute ) ) + { + // These are index to CustomAttribute table directly + HENUMInternal::InitSimpleEnum( mdtCustomAttribute, ulStart, ulEnd, phEnum); + } + else + { + // These are index to VirtualSort table. Skip over one level direction. + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = ulStart; index < ulEnd; index ++ ) + { + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(m_pStgdb->m_MiniMd.GetCustomAttributeRid(index), mdtCustomAttribute) ) ); + } + } + break; + case mdtAssemblyRef: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountAssemblyRefs() + 1; + break; + case mdtFile: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountFiles() + 1; + break; + case mdtExportedType: + _ASSERTE(IsNilToken(tkParent)); + if ( m_pStgdb->m_MiniMd.HasDelete() ) + { + HENUMInternal::InitDynamicArrayEnum(phEnum); + + phEnum->m_tkKind = mdtExportedType; + for (ULONG typeindex = 1; typeindex <= m_pStgdb->m_MiniMd.getCountExportedTypes(); typeindex ++ ) + { + ExportedTypeRec *pExportedTypeRec; + IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(typeindex, &pExportedTypeRec)); + LPCSTR szTypeName; + IfFailGo(m_pStgdb->m_MiniMd.getTypeNameOfExportedType(pExportedTypeRec, &szTypeName)); + if (IsDeletedName(szTypeName)) + { + continue; + } + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(typeindex, mdtExportedType) ) ); + } + } + else + { + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountExportedTypes() + 1; + } + break; + case mdtManifestResource: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountManifestResources() + 1; + break; + case mdtModuleRef: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_pStgdb->m_MiniMd.getCountModuleRefs() + 1; + break; + default: + _ASSERTE(!"ENUM INIT not implemented for the uncompressed format!"); + IfFailGo(E_NOTIMPL); + break; + } + + // If the count is negative, the metadata is corrupted somehow. + if (phEnum->u.m_ulEnd < phEnum->u.m_ulStart) + IfFailGo(CLDB_E_FILE_CORRUPT); + + phEnum->m_ulCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart; + phEnum->u.m_ulCur = phEnum->u.m_ulStart; +ErrExit: + // we are done + + return hr; +} // MDInternalRW::EnumInit +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************** +// Enumerator initializer +//***************************************** +__checkReturn +HRESULT MDInternalRW::EnumAllInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + HENUMInternal *phEnum) // [OUT] the enumerator to fill +{ + HRESULT hr = S_OK; + LOCKREAD(); + + // Vars for query. + _ASSERTE(phEnum); + HENUMInternal::ZeroEnum(phEnum); + + // cache the tkKind and the scope + phEnum->m_tkKind = TypeFromToken(tkKind); + phEnum->m_EnumType = MDSimpleEnum; + + switch (TypeFromToken(tkKind)) + { + case mdtTypeRef: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeRefs(); + break; + + case mdtMemberRef: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountMemberRefs(); + break; + + case mdtSignature: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountStandAloneSigs(); + break; + + case mdtMethodDef: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountMethods(); + break; + + case mdtMethodSpec: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountMethodSpecs(); + break; + + case mdtFieldDef: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountFields(); + break; + + case mdtTypeSpec: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeSpecs(); + break; + + case mdtAssemblyRef: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountAssemblyRefs(); + break; + + case mdtModuleRef: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountModuleRefs(); + break; + + case mdtTypeDef: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountTypeDefs(); + break; + + case mdtFile: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountFiles(); + break; + + case mdtCustomAttribute: + phEnum->m_ulCount = m_pStgdb->m_MiniMd.getCountCustomAttributes(); + break; + + default: + _ASSERTE(!"Bad token kind!"); + break; + } + phEnum->u.m_ulStart = phEnum->u.m_ulCur = 1; + phEnum->u.m_ulEnd = phEnum->m_ulCount + 1; + +ErrExit: + // we are done + + return hr; +} // MDInternalRW::EnumAllInit + +//***************************************** +// Enumerator initializer for CustomAttributes +//***************************************** +__checkReturn +HRESULT MDInternalRW::EnumCustomAttributeByNameInit(// return S_FALSE if record not found + mdToken tkParent, // [IN] token to scope the search + LPCSTR szName, // [IN] CustomAttribute's name to scope the search + HENUMInternal *phEnum) // [OUT] the enumerator to fill +{ + return m_pStgdb->m_MiniMd.CommonEnumCustomAttributeByName(tkParent, szName, false, phEnum); +} // MDInternalRW::EnumCustomAttributeByNameInit + +//***************************************** +// Nagivator helper to navigate back to the parent token given a token. +// For example, given a memberdef token, it will return the containing typedef. +// +// the mapping is as following: +// ---given child type---------parent type +// mdMethodDef mdTypeDef +// mdFieldDef mdTypeDef +// mdInterfaceImpl mdTypeDef +// mdParam mdMethodDef +// mdProperty mdTypeDef +// mdEvent mdTypeDef +// +//***************************************** +__checkReturn +HRESULT MDInternalRW::GetParentToken( + mdToken tkChild, // [IN] given child token + mdToken *ptkParent) // [OUT] returning parent +{ + HRESULT hr = NOERROR; + LOCKREAD(); + + _ASSERTE(ptkParent); + + switch (TypeFromToken(tkChild)) + { + case mdtTypeDef: + { + RID rid; + if (!m_pStgdb->m_MiniMd.IsSorted(TBL_NestedClass) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_NestedClass)) + { + // NestedClass table is not sorted. + CONVERT_READ_TO_WRITE_LOCK(); + } + IfFailGo(m_pStgdb->m_MiniMd.FindNestedClassFor(RidFromToken(tkChild), &rid)); + + if (InvalidRid(rid)) + { + // If not found, the *ptkParent has to be left unchanged! (callers depend on that) + hr = S_OK; + } + else + { + NestedClassRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetNestedClassRecord(rid, &pRecord)); + *ptkParent = m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord); + } + break; + } + case mdtMethodDef: + IfFailGo(m_pStgdb->m_MiniMd.FindParentOfMethodHelper(RidFromToken(tkChild), ptkParent)); + RidToToken(*ptkParent, mdtTypeDef); + break; + case mdtMethodSpec: + { + MethodSpecRec *pRec; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodSpecRecord(RidFromToken(tkChild), &pRec)); + *ptkParent = m_pStgdb->m_MiniMd.getMethodOfMethodSpec(pRec); + } + break; + case mdtFieldDef: + IfFailGo(m_pStgdb->m_MiniMd.FindParentOfFieldHelper(RidFromToken(tkChild), ptkParent)); + RidToToken(*ptkParent, mdtTypeDef); + break; + case mdtParamDef: + IfFailGo(m_pStgdb->m_MiniMd.FindParentOfParamHelper(RidFromToken(tkChild), ptkParent)); + RidToToken(*ptkParent, mdtMethodDef); + break; + case mdtMemberRef: + { + MemberRefRec *pRec; + IfFailGo(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(tkChild), &pRec)); + *ptkParent = m_pStgdb->m_MiniMd.getClassOfMemberRef(pRec); + break; + } + case mdtCustomAttribute: + { + CustomAttributeRec *pRec; + IfFailGo(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkChild), &pRec)); + *ptkParent = m_pStgdb->m_MiniMd.getParentOfCustomAttribute(pRec); + break; + } + case mdtEvent: + IfFailGo(m_pStgdb->m_MiniMd.FindParentOfEventHelper(tkChild, ptkParent)); + break; + case mdtProperty: + IfFailGo(m_pStgdb->m_MiniMd.FindParentOfPropertyHelper(tkChild, ptkParent)); + break; + default: + _ASSERTE(!"NYI: for compressed format!"); + break; + } +ErrExit: + return hr; +} // MDInternalRW::GetParentToken + +//***************************************************************************** +// Get information about a CustomAttribute. +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetCustomAttributeProps( // S_OK or error. + mdCustomAttribute at, // The attribute. + mdToken *pTkType) // Put attribute type here. +{ + HRESULT hr; + // Getting the custom value prop with a token, no need to lock! + + _ASSERTE(TypeFromToken(at) == mdtCustomAttribute); + + // Do a linear search on compressed version as we do not want to + // depend on ICR. + // + CustomAttributeRec *pCustomAttributeRec; + + IfFailRet(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(at), &pCustomAttributeRec)); + *pTkType = m_pStgdb->m_MiniMd.getTypeOfCustomAttribute(pCustomAttributeRec); + return S_OK; +} // MDInternalRW::GetCustomAttributeProps + + +//***************************************************************************** +// return custom value +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetCustomAttributeAsBlob( + mdCustomAttribute cv, // [IN] given custom attribute token + void const **ppBlob, // [OUT] return the pointer to internal blob + ULONG *pcbSize) // [OUT] return the size of the blob +{ + // Getting the custom value prop with a token, no need to lock! + HRESULT hr; + _ASSERTE(ppBlob && pcbSize && TypeFromToken(cv) == mdtCustomAttribute); + + CustomAttributeRec *pCustomAttributeRec; + + IfFailRet(m_pStgdb->m_MiniMd.GetCustomAttributeRecord(RidFromToken(cv), &pCustomAttributeRec)); + + IfFailRet(m_pStgdb->m_MiniMd.getValueOfCustomAttribute(pCustomAttributeRec, reinterpret_cast<const BYTE **>(ppBlob), pcbSize)); + return S_OK; +} // MDInternalRW::GetCustomAttributeAsBlob + +//***************************************************************************** +// Helper function to lookup and retrieve a CustomAttribute. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + __deref_out_bcount(*pcbData) const void **ppData, // [OUT] Put pointer to data here. + __out ULONG *pcbData) // [OUT] Put size of data here. +{ + HRESULT hr = S_OK; + LOCKREADIFFAILRET(); + return m_pStgdb->m_MiniMd.CommonGetCustomAttributeByName(tkObj, szName, ppData, pcbData); +} // MDInternalRW::GetCustomAttributeByName + +//***************************************************************************** +// return the name of a custom attribute +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetNameOfCustomAttribute( // S_OK or error. + mdCustomAttribute mdAttribute, // [IN] The Custom Attribute + LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute. + LPCUTF8 *pszName) // [OUT] Name of Custom Attribute. +{ + HRESULT hr = S_OK; + LOCKREADIFFAILRET(); + hr = m_pStgdb->m_MiniMd.CommonGetNameOfCustomAttribute(RidFromToken(mdAttribute), pszNamespace, pszName); + return (hr == S_FALSE) ? E_FAIL : hr; +} // MDInternalRW::GetNameOfCustomAttribute + +//***************************************************************************** +// return scope properties +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetScopeProps( + LPCSTR *pszName, // [OUT] scope name + GUID *pmvid) // [OUT] version id +{ + HRESULT hr = S_OK; + LOCKREAD(); + + ModuleRec *pModuleRec; + + // there is only one module record + IfFailGo(m_pStgdb->m_MiniMd.GetModuleRecord(1, &pModuleRec)); + + if (pmvid != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getMvidOfModule(pModuleRec, pmvid)); + } + + if (pszName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfModule(pModuleRec, pszName)); + } + +ErrExit: + return hr; +} // MDInternalRW::GetScopeProps + +//***************************************************************************** +// This function gets the "built for" version of a metadata scope. +// NOTE: if the scope has never been saved, it will not have a built-for +// version, and an empty string will be returned. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetVersionString( // S_OK or error. + LPCSTR *pVer) // [OUT] Put version string here. +{ + HRESULT hr = NOERROR; + + if (m_pStgdb->m_pvMd != NULL) + { + // For convenience, get a pointer to the version string. + // @todo: get from alternate locations when there is no STOREAGESIGNATURE. + *pVer = reinterpret_cast<const char*>(reinterpret_cast<const STORAGESIGNATURE*>(m_pStgdb->m_pvMd)->pVersion); + } + else + { // No string. + *pVer = NULL; + } + + return hr; +} // MDInternalRW::GetVersionString + +//***************************************************************************** +// Find a given member in a TypeDef (typically a class). +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindMethodDef(// S_OK or error. + mdTypeDef classdef, // The owning class of the member. + LPCSTR szName, // Name of the member in utf8. + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodDef *pmethoddef) // Put MemberDef token here. +{ + HRESULT hr = S_OK; + LOCKREAD(); + + _ASSERTE(szName && pmethoddef); + + IfFailGo(ImportHelper::FindMethod(&(m_pStgdb->m_MiniMd), + classdef, + szName, + pvSigBlob, + cbSigBlob, + pmethoddef)); + +ErrExit: + return hr; +} + +//***************************************************************************** +// Find a given member in a TypeDef (typically a class). +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindMethodDefUsingCompare(// S_OK or error. + mdTypeDef classdef, // The owning class of the member. + LPCSTR szName, // Name of the member in utf8. + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + PSIGCOMPARE pSignatureCompare, // [IN] Routine to compare signatures + void* pSignatureArgs, // [IN] Additional info to supply the compare function + mdMethodDef *pmethoddef) // Put MemberDef token here. +{ + HRESULT hr = S_OK; + LOCKREAD(); + + _ASSERTE(szName && pmethoddef); + + IfFailGo(ImportHelper::FindMethod(&(m_pStgdb->m_MiniMd), + classdef, + szName, + pvSigBlob, + cbSigBlob, + pmethoddef, + 0, + pSignatureCompare, + pSignatureArgs)); + +ErrExit: + return hr; +} + +//***************************************************************************** +// Find a given param of a Method. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindParamOfMethod(// S_OK or error. + mdMethodDef md, // [IN] The owning method of the param. + ULONG iSeq, // [IN] The sequence # of the param. + mdParamDef *pparamdef) // [OUT] Put ParamDef token here. +{ + ParamRec *pParamRec; + RID ridStart, ridEnd; + HRESULT hr = NOERROR; + MethodRec *pMethodRec = NULL; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(md) == mdtMethodDef && pparamdef); + + // get the methoddef record + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + + // figure out the start rid and end rid of the parameter list of this methoddef + ridStart = m_pStgdb->m_MiniMd.getParamListOfMethod(pMethodRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndParamListOfMethod(RidFromToken(md), &ridEnd)); + + // loop through each param + // + for (; ridStart < ridEnd; ridStart++) + { + RID paramRid; + IfFailGo(m_pStgdb->m_MiniMd.GetParamRid(ridStart, ¶mRid)); + IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(paramRid, &pParamRec)); + if (iSeq == m_pStgdb->m_MiniMd.getSequenceOfParam( pParamRec) ) + { + // parameter has the sequence number matches what we are looking for + *pparamdef = TokenFromRid(paramRid, mdtParamDef ); + goto ErrExit; + } + } + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + + return hr; +} // MDInternalRW::FindParamOfMethod + + + +//***************************************************************************** +// return a pointer which points to meta data's internal string +// return the the type name in utf8 +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetNameOfTypeDef( // return hresult + mdTypeDef classdef, // given typedef + LPCSTR* pszname, // pointer to an internal UTF8 string + LPCSTR* psznamespace) // pointer to the namespace. +{ + // No need to lock this method. + HRESULT hr; + + if (pszname != NULL) + { + *pszname = NULL; + } + if (psznamespace != NULL) + { + *psznamespace = NULL; + } + + if (TypeFromToken(classdef) == mdtTypeDef) + { + TypeDefRec *pTypeDefRec; + IfFailRet(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(classdef), &pTypeDefRec)); + + if (pszname != NULL) + { + IfFailRet(m_pStgdb->m_MiniMd.getNameOfTypeDef(pTypeDefRec, pszname)); + } + + if (psznamespace != NULL) + { + IfFailRet(m_pStgdb->m_MiniMd.getNamespaceOfTypeDef(pTypeDefRec, psznamespace)); + } + return S_OK; + } + + _ASSERTE(!"Invalid argument(s) of GetNameOfTypeDef"); + return CLDB_E_INTERNALERROR; +} // MDInternalRW::GetNameOfTypeDef + +//***************************************************************************** +// return pDual indicating if the given TypeDef is marked as a Dual interface +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetIsDualOfTypeDef(// return hresult + mdTypeDef classdef, // given classdef + ULONG *pDual) // [OUT] return dual flag here. +{ + ULONG iFace=0; // Iface type. + HRESULT hr; // A result. + + // no need to lock at this level + + hr = GetIfaceTypeOfTypeDef(classdef, &iFace); + if (hr == S_OK) + *pDual = (iFace == ifDual); + else + *pDual = 1; + + return hr; +} // MDInternalRW::GetIsDualOfTypeDef + +__checkReturn +HRESULT MDInternalRW::GetIfaceTypeOfTypeDef( + mdTypeDef classdef, // [IN] given classdef. + ULONG *pIface) // [OUT] 0=dual, 1=vtable, 2=dispinterface +{ + HRESULT hr; // A result. + const BYTE *pVal; // The custom value. + ULONG cbVal; // Size of the custom value. + ULONG ItfType = DEFAULT_COM_INTERFACE_TYPE; // Set the interface type to the default. + + // all of the public functions that it calls have proper locked + + // If the value is not present, the class is assumed dual. + hr = GetCustomAttributeByName(classdef, INTEROP_INTERFACETYPE_TYPE, (const void**)&pVal, &cbVal); + if (hr == S_OK) + { + _ASSERTE("The ComInterfaceType custom attribute is invalid" && cbVal); + _ASSERTE("ComInterfaceType custom attribute does not have the right format" && (*pVal == 0x01) && (*(pVal + 1) == 0x00)); + ItfType = *(pVal + 2); + if (ItfType >= ifLast) + ItfType = DEFAULT_COM_INTERFACE_TYPE; + } + + // Set the return value. + *pIface = ItfType; + + return hr; +} // MDInternalRW::GetIfaceTypeOfTypeDef + +//***************************************************************************** +// Given a methoddef, return a pointer to methoddef's name +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetNameOfMethodDef( + mdMethodDef md, + LPCSTR *pszMethodName) +{ + // name of method will not change. So no need to lock + HRESULT hr; + MethodRec *pMethodRec; + *pszMethodName = NULL; + IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + IfFailRet(m_pStgdb->m_MiniMd.getNameOfMethod(pMethodRec, pszMethodName)); + return S_OK; +} // MDInternalRW::GetNameOfMethodDef + + +//***************************************************************************** +// Given a methoddef, return a pointer to methoddef's signature and methoddef's name +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetNameAndSigOfMethodDef( + mdMethodDef methoddef, // [IN] given memberdef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszMethodName) +{ + HRESULT hr; + // we don't need lock here because name and signature will not change + + // Output parameter should not be NULL + _ASSERTE(ppvSigBlob && pcbSigBlob); + _ASSERTE(TypeFromToken(methoddef) == mdtMethodDef); + + MethodRec *pMethodRec; + *pszMethodName = NULL; + *ppvSigBlob = NULL; + *ppvSigBlob = NULL; + IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec)); + IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfMethod(pMethodRec, ppvSigBlob, pcbSigBlob)); + + return GetNameOfMethodDef(methoddef, pszMethodName); +} // MDInternalRW::GetNameAndSigOfMethodDef + + +//***************************************************************************** +// Given a FieldDef, return a pointer to FieldDef's name in UTF8 +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetNameOfFieldDef( // return hresult + mdFieldDef fd, // given field + LPCSTR *pszFieldName) +{ + // we don't need lock here because name of field will not change + HRESULT hr; + + FieldRec *pFieldRec; + *pszFieldName = NULL; + IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec)); + IfFailRet(m_pStgdb->m_MiniMd.getNameOfField(pFieldRec, pszFieldName)); + return S_OK; +} // MDInternalRW::GetNameOfFieldDef + + +//***************************************************************************** +// Given a classdef, return a pointer to classdef's name in UTF8 +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetNameOfTypeRef( // return TypeDef's name + mdTypeRef classref, // [IN] given typeref + LPCSTR *psznamespace, // [OUT] return typeref name + LPCSTR *pszname) // [OUT] return typeref namespace +{ + _ASSERTE(TypeFromToken(classref) == mdtTypeRef); + HRESULT hr; + + *psznamespace = NULL; + *pszname = NULL; + + // we don't need lock here because name of a typeref will not change + + TypeRefRec *pTypeRefRec; + IfFailRet(m_pStgdb->m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec)); + IfFailRet(m_pStgdb->m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, psznamespace)); + IfFailRet(m_pStgdb->m_MiniMd.getNameOfTypeRef(pTypeRefRec, pszname)); + return S_OK; +} // MDInternalRW::GetNameOfTypeRef + +//***************************************************************************** +// return the resolutionscope of typeref +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetResolutionScopeOfTypeRef( + mdTypeRef classref, // given classref + mdToken *ptkResolutionScope) +{ + HRESULT hr = S_OK; + TypeRefRec *pTypeRefRec = NULL; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(classref) == mdtTypeRef && RidFromToken(classref)); + + IfFailGo(m_pStgdb->m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec)); + _ASSERTE(hr == S_OK); + *ptkResolutionScope = m_pStgdb->m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec); + return S_OK; + +ErrExit: + *ptkResolutionScope = mdTokenNil; + return hr; +} // MDInternalRW::GetResolutionScopeOfTypeRef + +//***************************************************************************** +// Given a name, find the corresponding TypeRef. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindTypeRefByName( // S_OK or error. + LPCSTR szNamespace, // [IN] Namespace for the TypeRef. + LPCSTR szName, // [IN] Name of the TypeRef. + mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef. + mdTypeRef *ptk) // [OUT] TypeRef token returned. +{ + HRESULT hr = NOERROR; + ULONG cTypeRefRecs; + TypeRefRec *pTypeRefRec; + LPCUTF8 szNamespaceTmp; + LPCUTF8 szNameTmp; + mdToken tkRes; + + LOCKREAD(); + _ASSERTE(ptk); + + // initialize the output parameter + *ptk = mdTypeRefNil; + + // Treat no namespace as empty string. + if (!szNamespace) + szNamespace = ""; + + // It is a linear search here. Do we want to instantiate the name hash? + cTypeRefRecs = m_pStgdb->m_MiniMd.getCountTypeRefs(); + + for (ULONG i = 1; i <= cTypeRefRecs; i++) + { + IfFailGo(m_pStgdb->m_MiniMd.GetTypeRefRecord(i, &pTypeRefRec)); + + tkRes = m_pStgdb->m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec); + if (IsNilToken(tkRes)) + { + if (!IsNilToken(tkResolutionScope)) + continue; + } + else if (tkRes != tkResolutionScope) + continue; + + IfFailGo(m_pStgdb->m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp)); + if (strcmp(szNamespace, szNamespaceTmp)) + continue; + + IfFailGo(m_pStgdb->m_MiniMd.getNameOfTypeRef(pTypeRefRec, &szNameTmp)); + if (!strcmp(szNameTmp, szName)) + { + *ptk = TokenFromRid(i, mdtTypeRef); + goto ErrExit; + } + } + + // cannot find the typedef + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; +} // MDInternalRW::FindTypeRefByName + +//***************************************************************************** +// return flags for a given class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetTypeDefProps( + mdTypeDef td, // given classdef + DWORD *pdwAttr, // return flags on class + mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here. +{ + HRESULT hr = S_OK; + TypeDefRec *pTypeDefRec = NULL; + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + + if (ptkExtends) + { + *ptkExtends = m_pStgdb->m_MiniMd.getExtendsOfTypeDef(pTypeDefRec); + } + if (pdwAttr) + { + *pdwAttr = m_pStgdb->m_MiniMd.getFlagsOfTypeDef(pTypeDefRec); + } + +ErrExit: + + return hr; +} // MDInternalRW::GetTypeDefProps + + +//***************************************************************************** +// return guid pointer to MetaData internal guid pool given a given class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetItemGuid( // return hresult + mdToken tkObj, // given item. + CLSID *pGuid) // [OUT] put guid here. +{ + + HRESULT hr; // A result. + const BYTE *pBlob = NULL; // Blob with dispid. + ULONG cbBlob; // Length of blob. + WCHAR wzBlob[40]; // Wide char format of guid. + int ix; // Loop control. + + // Get the GUID, if any. + hr = GetCustomAttributeByName(tkObj, INTEROP_GUID_TYPE, (const void**)&pBlob, &cbBlob); + if (SUCCEEDED(hr) && hr != S_FALSE) + { + // Should be in format. Total length == 41 + // <0x0001><0x24>01234567-0123-0123-0123-001122334455<0x0000> + if ((cbBlob != 41) || (GET_UNALIGNED_VAL16(pBlob) != 1)) + IfFailGo(E_INVALIDARG); + for (ix=1; ix<=36; ++ix) + wzBlob[ix] = pBlob[ix+2]; + wzBlob[0] = '{'; + wzBlob[37] = '}'; + wzBlob[38] = 0; + hr = IIDFromString(wzBlob, pGuid); + } + else + *pGuid = GUID_NULL; + +ErrExit: + return hr; +} // MDInternalRW::GetItemGuid + +//***************************************************************************** +// // get enclosing class of NestedClass +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetNestedClassProps( + mdTypeDef tkNestedClass, // [IN] NestedClass token. + mdTypeDef *ptkEnclosingClass) // [OUT] EnclosingClass token. +{ + HRESULT hr = NOERROR; + RID rid; + + LOCKREAD(); + + if (!m_pStgdb->m_MiniMd.IsSorted(TBL_NestedClass) && !m_pStgdb->m_MiniMd.IsTableVirtualSorted(TBL_NestedClass)) + { + // NestedClass table is not sorted. + CONVERT_READ_TO_WRITE_LOCK(); + } + + // This is a binary search thus we need to grap a read lock. Or this table + // might be sorted underneath our feet. + + _ASSERTE(TypeFromToken(tkNestedClass) == mdtTypeDef && ptkEnclosingClass); + + IfFailGo(m_pStgdb->m_MiniMd.FindNestedClassFor(RidFromToken(tkNestedClass), &rid)); + + if (InvalidRid(rid)) + { + hr = CLDB_E_RECORD_NOTFOUND; + } + else + { + NestedClassRec *pRecord; + IfFailGo(m_pStgdb->m_MiniMd.GetNestedClassRecord(rid, &pRecord)); + *ptkEnclosingClass = m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord); + } + +ErrExit: + return hr; +} // MDInternalRW::GetNestedClassProps + +//******************************************************************************* +// Get count of Nested classes given the enclosing class. +//******************************************************************************* +__checkReturn +HRESULT +MDInternalRW::GetCountNestedClasses( // return count of Nested classes. + mdTypeDef tkEnclosingClass, // [IN]Enclosing class. + ULONG *pcNestedClassesCount) +{ + HRESULT hr; + ULONG ulCount; + ULONG ulRetCount = 0; + NestedClassRec *pRecord; + + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef && !IsNilToken(tkEnclosingClass)); + + *pcNestedClassesCount = 0; + + ulCount = m_pStgdb->m_MiniMd.getCountNestedClasss(); + + for (ULONG i = 1; i <= ulCount; i++) + { + IfFailRet(m_pStgdb->m_MiniMd.GetNestedClassRecord(i, &pRecord)); + if (tkEnclosingClass == m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord)) + ulRetCount++; + } + *pcNestedClassesCount = ulRetCount; + return S_OK; +} // MDInternalRW::GetCountNestedClasses + +//******************************************************************************* +// Return array of Nested classes given the enclosing class. +//******************************************************************************* +__checkReturn +HRESULT +MDInternalRW::GetNestedClasses( // Return actual count. + mdTypeDef tkEnclosingClass, // [IN] Enclosing class. + mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens. + ULONG ulNestedClasses, // [IN] Size of array. + ULONG *pcNestedClasses) +{ + HRESULT hr; + ULONG ulCount; + ULONG ulRetCount = 0; + NestedClassRec *pRecord; + + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef && + !IsNilToken(tkEnclosingClass)); + + *pcNestedClasses = 0; + + ulCount = m_pStgdb->m_MiniMd.getCountNestedClasss(); + + for (ULONG i = 1; i <= ulCount; i++) + { + IfFailRet(m_pStgdb->m_MiniMd.GetNestedClassRecord(i, &pRecord)); + if (tkEnclosingClass == m_pStgdb->m_MiniMd.getEnclosingClassOfNestedClass(pRecord)) + { + if (ovadd_le(ulRetCount, 1, ulNestedClasses)) // ulRetCount is 0 based. + rNestedClasses[ulRetCount] = m_pStgdb->m_MiniMd.getNestedClassOfNestedClass(pRecord); + ulRetCount++; + } + } + *pcNestedClasses = ulRetCount; + return S_OK; +} // MDInternalRW::GetNestedClasses + +//******************************************************************************* +// return the ModuleRef properties +//******************************************************************************* +__checkReturn +HRESULT MDInternalRW::GetModuleRefProps( // return hresult + mdModuleRef mur, // [IN] moduleref token + LPCSTR *pszName) // [OUT] buffer to fill with the moduleref name +{ + _ASSERTE(TypeFromToken(mur) == mdtModuleRef); + _ASSERTE(pszName); + + HRESULT hr = S_OK; + ModuleRefRec *pModuleRefRec = NULL; + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.GetModuleRefRecord(RidFromToken(mur), &pModuleRefRec)); + IfFailGo(m_pStgdb->m_MiniMd.getNameOfModuleRef(pModuleRefRec, pszName)); + +ErrExit: + + return hr; +} // MDInternalRW::GetModuleRefProps + + + +//***************************************************************************** +// Given a scope and a methoddef, return a pointer to methoddef's signature +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetSigOfMethodDef( + mdMethodDef methoddef, // given a methoddef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig) +{ + // Output parameter should not be NULL + _ASSERTE(pcbSigBlob); + _ASSERTE(TypeFromToken(methoddef) == mdtMethodDef); + + HRESULT hr; + // We don't change MethodDef signature. No need to lock. + + MethodRec *pMethodRec; + *ppSig = NULL; + *pcbSigBlob = 0; + IfFailRet(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec)); + IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfMethod(pMethodRec, ppSig, pcbSigBlob)); + return S_OK; +} // MDInternalRW::GetSigOfMethodDef + + +//***************************************************************************** +// Given a scope and a fielddef, return a pointer to fielddef's signature +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetSigOfFieldDef( + mdFieldDef fielddef, // given a methoddef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig) +{ + _ASSERTE(pcbSigBlob); + _ASSERTE(TypeFromToken(fielddef) == mdtFieldDef); + + HRESULT hr; + // We don't change Field's signature. No need to lock. + + FieldRec *pFieldRec; + *ppSig = NULL; + *pcbSigBlob = 0; + IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fielddef), &pFieldRec)); + IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfField(pFieldRec, ppSig, pcbSigBlob)); + return S_OK; +} // MDInternalRW::GetSigOfFieldDef + + +//***************************************************************************** +// Get signature for the token (FieldDef, MethodDef, Signature, or TypeSpec). +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetSigFromToken( + mdToken tk, + ULONG * pcbSig, + PCCOR_SIGNATURE * ppSig) +{ + HRESULT hr; + // We don't change token's signature. Thus no need to lock. + + *ppSig = NULL; + *pcbSig = 0; + switch (TypeFromToken(tk)) + { + case mdtSignature: + { + StandAloneSigRec *pRec; + IfFailGo(m_pStgdb->m_MiniMd.GetStandAloneSigRecord(RidFromToken(tk), &pRec)); + IfFailGo(m_pStgdb->m_MiniMd.getSignatureOfStandAloneSig(pRec, ppSig, pcbSig)); + return S_OK; + } + case mdtTypeSpec: + { + TypeSpecRec *pRec; + IfFailGo(m_pStgdb->m_MiniMd.GetTypeSpecRecord(RidFromToken(tk), &pRec)); + IfFailGo(m_pStgdb->m_MiniMd.getSignatureOfTypeSpec(pRec, ppSig, pcbSig)); + return S_OK; + } + case mdtMethodDef: + { + IfFailGo(GetSigOfMethodDef(tk, pcbSig, ppSig)); + return S_OK; + } + case mdtFieldDef: + { + IfFailGo(GetSigOfFieldDef(tk, pcbSig, ppSig)); + return S_OK; + } + } + + // not a known token type. +#ifdef _DEBUG + if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) + _ASSERTE(!"Unexpected token type"); +#endif + *pcbSig = 0; + hr = META_E_INVALID_TOKEN_TYPE; + +ErrExit: + return hr; +} // MDInternalRW::GetSigFromToken + + +//***************************************************************************** +// Given methoddef, return the flags +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetMethodDefProps( + mdMethodDef md, + DWORD *pdwFlags) // return mdPublic, mdAbstract, etc +{ + HRESULT hr = S_OK; + MethodRec *pMethodRec = NULL; + + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + _ASSERTE(hr == S_OK); + *pdwFlags = m_pStgdb->m_MiniMd.getFlagsOfMethod(pMethodRec); + return S_OK; + +ErrExit: + *pdwFlags = (DWORD)-1; + return hr; +} // MDInternalRW::GetMethodDefProps + +//***************************************************************************** +// Given a scope and a methoddef, return RVA and impl flags +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetMethodImplProps( + mdToken tk, // [IN] MethodDef + ULONG *pulCodeRVA, // [OUT] CodeRVA + DWORD *pdwImplFlags) // [OUT] Impl. Flags +{ + _ASSERTE(TypeFromToken(tk) == mdtMethodDef); + HRESULT hr = S_OK; + MethodRec *pMethodRec = NULL; + + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec)); + + if (pulCodeRVA) + { + *pulCodeRVA = m_pStgdb->m_MiniMd.getRVAOfMethod(pMethodRec); + } + + if (pdwImplFlags) + { + *pdwImplFlags = m_pStgdb->m_MiniMd.getImplFlagsOfMethod(pMethodRec); + } + +ErrExit: + + return hr; +} // MDInternalRW::GetMethodImplProps + + +//***************************************************************************** +// return the field RVA +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetFieldRVA( + mdToken fd, // [IN] FieldDef + ULONG *pulCodeRVA) // [OUT] CodeRVA +{ + _ASSERTE(TypeFromToken(fd) == mdtFieldDef); + _ASSERTE(pulCodeRVA); + ULONG iRecord; + HRESULT hr = NOERROR; + + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.FindFieldRVAHelper(fd, &iRecord)); + if (InvalidRid(iRecord)) + { + if (pulCodeRVA) + *pulCodeRVA = 0; + hr = CLDB_E_RECORD_NOTFOUND; + } + else + { + FieldRVARec *pFieldRVARec; + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRVARecord(iRecord, &pFieldRVARec)); + + *pulCodeRVA = m_pStgdb->m_MiniMd.getRVAOfFieldRVA(pFieldRVARec); + } + +ErrExit: + return hr; +} // MDInternalRW::GetFieldRVA + + +//***************************************************************************** +// Given a fielddef, return the flags. Such as fdPublic, fdStatic, etc +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetFieldDefProps( + mdFieldDef fd, // given memberdef + DWORD *pdwFlags) // [OUT] return fdPublic, fdPrive, etc flags +{ + _ASSERTE(TypeFromToken(fd) == mdtFieldDef); + HRESULT hr = S_OK; + FieldRec *pFieldRec = NULL; + + LOCKREAD(); + + IfFailRet(m_pStgdb->m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec)); + _ASSERTE(hr == S_OK); + *pdwFlags = m_pStgdb->m_MiniMd.getFlagsOfField(pFieldRec); + return S_OK; + +ErrExit: + *pdwFlags = (DWORD)-1; + return hr; +} // MDInternalRW::GetFieldDefProps + +//***************************************************************************** +// return default value of a token(could be paramdef, fielddef, or property) +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetDefaultValue( // return hresult + mdToken tk, // [IN] given FieldDef, ParamDef, or Property + MDDefaultValue *pMDDefaultValue) // [OUT] default value +{ + _ASSERTE(pMDDefaultValue); + + HRESULT hr; + BYTE bType; + const VOID *pValue; + ULONG cbValue; + RID rid; + ConstantRec *pConstantRec; + + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.FindConstantHelper(tk, &rid)); + if (InvalidRid(rid)) + { + pMDDefaultValue->m_bType = ELEMENT_TYPE_VOID; + return S_OK; + } + IfFailGo(m_pStgdb->m_MiniMd.GetConstantRecord(rid, &pConstantRec)); + + // get the type of constant value + bType = m_pStgdb->m_MiniMd.getTypeOfConstant(pConstantRec); + + // get the value blob + IfFailGo(m_pStgdb->m_MiniMd.getValueOfConstant(pConstantRec, reinterpret_cast<const BYTE **>(&pValue), &cbValue)); + + // convert it to our internal default value representation + hr = _FillMDDefaultValue(bType, pValue, cbValue, pMDDefaultValue); + +ErrExit: + + return hr; +} // MDInternalRW::GetDefaultValue + + +//***************************************************************************** +// Given a scope and a methoddef/fielddef, return the dispid +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetDispIdOfMemberDef( // return hresult + mdToken tk, // given methoddef or fielddef + ULONG *pDispid) // Put the dispid here. +{ +#ifdef FEATURE_COMINTEROP + HRESULT hr; // A result. + const BYTE *pBlob; // Blob with dispid. + ULONG cbBlob; // Length of blob. + + // No need to lock this function. All of the function that it is calling is already locked! + + // Get the DISPID, if any. + _ASSERTE(pDispid); + + *pDispid = DISPID_UNKNOWN; + hr = GetCustomAttributeByName(tk, INTEROP_DISPID_TYPE, (const void**)&pBlob, &cbBlob); + if (hr != S_FALSE) + { + // Check that this might be a dispid. + if (cbBlob >= (sizeof(*pDispid)+2)) + *pDispid = GET_UNALIGNED_VAL32(pBlob+2); + else + IfFailGo(E_INVALIDARG); + } + +ErrExit: + return hr; +#else // FEATURE_COMINTEROP + _ASSERTE(false); + return E_NOTIMPL; +#endif // FEATURE_COMINTEROP +} // MDInternalRW::GetDispIdOfMemberDef + + +//***************************************************************************** +// Given interfaceimpl, return the TypeRef/TypeDef and flags +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetTypeOfInterfaceImpl( // return hresult + mdInterfaceImpl iiImpl, // given a interfaceimpl + mdToken *ptkType) +{ + HRESULT hr; + // no need to lock this function. + + _ASSERTE(TypeFromToken(iiImpl) == mdtInterfaceImpl); + + *ptkType = mdTypeDefNil; + + InterfaceImplRec *pIIRec; + IfFailRet(m_pStgdb->m_MiniMd.GetInterfaceImplRecord(RidFromToken(iiImpl), &pIIRec)); + *ptkType = m_pStgdb->m_MiniMd.getInterfaceOfInterfaceImpl(pIIRec); + return S_OK; +} // MDInternalRW::GetTypeOfInterfaceImpl + +//***************************************************************************** +// This routine gets the properties for the given MethodSpec token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetMethodSpecProps( // S_OK or error. + mdMethodSpec mi, // [IN] The method instantiation + mdToken *tkParent, // [OUT] MethodDef or MemberRef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob) // [OUT] actual size of signature blob +{ + HRESULT hr = NOERROR; + MethodSpecRec *pMethodSpecRec; + + _ASSERTE(TypeFromToken(mi) == mdtMethodSpec); + + IfFailGo(m_pStgdb->m_MiniMd.GetMethodSpecRecord(RidFromToken(mi), &pMethodSpecRec)); + + if (tkParent) + *tkParent = m_pStgdb->m_MiniMd.getMethodOfMethodSpec(pMethodSpecRec); + + if (ppvSigBlob || pcbSigBlob) + { + // caller wants signature information + PCCOR_SIGNATURE pvSigTmp; + ULONG cbSig; + IfFailGo(m_pStgdb->m_MiniMd.getInstantiationOfMethodSpec(pMethodSpecRec, &pvSigTmp, &cbSig)); + if ( ppvSigBlob ) + *ppvSigBlob = pvSigTmp; + if ( pcbSigBlob) + *pcbSigBlob = cbSig; + } + +ErrExit: + return hr; +} // MDInternalRW::GetMethodSpecProps + +//***************************************************************************** +// Given a classname, return the typedef +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindTypeDef( // return hresult + LPCSTR szNamespace, // [IN] Namespace for the TypeDef. + LPCSTR szName, // [IN] Name of the TypeDef. + mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class. + mdTypeDef *ptypedef) // [OUT] return typedef +{ + HRESULT hr = S_OK; + LOCKREADIFFAILRET(); + + _ASSERTE(ptypedef); + + // initialize the output parameter + *ptypedef = mdTypeDefNil; + + return ImportHelper::FindTypeDefByName(&(m_pStgdb->m_MiniMd), + szNamespace, + szName, + tkEnclosingClass, + ptypedef); +} // MDInternalRW::FindTypeDef + +//***************************************************************************** +// Given a memberref, return a pointer to memberref's name and signature +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetNameAndSigOfMemberRef( // meberref's name + mdMemberRef memberref, // given a memberref + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszMemberRefName) +{ + HRESULT hr; + + // MemberRef's name and sig won't change. Don't need to lock this. + + _ASSERTE(TypeFromToken(memberref) == mdtMemberRef); + + MemberRefRec *pMemberRefRec; + *pszMemberRefName = NULL; + if (ppvSigBlob != NULL) + { + _ASSERTE(pcbSigBlob != NULL); + *ppvSigBlob = NULL; + *pcbSigBlob = 0; + } + IfFailRet(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec)); + if (ppvSigBlob != NULL) + { + IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfMemberRef(pMemberRefRec, ppvSigBlob, pcbSigBlob)); + } + IfFailRet(m_pStgdb->m_MiniMd.getNameOfMemberRef(pMemberRefRec, pszMemberRefName)); + return S_OK; +} // MDInternalRW::GetNameAndSigOfMemberRef + + + +//***************************************************************************** +// Given a memberref, return parent token. It can be a TypeRef, ModuleRef, or a MethodDef +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetParentOfMemberRef( // return parent token + mdMemberRef memberref, // given a typedef + mdToken *ptkParent) // return the parent token +{ + HRESULT hr = S_OK; + MemberRefRec *pMemberRefRec = NULL; + + LOCKREAD(); + + // parent for MemberRef can change. See SetParent. + + _ASSERTE(TypeFromToken(memberref) == mdtMemberRef); + + IfFailRet(m_pStgdb->m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec)); + _ASSERTE(hr == S_OK); + *ptkParent = m_pStgdb->m_MiniMd.getClassOfMemberRef(pMemberRefRec); + return S_OK; + +ErrExit: + *ptkParent = mdTokenNil; + return hr; +} // MDInternalRW::GetParentOfMemberRef + +//***************************************************************************** +// return properties of a paramdef +//*****************************************************************************/ +__checkReturn +HRESULT +MDInternalRW::GetParamDefProps ( + mdParamDef paramdef, // given a paramdef + USHORT *pusSequence, // [OUT] slot number for this parameter + DWORD *pdwAttr, // [OUT] flags + LPCSTR *pszName) // [OUT] return the name of the parameter +{ + HRESULT hr = S_OK; + ParamRec *pParamRec = NULL; + + LOCKREAD(); + + // parent for MemberRef can change. See SetParamProps. + + _ASSERTE(TypeFromToken(paramdef) == mdtParamDef); + IfFailGo(m_pStgdb->m_MiniMd.GetParamRecord(RidFromToken(paramdef), &pParamRec)); + _ASSERTE(hr == S_OK); + if (pdwAttr != NULL) + { + *pdwAttr = m_pStgdb->m_MiniMd.getFlagsOfParam(pParamRec); + } + if (pusSequence != NULL) + { + *pusSequence = m_pStgdb->m_MiniMd.getSequenceOfParam(pParamRec); + } + IfFailGo(m_pStgdb->m_MiniMd.getNameOfParam(pParamRec, pszName)); + _ASSERTE(hr == S_OK); + return S_OK; + +ErrExit: + *pszName = NULL; + return S_OK; +} // MDInternalRW::GetParamDefProps + +//***************************************************************************** +// Get property info for the method. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetPropertyInfoForMethodDef( // Result. + mdMethodDef md, // [IN] memberdef + mdProperty *ppd, // [OUT] put property token here + LPCSTR *pName, // [OUT] put pointer to name here + ULONG *pSemantic) // [OUT] put semantic here +{ + MethodSemanticsRec *pSemantics; + RID ridCur; + RID ridMax; + USHORT usSemantics; + HRESULT hr = S_OK; + LOCKREAD(); + + ridMax = m_pStgdb->m_MiniMd.getCountMethodSemantics(); + for (ridCur = 1; ridCur <= ridMax; ridCur++) + { + IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + if (md == m_pStgdb->m_MiniMd.getMethodOfMethodSemantics(pSemantics)) + { + // match the method + usSemantics = m_pStgdb->m_MiniMd.getSemanticOfMethodSemantics(pSemantics); + if (usSemantics == msGetter || usSemantics == msSetter) + { + // Make sure that it is not an invalid entry + if (m_pStgdb->m_MiniMd.getAssociationOfMethodSemantics(pSemantics) != mdPropertyNil) + { + // found a match. Fill out the output parameters + PropertyRec *pProperty; + mdProperty prop; + prop = m_pStgdb->m_MiniMd.getAssociationOfMethodSemantics(pSemantics); + + if (ppd) + *ppd = prop; + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty)); + + if (pName) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pProperty, pName)); + } + + if (pSemantic) + *pSemantic = usSemantics; + goto ErrExit; + } + } + } + } + + hr = S_FALSE; +ErrExit: + return hr; +} // MDInternalRW::GetPropertyInfoForMethodDef + +//***************************************************************************** +// return the pack size of a class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetClassPackSize( + mdTypeDef td, // [IN] give typedef + DWORD *pdwPackSize) // [OUT] +{ + HRESULT hr = NOERROR; + RID ridClassLayout = 0; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pdwPackSize); + + ClassLayoutRec *pRec; + IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &ridClassLayout)); + + if (InvalidRid(ridClassLayout)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + + IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec)); + *pdwPackSize = m_pStgdb->m_MiniMd.getPackingSizeOfClassLayout(pRec); +ErrExit: + return hr; +} // MDInternalRW::GetClassPackSize + + +//***************************************************************************** +// return the total size of a value class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetClassTotalSize( // return error if a class does not have total size info + mdTypeDef td, // [IN] give typedef + ULONG *pulClassSize) // [OUT] return the total size of the class +{ + CONTRACT_VIOLATION(ThrowsViolation); + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pulClassSize); + + ClassLayoutRec *pRec; + HRESULT hr = NOERROR; + RID ridClassLayout; + + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.FindClassLayoutHelper(td, &ridClassLayout)); + if (InvalidRid(ridClassLayout)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + + IfFailGo(m_pStgdb->m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec)); + *pulClassSize = m_pStgdb->m_MiniMd.getClassSizeOfClassLayout(pRec); +ErrExit: + return hr; +} // MDInternalRW::GetClassTotalSize + + +//***************************************************************************** +// init the layout enumerator of a class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetClassLayoutInit( + mdTypeDef td, // [IN] give typedef + MD_CLASS_LAYOUT *pmdLayout) // [OUT] set up the status of query here +{ + HRESULT hr = NOERROR; + LOCKREAD(); + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + // <TODO>Do we need to lock this function? Can clints add more Fields on a TypeDef?</TODO> + + // initialize the output parameter + _ASSERTE(pmdLayout); + memset(pmdLayout, 0, sizeof(MD_CLASS_LAYOUT)); + + TypeDefRec *pTypeDefRec; + + // record for this typedef in TypeDef Table + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + + // find the starting and end field for this typedef + pmdLayout->m_ridFieldCur = m_pStgdb->m_MiniMd.getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &(pmdLayout->m_ridFieldEnd))); + +ErrExit: + + return hr; +} // MDInternalRW::GetClassLayoutInit + +//***************************************************************************** +// Get the field offset for a given field token +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetFieldOffset( + mdFieldDef fd, // [IN] fielddef + ULONG *pulOffset) // [OUT] FieldOffset +{ + HRESULT hr = S_OK; + FieldLayoutRec *pRec; + + _ASSERTE(pulOffset); + + RID iLayout; + + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(fd, &iLayout)); + + if (InvalidRid(iLayout)) + { + hr = S_FALSE; + goto ErrExit; + } + + IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iLayout, &pRec)); + *pulOffset = m_pStgdb->m_MiniMd.getOffSetOfFieldLayout(pRec); + _ASSERTE(*pulOffset != UINT32_MAX); + +ErrExit: + return hr; +} // MDInternalRW::GetFieldOffset + +//***************************************************************************** +// enum the next the field layout +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetClassLayoutNext( + MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here + mdFieldDef *pfd, // [OUT] field def + ULONG *pulOffset) // [OUT] field offset or sequence +{ + HRESULT hr = S_OK; + + _ASSERTE(pfd && pulOffset && pLayout); + + RID iLayout2; + FieldLayoutRec *pRec; + + LOCKREAD(); + + while (pLayout->m_ridFieldCur < pLayout->m_ridFieldEnd) + { + RID fieldRid; + IfFailGo(m_pStgdb->m_MiniMd.GetFieldRid(pLayout->m_ridFieldCur, &fieldRid)); + mdFieldDef fd = TokenFromRid(fieldRid, mdtFieldDef); + IfFailGo(m_pStgdb->m_MiniMd.FindFieldLayoutHelper(fd, &iLayout2)); + pLayout->m_ridFieldCur++; + if (!InvalidRid(iLayout2)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetFieldLayoutRecord(iLayout2, &pRec)); + *pulOffset = m_pStgdb->m_MiniMd.getOffSetOfFieldLayout(pRec); + _ASSERTE(*pulOffset != UINT32_MAX); + *pfd = fd; + goto ErrExit; + } + } + + *pfd = mdFieldDefNil; + hr = S_FALSE; + + // fall through + +ErrExit: + return hr; +} // MDInternalRW::GetClassLayoutNext + + +//***************************************************************************** +// return the field's native type signature +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetFieldMarshal( // return error if no native type associate with the token + mdToken tk, // [IN] given fielddef or paramdef + PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature + ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType +{ + // output parameters have to be supplied + _ASSERTE(pcbNativeType); + + RID rid; + FieldMarshalRec *pFieldMarshalRec; + HRESULT hr = NOERROR; + + LOCKREAD(); + + // find the row containing the marshal definition for tk + IfFailGo(m_pStgdb->m_MiniMd.FindFieldMarshalHelper(tk, &rid)); + if (InvalidRid(rid)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + IfFailGo(m_pStgdb->m_MiniMd.GetFieldMarshalRecord(rid, &pFieldMarshalRec)); + + // get the native type + IfFailGo(m_pStgdb->m_MiniMd.getNativeTypeOfFieldMarshal(pFieldMarshalRec, pSigNativeType, pcbNativeType)); +ErrExit: + return hr; +} // MDInternalRW::GetFieldMarshal + + + +//***************************************** +// property APIs +//***************************************** + +//***************************************************************************** +// Find property by name +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindProperty( + mdTypeDef td, // [IN] given a typdef + LPCSTR szPropName, // [IN] property name + mdProperty *pProp) // [OUT] return property token +{ + HRESULT hr = NOERROR; + LOCKREAD(); + + // output parameters have to be supplied + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pProp); + + PropertyMapRec *pRec; + PropertyRec *pProperty; + RID ridPropertyMap; + RID ridCur; + RID ridEnd; + LPCUTF8 szName; + + IfFailGo(m_pStgdb->m_MiniMd.FindPropertyMapFor(RidFromToken(td), &ridPropertyMap)); + if (InvalidRid(ridPropertyMap)) + { + // not found! + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pRec)); + + // get the starting/ending rid of properties of this typedef + ridCur = m_pStgdb->m_MiniMd.getPropertyListOfPropertyMap(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + + for ( ; ridCur < ridEnd; ridCur ++ ) + { + RID propertyRid; + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRid(ridCur, &propertyRid)); + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(propertyRid, &pProperty)); + IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pProperty, &szName)); + if ( strcmp(szName, szPropName) ==0 ) + { + // Found the match. Set the output parameter and we are done. + *pProp = TokenFromRid(propertyRid, mdtProperty); + goto ErrExit; + } + } + + // not found + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; +} // MDInternalRW::FindProperty + + + +//***************************************************************************** +// return the properties of a property +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetPropertyProps( + mdProperty prop, // [IN] property token + LPCSTR *pszProperty, // [OUT] property name + DWORD *pdwPropFlags, // [OUT] property flags. + PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob + ULONG *pcbSig) // [OUT] count of bytes in *ppvSig +{ + HRESULT hr = S_OK; + LOCKREAD(); + + // output parameters have to be supplied + _ASSERTE(TypeFromToken(prop) == mdtProperty); + + PropertyRec *pProperty; + ULONG cbSig; + + IfFailGo(m_pStgdb->m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty)); + + // get name of the property + if (pszProperty) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfProperty(pProperty, pszProperty)); + } + + // get the flags of property + if (pdwPropFlags) + *pdwPropFlags = m_pStgdb->m_MiniMd.getPropFlagsOfProperty(pProperty); + + // get the type of the property + if (ppvSig) + { + IfFailGo(m_pStgdb->m_MiniMd.getTypeOfProperty(pProperty, ppvSig, &cbSig)); + if (pcbSig) + { + *pcbSig = cbSig; + } + } + +ErrExit: + return hr; +} // MDInternalRW::GetPropertyProps + + +//********************************** +// +// Event APIs +// +//********************************** + +//***************************************************************************** +// return an event by given the name +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindEvent( + mdTypeDef td, // [IN] given a typdef + LPCSTR szEventName, // [IN] event name + mdEvent *pEvent) // [OUT] return event token +{ + HRESULT hr = NOERROR; + LOCKREAD(); + + // output parameters have to be supplied + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pEvent); + + EventMapRec *pRec; + EventRec *pEventRec; + RID ridEventMap; + RID ridCur; + RID ridEnd; + LPCUTF8 szName; + + IfFailGo(m_pStgdb->m_MiniMd.FindEventMapFor(RidFromToken(td), &ridEventMap)); + if (InvalidRid(ridEventMap)) + { + // not found! + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + IfFailGo(m_pStgdb->m_MiniMd.GetEventMapRecord(ridEventMap, &pRec)); + + // get the starting/ending rid of properties of this typedef + ridCur = m_pStgdb->m_MiniMd.getEventListOfEventMap(pRec); + IfFailGo(m_pStgdb->m_MiniMd.getEndEventListOfEventMap(ridEventMap, &ridEnd)); + + for (; ridCur < ridEnd; ridCur ++) + { + RID eventRid; + IfFailGo(m_pStgdb->m_MiniMd.GetEventRid(ridCur, &eventRid)); + IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(eventRid, &pEventRec)); + IfFailGo(m_pStgdb->m_MiniMd.getNameOfEvent(pEventRec, &szName)); + if ( strcmp(szName, szEventName) ==0 ) + { + // Found the match. Set the output parameter and we are done. + *pEvent = TokenFromRid(eventRid, mdtEvent); + goto ErrExit; + } + } + + // not found + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + + return hr; +} // MDInternalRW::FindEvent + + +//***************************************************************************** +// return the properties of an event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetEventProps( // S_OK, S_FALSE, or error. + mdEvent ev, // [IN] event token + LPCSTR *pszEvent, // [OUT] Event name + DWORD *pdwEventFlags, // [OUT] Event flags. + mdToken *ptkEventType) // [OUT] EventType class +{ + HRESULT hr = S_OK; + LOCKREAD(); + EventRec *pEvent; + + // output parameters have to be supplied + _ASSERTE(TypeFromToken(ev) == mdtEvent); + + IfFailGo(m_pStgdb->m_MiniMd.GetEventRecord(RidFromToken(ev), &pEvent)); + if (pszEvent != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfEvent(pEvent, pszEvent)); + } + if (pdwEventFlags) + *pdwEventFlags = m_pStgdb->m_MiniMd.getEventFlagsOfEvent(pEvent); + if (ptkEventType) + *ptkEventType = m_pStgdb->m_MiniMd.getEventTypeOfEvent(pEvent); + +ErrExit: + + return hr; +} // MDInternalRW::GetEventProps + +//***************************************************************************** +// return the properties of a generic param +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetGenericParamProps( // S_OK or error. + mdGenericParam rd, // [IN] The type parameter + ULONG* pulSequence, // [OUT] Parameter sequence number + DWORD* pdwAttr, // [OUT] Type parameter flags (for future use) + mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef) + DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use) + LPCSTR *szName) // [OUT] The name +{ + HRESULT hr = NOERROR; + GenericParamRec *pGenericParamRec = NULL; + + // See if this version of the metadata can do Generics + if (!m_pStgdb->m_MiniMd.SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + _ASSERTE(TypeFromToken(rd) == mdtGenericParam); + if (TypeFromToken(rd) != mdtGenericParam) + IfFailGo(CLDB_E_FILE_CORRUPT); + + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamRecord(RidFromToken(rd), &pGenericParamRec)); + + if (pulSequence) + *pulSequence = m_pStgdb->m_MiniMd.getNumberOfGenericParam(pGenericParamRec); + if (pdwAttr) + *pdwAttr = m_pStgdb->m_MiniMd.getFlagsOfGenericParam(pGenericParamRec); + if (ptOwner) + *ptOwner = m_pStgdb->m_MiniMd.getOwnerOfGenericParam(pGenericParamRec); + if (szName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfGenericParam(pGenericParamRec, szName)); + } + +ErrExit: + return hr; +} // MDInternalRW::GetGenericParamProps + + +//***************************************************************************** +// This routine gets the properties for the given GenericParamConstraint token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetGenericParamConstraintProps( // S_OK or error. + mdGenericParamConstraint rd, // [IN] The constraint token + mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained + mdToken *ptkConstraintType) // [OUT] TypeDef/Ref/Spec constraint +{ + HRESULT hr = NOERROR; + GenericParamConstraintRec *pGPCRec; + RID ridRD = RidFromToken(rd); + + // See if this version of the metadata can do Generics + if (!m_pStgdb->m_MiniMd.SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + if((TypeFromToken(rd) == mdtGenericParamConstraint) && (ridRD != 0)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetGenericParamConstraintRecord(ridRD, &pGPCRec)); + + if (ptGenericParam) + *ptGenericParam = TokenFromRid(m_pStgdb->m_MiniMd.getOwnerOfGenericParamConstraint(pGPCRec),mdtGenericParam); + if (ptkConstraintType) + *ptkConstraintType = m_pStgdb->m_MiniMd.getConstraintOfGenericParamConstraint(pGPCRec); + } + else + hr = META_E_BAD_INPUT_PARAMETER; + +ErrExit: + return hr; +} // MDInternalRW::GetGenericParamConstraintProps + +//***************************************************************************** +// Find methoddef of a particular associate with a property or an event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::FindAssociate( + mdToken evprop, // [IN] given a property or event token + DWORD dwSemantics, // [IN] given a associate semantics(setter, getter, testdefault, reset) + mdMethodDef *pmd) // [OUT] return method def token +{ + HRESULT hr = NOERROR; + RID rid; + MethodSemanticsRec *pMethodSemantics; + + // output parameters have to be supplied + _ASSERTE(pmd); + _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty); + + LOCKREAD(); + + hr = m_pStgdb->m_MiniMd.FindAssociateHelper(evprop, dwSemantics, &rid); + if (SUCCEEDED(hr)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(rid, &pMethodSemantics)); + *pmd = m_pStgdb->m_MiniMd.getMethodOfMethodSemantics(pMethodSemantics); + } + +ErrExit: + + return hr; +} // MDInternalRW::FindAssociate + + +//***************************************************************************** +// get counts of methodsemantics associated with a particular property/event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::EnumAssociateInit( + mdToken evprop, // [IN] given a property or an event token + HENUMInternal *phEnum) // [OUT] cursor to hold the query result +{ + HRESULT hr; + + LOCKREAD(); + + // output parameters have to be supplied + _ASSERTE(phEnum); + _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty); + + hr = m_pStgdb->m_MiniMd.FindMethodSemanticsHelper(evprop, phEnum); + +ErrExit: + return hr; +} // MDInternalRW::EnumAssociateInit + + +//***************************************************************************** +// get all methodsemantics associated with a particular property/event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetAllAssociates( + HENUMInternal *phEnum, // [OUT] cursor to hold the query result + ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output + ULONG cAssociateRec) // [IN] size of the buffer +{ + HRESULT hr = S_OK; + MethodSemanticsRec *pSemantics; + RID ridCur; + int index = 0; + + LOCKREAD(); + + // <TODO>@FUTURE: rewrite the EnumAssociateInit and GetAllAssociates. Because we might add more properties and events. + // Then we might resort MethodSemantics table. So this can be totally out of sync.</TODO> + + _ASSERTE(phEnum && pAssociateRec); + _ASSERTE(cAssociateRec == phEnum->m_ulCount); + + // Convert from row pointers to RIDs. + while (HENUMInternal::EnumNext(phEnum, (mdToken *)&ridCur)) + { + IfFailGo(m_pStgdb->m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + + pAssociateRec[index].m_memberdef = m_pStgdb->m_MiniMd.getMethodOfMethodSemantics(pSemantics); + pAssociateRec[index].m_dwSemantics = m_pStgdb->m_MiniMd.getSemanticOfMethodSemantics(pSemantics); + index++; + } + +ErrExit: + + return hr; +} // MDInternalRW::GetAllAssociates + + +//***************************************************************************** +// Get the Action and Permissions blob for a given PermissionSet. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetPermissionSetProps( + mdPermission pm, // [IN] the permission token. + DWORD *pdwAction, // [OUT] CorDeclSecurity. + void const **ppvPermission, // [OUT] permission blob. + ULONG *pcbPermission) // [OUT] count of bytes of pvPermission. +{ + HRESULT hr = S_OK; + _ASSERTE(TypeFromToken(pm) == mdtPermission); + _ASSERTE(pdwAction && ppvPermission && pcbPermission); + + DeclSecurityRec *pPerm; + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.GetDeclSecurityRecord(RidFromToken(pm), &pPerm)); + *pdwAction = m_pStgdb->m_MiniMd.getActionOfDeclSecurity(pPerm); + IfFailGo(m_pStgdb->m_MiniMd.getPermissionSetOfDeclSecurity(pPerm, reinterpret_cast<const BYTE **>(ppvPermission), pcbPermission)); + +ErrExit: + + return hr; +} // MDInternalRW::GetPermissionSetProps + + +//***************************************************************************** +// Get the String given the String token. +// Return a pointer to the string, or NULL in case of error. +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRW::GetUserString( // Offset into the string blob heap. + mdString stk, // [IN] the string token. + ULONG *pcchStringSize, // [OUT] count of characters in the string. + BOOL *pfIs80Plus, // [OUT] specifies where there are extended characters >= 0x80. + LPCWSTR *pwszUserString) +{ + HRESULT hr; + LPWSTR wszTmp; + + // no need to lock this function. + + if (pfIs80Plus != NULL) + { + *pfIs80Plus = FALSE; + } + *pwszUserString = NULL; + *pcchStringSize = 0; + + _ASSERTE(pcchStringSize != NULL); + MetaData::DataBlob userString; + IfFailRet(m_pStgdb->m_MiniMd.GetUserString(RidFromToken(stk), &userString)); + + wszTmp = reinterpret_cast<LPWSTR>(userString.GetDataPointer()); + + *pcchStringSize = userString.GetSize() / sizeof(WCHAR); + + if (userString.IsEmpty()) + { + *pwszUserString = NULL; + return S_OK; + } + + if (pfIs80Plus != NULL) + { + if (userString.GetSize() % sizeof(WCHAR) == 0) + { + *pfIs80Plus = TRUE; // no indicator, presume the worst + } + // Return the user string terminator (contains value fIs80Plus) + *pfIs80Plus = *(reinterpret_cast<PBYTE>(wszTmp + *pcchStringSize)); + } + + *pwszUserString = wszTmp; + return S_OK; +} // MDInternalRW::GetUserString + +//***************************************************************************** +// Get the properties for the given Assembly token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetAssemblyProps( + mdAssembly mda, // [IN] The Assembly for which to get the properties. + const void **ppbPublicKey, // [OUT] Pointer to the public key. + ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key. + ULONG *pulHashAlgId, // [OUT] Hash Algorithm. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + DWORD *pdwAssemblyFlags) // [OUT] Flags. +{ + AssemblyRec *pRecord; + HRESULT hr = S_OK; + LOCKREAD(); + + _ASSERTE(TypeFromToken(mda) == mdtAssembly && RidFromToken(mda)); + IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRecord(RidFromToken(mda), &pRecord)); + + if (ppbPublicKey != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getPublicKeyOfAssembly(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKey), pcbPublicKey)); + } + if (pulHashAlgId) + *pulHashAlgId = m_pStgdb->m_MiniMd.getHashAlgIdOfAssembly(pRecord); + if (pszName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfAssembly(pRecord, pszName)); + } + if (pMetaData) + { + pMetaData->usMajorVersion = m_pStgdb->m_MiniMd.getMajorVersionOfAssembly(pRecord); + pMetaData->usMinorVersion = m_pStgdb->m_MiniMd.getMinorVersionOfAssembly(pRecord); + pMetaData->usBuildNumber = m_pStgdb->m_MiniMd.getBuildNumberOfAssembly(pRecord); + pMetaData->usRevisionNumber = m_pStgdb->m_MiniMd.getRevisionNumberOfAssembly(pRecord); + IfFailGo(m_pStgdb->m_MiniMd.getLocaleOfAssembly(pRecord, &pMetaData->szLocale)); + } + if (pdwAssemblyFlags) + { + *pdwAssemblyFlags = m_pStgdb->m_MiniMd.getFlagsOfAssembly(pRecord); + + // Turn on the afPublicKey if PublicKey blob is not empty + DWORD cbPublicKey; + const BYTE *pbPublicKey; + IfFailGo(m_pStgdb->m_MiniMd.getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey)); + if (cbPublicKey) + *pdwAssemblyFlags |= afPublicKey; + } + +ErrExit: + return hr; + +} // MDInternalRW::GetAssemblyProps + +//***************************************************************************** +// Get the properties for the given AssemblyRef token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetAssemblyRefProps( + mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties. + const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token. + ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + const void **ppbHashValue, // [OUT] Hash blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob. + DWORD *pdwAssemblyRefFlags) // [OUT] Flags. +{ + AssemblyRefRec *pRecord; + HRESULT hr = S_OK; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdar) == mdtAssemblyRef && RidFromToken(mdar)); + IfFailGo(m_pStgdb->m_MiniMd.GetAssemblyRefRecord(RidFromToken(mdar), &pRecord)); + + if (ppbPublicKeyOrToken != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getPublicKeyOrTokenOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKeyOrToken), pcbPublicKeyOrToken)); + } + if (pszName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfAssemblyRef(pRecord, pszName)); + } + if (pMetaData) + { + pMetaData->usMajorVersion = m_pStgdb->m_MiniMd.getMajorVersionOfAssemblyRef(pRecord); + pMetaData->usMinorVersion = m_pStgdb->m_MiniMd.getMinorVersionOfAssemblyRef(pRecord); + pMetaData->usBuildNumber = m_pStgdb->m_MiniMd.getBuildNumberOfAssemblyRef(pRecord); + pMetaData->usRevisionNumber = m_pStgdb->m_MiniMd.getRevisionNumberOfAssemblyRef(pRecord); + IfFailGo(m_pStgdb->m_MiniMd.getLocaleOfAssemblyRef(pRecord, &pMetaData->szLocale)); + } + if (ppbHashValue != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getHashValueOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue)); + } + if (pdwAssemblyRefFlags) + *pdwAssemblyRefFlags = m_pStgdb->m_MiniMd.getFlagsOfAssemblyRef(pRecord); + +ErrExit: + return hr; +} // MDInternalRW::GetAssemblyRefProps + +//***************************************************************************** +// Get the properties for the given File token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetFileProps( + mdFile mdf, // [IN] The File for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob. + DWORD *pdwFileFlags) // [OUT] Flags. +{ + FileRec *pRecord; + HRESULT hr = S_OK; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdf) == mdtFile && RidFromToken(mdf)); + IfFailGo(m_pStgdb->m_MiniMd.GetFileRecord(RidFromToken(mdf), &pRecord)); + + if (pszName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfFile(pRecord, pszName)); + } + if (ppbHashValue != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getHashValueOfFile(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue)); + } + if (pdwFileFlags) + *pdwFileFlags = m_pStgdb->m_MiniMd.getFlagsOfFile(pRecord); + +ErrExit: + return hr; +} // MDInternalRW::GetFileProps + +//***************************************************************************** +// Get the properties for the given ExportedType token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetExportedTypeProps( + mdExportedType mdct, // [IN] The ExportedType for which to get the properties. + LPCSTR *pszNamespace, // [OUT] Buffer to fill with name. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file. + DWORD *pdwExportedTypeFlags) // [OUT] Flags. +{ + ExportedTypeRec *pRecord; + HRESULT hr = S_OK; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdct) == mdtExportedType && RidFromToken(mdct)); + IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(mdct), &pRecord)); + + if (pszNamespace != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getTypeNamespaceOfExportedType(pRecord, pszNamespace)); + } + if (pszName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getTypeNameOfExportedType(pRecord, pszName)); + } + if (ptkImplementation) + *ptkImplementation = m_pStgdb->m_MiniMd.getImplementationOfExportedType(pRecord); + if (ptkTypeDef) + *ptkTypeDef = m_pStgdb->m_MiniMd.getTypeDefIdOfExportedType(pRecord); + if (pdwExportedTypeFlags) + *pdwExportedTypeFlags = m_pStgdb->m_MiniMd.getFlagsOfExportedType(pRecord); + +ErrExit: + return hr; +} // MDInternalRW::GetExportedTypeProps + +//***************************************************************************** +// Get the properties for the given Resource token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetManifestResourceProps( + mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file. + DWORD *pdwResourceFlags) // [OUT] Flags. +{ + ManifestResourceRec *pRecord; + HRESULT hr = S_OK; + + LOCKREAD(); + + _ASSERTE(TypeFromToken(mdmr) == mdtManifestResource && RidFromToken(mdmr)); + IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(RidFromToken(mdmr), &pRecord)); + + if (pszName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getNameOfManifestResource(pRecord, pszName)); + } + if (ptkImplementation) + *ptkImplementation = m_pStgdb->m_MiniMd.getImplementationOfManifestResource(pRecord); + if (pdwOffset) + *pdwOffset = m_pStgdb->m_MiniMd.getOffsetOfManifestResource(pRecord); + if (pdwResourceFlags) + *pdwResourceFlags = m_pStgdb->m_MiniMd.getFlagsOfManifestResource(pRecord); + +ErrExit: + return hr; +} // MDInternalRW::GetManifestResourceProps + +//***************************************************************************** +// Find the ExportedType given the name. +//***************************************************************************** +__checkReturn +STDMETHODIMP MDInternalRW::FindExportedTypeByName( // S_OK or error + LPCSTR szNamespace, // [IN] Namespace of the ExportedType. + LPCSTR szName, // [IN] Name of the ExportedType. + mdExportedType tkEnclosingType, // [IN] Enclosing ExportedType. + mdExportedType *pmct) // [OUT] Put ExportedType token here. +{ + _ASSERTE(szName && pmct); + HRESULT hr = S_OK; + LOCKREADIFFAILRET(); + + IMetaModelCommon *pCommon = static_cast<IMetaModelCommon*>(&m_pStgdb->m_MiniMd); + return pCommon->CommonFindExportedType(szNamespace, szName, tkEnclosingType, pmct); +} // MDInternalRW::FindExportedTypeByName + +//***************************************************************************** +// Find the ManifestResource given the name. +//***************************************************************************** +__checkReturn +STDMETHODIMP MDInternalRW::FindManifestResourceByName(// S_OK or error + LPCSTR szName, // [IN] Name of the resource. + mdManifestResource *pmmr) // [OUT] Put ManifestResource token here. +{ + _ASSERTE(szName && pmmr); + + ManifestResourceRec *pRecord; + ULONG cRecords; // Count of records. + LPCUTF8 szNameTmp = 0; // Name obtained from the database. + ULONG i; + HRESULT hr = S_OK; + + LOCKREAD(); + + cRecords = m_pStgdb->m_MiniMd.getCountManifestResources(); + + // Search for the ExportedType. + for (i = 1; i <= cRecords; i++) + { + IfFailGo(m_pStgdb->m_MiniMd.GetManifestResourceRecord(i, &pRecord)); + IfFailGo(m_pStgdb->m_MiniMd.getNameOfManifestResource(pRecord, &szNameTmp)); + if (! strcmp(szName, szNameTmp)) + { + *pmmr = TokenFromRid(i, mdtManifestResource); + goto ErrExit; + } + } + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + + return hr; +} // MDInternalRW::FindManifestResourceByName + +//***************************************************************************** +// Get the Assembly token from the given scope. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetAssemblyFromScope( // S_OK or error + mdAssembly *ptkAssembly) // [OUT] Put token here. +{ + _ASSERTE(ptkAssembly); + + if (m_pStgdb->m_MiniMd.getCountAssemblys()) + { + *ptkAssembly = TokenFromRid(1, mdtAssembly); + return S_OK; + } + else + return CLDB_E_RECORD_NOTFOUND; +} // MDInternalRW::GetAssemblyFromScope + +//******************************************************************************* +// return properties regarding a TypeSpec +//******************************************************************************* +//******************************************************************************* +// return properties regarding a TypeSpec +//******************************************************************************* +__checkReturn +HRESULT MDInternalRW::GetTypeSpecFromToken( // S_OK or error. + mdTypeSpec typespec, // [IN] Signature token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token. + ULONG *pcbSig) // [OUT] return size of signature. +{ + HRESULT hr = NOERROR; + + _ASSERTE(TypeFromToken(typespec) == mdtTypeSpec); + _ASSERTE(ppvSig && pcbSig); + + if (!IsValidToken(typespec)) + return E_INVALIDARG; + + TypeSpecRec *pRec; + IfFailRet(m_pStgdb->m_MiniMd.GetTypeSpecRecord(RidFromToken(typespec), &pRec)); + + if (pRec == NULL) + return CLDB_E_FILE_CORRUPT; + + IfFailRet(m_pStgdb->m_MiniMd.getSignatureOfTypeSpec(pRec, ppvSig, pcbSig)); + + return hr; +} // MDInternalRW::GetTypeSpecFromToken + + +//***************************************************************************** +// Return contents of Pinvoke given the forwarded member token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetPinvokeMap( + mdToken tk, // [IN] FieldDef, MethodDef or MethodImpl. + DWORD *pdwMappingFlags, // [OUT] Flags used for mapping. + LPCSTR *pszImportName, // [OUT] Import name. + mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL. +{ + ImplMapRec *pRecord; + ULONG iRecord; + HRESULT hr = S_OK; + + LOCKREAD(); + + IfFailGo(m_pStgdb->m_MiniMd.FindImplMapHelper(tk, &iRecord)); + if (InvalidRid(iRecord)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + else + IfFailGo(m_pStgdb->m_MiniMd.GetImplMapRecord(iRecord, &pRecord)); + + if (pdwMappingFlags) + *pdwMappingFlags = m_pStgdb->m_MiniMd.getMappingFlagsOfImplMap(pRecord); + if (pszImportName != NULL) + { + IfFailGo(m_pStgdb->m_MiniMd.getImportNameOfImplMap(pRecord, pszImportName)); + } + if (pmrImportDLL) + *pmrImportDLL = m_pStgdb->m_MiniMd.getImportScopeOfImplMap(pRecord); +ErrExit: + return hr; +} // MDInternalRW::GetPinvokeMap + +//***************************************************************************** +// convert a text signature to com format +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::ConvertTextSigToComSig(// Return hresult. + BOOL fCreateTrIfNotFound, // create typeref if not found or not + LPCSTR pSignature, // class file format signature + CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature + ULONG *pcbCount) // [OUT] the result size of signature +{ + return E_NOTIMPL; +} // _ConvertTextSigToComSig + +//***************************************************************************** +// This is a way for the EE to associate some data with this RW metadata to +// be released when this RW goes away. This is useful when a RO metadata is +// converted to RW, because arbitrary threads can be executing in the RO. +// So, we hold onto the RO here, and when the module shuts down, we release it. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::SetUserContextData(// S_OK or E_NOTIMPL + IUnknown *pIUnk) // The user context. +{ + // Only one chance to do this. + if (m_pUserUnk) + return E_UNEXPECTED; + m_pUserUnk = pIUnk; + return S_OK; +} // MDInternalRW::SetUserContextData + +//***************************************************************************** +// determine if a token is valid or not +//***************************************************************************** +BOOL MDInternalRW::IsValidToken( // True or False. + mdToken tk) // [IN] Given token. +{ + RID rid = RidFromToken(tk); + // no need to lock on this function. + if (rid == 0) + { + return FALSE; + } + switch (TypeFromToken(tk)) + { + case mdtModule: + // can have only one module record + return (rid <= m_pStgdb->m_MiniMd.getCountModules()); + case mdtTypeRef: + return (rid <= m_pStgdb->m_MiniMd.getCountTypeRefs()); + case mdtTypeDef: + return (rid <= m_pStgdb->m_MiniMd.getCountTypeDefs()); + case mdtFieldDef: + return (rid <= m_pStgdb->m_MiniMd.getCountFields()); + case mdtMethodDef: + return (rid <= m_pStgdb->m_MiniMd.getCountMethods()); + case mdtParamDef: + return (rid <= m_pStgdb->m_MiniMd.getCountParams()); + case mdtInterfaceImpl: + return (rid <= m_pStgdb->m_MiniMd.getCountInterfaceImpls()); + case mdtMemberRef: + return (rid <= m_pStgdb->m_MiniMd.getCountMemberRefs()); + case mdtCustomAttribute: + return (rid <= m_pStgdb->m_MiniMd.getCountCustomAttributes()); + case mdtPermission: + return (rid <= m_pStgdb->m_MiniMd.getCountDeclSecuritys()); + case mdtSignature: + return (rid <= m_pStgdb->m_MiniMd.getCountStandAloneSigs()); + case mdtEvent: + return (rid <= m_pStgdb->m_MiniMd.getCountEvents()); + case mdtProperty: + return (rid <= m_pStgdb->m_MiniMd.getCountPropertys()); + case mdtModuleRef: + return (rid <= m_pStgdb->m_MiniMd.getCountModuleRefs()); + case mdtTypeSpec: + return (rid <= m_pStgdb->m_MiniMd.getCountTypeSpecs()); + case mdtAssembly: + return (rid <= m_pStgdb->m_MiniMd.getCountAssemblys()); + case mdtAssemblyRef: + return (rid <= m_pStgdb->m_MiniMd.getCountAssemblyRefs()); + case mdtFile: + return (rid <= m_pStgdb->m_MiniMd.getCountFiles()); + case mdtExportedType: + return (rid <= m_pStgdb->m_MiniMd.getCountExportedTypes()); + case mdtManifestResource: + return (rid <= m_pStgdb->m_MiniMd.getCountManifestResources()); + case mdtMethodSpec: + return (rid <= m_pStgdb->m_MiniMd.getCountMethodSpecs()); + case mdtString: + // need to check the user string heap + return m_pStgdb->m_MiniMd.m_UserStringHeap.IsValidIndex(rid); + } + return FALSE; +} // MDInternalRW::IsValidToken + +mdModule MDInternalRW::GetModuleFromScope(void) +{ + return TokenFromRid(1, mdtModule); +} // MDInternalRW::GetModuleFromScope + +//***************************************************************************** +// Given a MetaData with ENC changes, apply those changes to this MetaData. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::ApplyEditAndContinue( // S_OK or error. + MDInternalRW *pDeltaMD) // Interface to MD with the ENC delta. +{ + HRESULT hr; // A result. + // Get the MiniMd on the delta. + + LOCKWRITEIFFAILRET(); + + CMiniMdRW &mdDelta = pDeltaMD->m_pStgdb->m_MiniMd; + CMiniMdRW &mdBase = m_pStgdb->m_MiniMd; + + + IfFailGo(mdBase.ConvertToRW()); + IfFailGo(mdBase.ApplyDelta(mdDelta)); +ErrExit: + return hr; +} // MDInternalRW::ApplyEditAndContinue + +//***************************************************************************** +// Given a MetaData with ENC changes, enumerate the changed tokens. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::EnumDeltaTokensInit( // return hresult + HENUMInternal *phEnum) // Enumerator to initialize. +{ + HRESULT hr = S_OK; // A result. + ULONG index; // Loop control. + ENCLogRec *pRec; // An ENCLog record. + + // Vars for query. + _ASSERTE(phEnum); + HENUMInternal::ZeroEnum(phEnum); + + // cache the tkKind and the scope + phEnum->m_tkKind = 0; + + phEnum->m_EnumType = MDSimpleEnum; + + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = 1; index <= m_pStgdb->m_MiniMd.m_Schema.m_cRecs[TBL_ENCLog]; ++index) + { + // Get the token type; see if it is a real token. + IfFailGo(m_pStgdb->m_MiniMd.GetENCLogRecord(index, &pRec)); + if (CMiniMdRW::IsRecId(pRec->GetToken())) + continue; + // If there is a function code, that means that this flags a child-record + // addition. The child record will generate its own token, so did the + // parent, so skip the record. + if (pRec->GetFuncCode()) + continue; + + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + pRec->GetToken())); + } + +ErrExit: + // we are done + return hr; +} // MDInternalRW::EnumDeltaTokensInit + + +//***************************************************************************** +// Static function to apply a delta md. This is what the EE calls to apply +// the metadata updates from an update PE to live metadata. +// <TODO>MAY REPLACE THE IMDInternalImport POINTER!</TODO> +//***************************************************************************** +__checkReturn +HRESULT MDApplyEditAndContinue( // S_OK or error. + IMDInternalImport **ppIMD, // [in, out] The metadata to be updated. + IMDInternalImportENC *pDeltaMD) // [in] The delta metadata. +{ + HRESULT hr; // A result. + IMDInternalImportENC *pENC; // ENC interface on the metadata. + + // If the input metadata isn't RW, convert it. + hr = (*ppIMD)->QueryInterface(IID_IMDInternalImportENC, (void**)&pENC); + if (FAILED(hr)) + { + IfFailGo(ConvertRO2RW(*ppIMD, IID_IMDInternalImportENC, (void**)&pENC)); + // Replace the old interface pointer with the ENC one. + (*ppIMD)->Release(); + IfFailGo(pENC->QueryInterface(IID_IMDInternalImport, (void**)ppIMD)); + } + + // Apply the delta to the input metadata. + hr = pENC->ApplyEditAndContinue(static_cast<MDInternalRW*>(pDeltaMD)); + +ErrExit: + if (pENC) + pENC->Release(); + return hr; +} // MDApplyEditAndContinue + +//***************************************************************************** +// Given a scope, return the table size and table ptr for a given index +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::GetTableInfoWithIndex( // return size + ULONG index, // [IN] pass in the index + void **pTable, // [OUT] pointer to table at index + void **pTableSize) // [OUT] size of table at index +{ + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//***************************************************************************** +// Given a delta metadata byte stream, apply the changes to the current metadata +// object returning the resulting metadata object in ppv +//***************************************************************************** +__checkReturn +HRESULT MDInternalRW::ApplyEditAndContinue( + void *pDeltaMD, // [IN] the delta metadata + ULONG cbDeltaMD, // [IN] length of pData + IMDInternalImport **ppv) // [OUT] the resulting metadata interface +{ + _ASSERTE(pDeltaMD); + _ASSERTE(ppv); + + HRESULT hr = E_FAIL; + IMDInternalImportENC *pDeltaMDImport = NULL; + + IfFailGo(GetInternalWithRWFormat(pDeltaMD, cbDeltaMD, 0, IID_IMDInternalImportENC, (void**)&pDeltaMDImport)); + + *ppv = this; + IfFailGo(MDApplyEditAndContinue(ppv, pDeltaMDImport)); + +ErrExit: + if (pDeltaMDImport) + pDeltaMDImport->Release(); + + return hr; +} // MDInternalRW::ApplyEditAndContinue + +#endif //FEATURE_METADATA_INTERNAL_APIS diff --git a/src/coreclr/md/enc/metamodelenc.cpp b/src/coreclr/md/enc/metamodelenc.cpp new file mode 100644 index 00000000000..5dc4b58bd59 --- /dev/null +++ b/src/coreclr/md/enc/metamodelenc.cpp @@ -0,0 +1,445 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaModelENC.cpp +// + +// +// Implementation for applying ENC deltas to a MiniMd. +// +//***************************************************************************** +#include "stdafx.h" +#include <limits.h> +#include <posterror.h> +#include <metamodelrw.h> +#include <stgio.h> +#include <stgtiggerstorage.h> +#include "mdlog.h" +#include "rwutil.h" + +ULONG CMiniMdRW::m_SuppressedDeltaColumns[TBL_COUNT] = {0}; + +//***************************************************************************** +// Copy the data from one MiniMd to another. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ApplyRecordDelta( + CMiniMdRW &mdDelta, // The delta MetaData. + ULONG ixTbl, // The table with the data. + void *pDelta, // The delta MetaData record. + void *pRecord) // The record to update. +{ + HRESULT hr = S_OK; + ULONG mask = m_SuppressedDeltaColumns[ixTbl]; + + for (ULONG ixCol = 0; ixCol<m_TableDefs[ixTbl].m_cCols; ++ixCol, mask >>= 1) + { // Skip certain pointer columns. + if (mask & 0x01) + continue; + + ULONG val = mdDelta.GetCol(ixTbl, ixCol, pDelta); + IfFailRet(PutCol(ixTbl, ixCol, pRecord, val)); + } + return hr; +} // CMiniMdRW::ApplyRecordDelta + +//***************************************************************************** +// Apply a delta record to a table, generically. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ApplyTableDelta( + CMiniMdRW &mdDelta, // Interface to MD with the ENC delta. + ULONG ixTbl, // Table index to update. + RID iRid, // RID of the changed item. + int fc) // Function code of update. +{ + HRESULT hr = S_OK; + void *pRec; // Record in existing MetaData. + void *pDeltaRec; // Record if Delta MetaData. + RID newRid; // Rid of new record. + + // Get the delta record. + IfFailGo(mdDelta.GetDeltaRecord(ixTbl, iRid, &pDeltaRec)); + // Get the record from the base metadata. + if (iRid > m_Schema.m_cRecs[ixTbl]) + { // Added record. Each addition is the next one. + _ASSERTE(iRid == m_Schema.m_cRecs[ixTbl] + 1); + switch (ixTbl) + { + case TBL_TypeDef: + IfFailGo(AddTypeDefRecord(reinterpret_cast<TypeDefRec **>(&pRec), &newRid)); + break; + case TBL_Method: + IfFailGo(AddMethodRecord(reinterpret_cast<MethodRec **>(&pRec), &newRid)); + break; + case TBL_EventMap: + IfFailGo(AddEventMapRecord(reinterpret_cast<EventMapRec **>(&pRec), &newRid)); + break; + case TBL_PropertyMap: + IfFailGo(AddPropertyMapRecord(reinterpret_cast<PropertyMapRec **>(&pRec), &newRid)); + break; + default: + IfFailGo(AddRecord(ixTbl, &pRec, &newRid)); + break; + } + IfNullGo(pRec); + _ASSERTE(iRid == newRid); + } + else + { // Updated record. + IfFailGo(getRow(ixTbl, iRid, &pRec)); + } + + // Copy the record info. + IfFailGo(ApplyRecordDelta(mdDelta, ixTbl, pDeltaRec, pRec)); + +ErrExit: + return hr; +} // CMiniMdRW::ApplyTableDelta + +//***************************************************************************** +// Get the record from a Delta MetaData that corresponds to the actual record. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetDeltaRecord( + ULONG ixTbl, // Table. + ULONG iRid, // Record in the table. + void **ppRecord) +{ + HRESULT hr; + ULONG iMap; // RID in map table. + ENCMapRec *pMap; // Row in map table. + + *ppRecord = NULL; + // If no remap, just return record directly. + if ((m_Schema.m_cRecs[TBL_ENCMap] == 0) || (ixTbl == TBL_Module) || !IsMinimalDelta()) + { + return getRow(ixTbl, iRid, ppRecord); + } + + // Use the remap table to find the physical row containing this logical row. + iMap = (*m_rENCRecs)[ixTbl]; + IfFailRet(GetENCMapRecord(iMap, &pMap)); + + // Search for desired record. + while ((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) < iRid)) + { + IfFailRet(GetENCMapRecord(++iMap, &pMap)); + } + + _ASSERTE((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) == iRid)); + + // Relative position within table's group in map is physical rid. + iRid = iMap - (*m_rENCRecs)[ixTbl] + 1; + + return getRow(ixTbl, iRid, ppRecord); +} // CMiniMdRW::GetDeltaRecord + +//***************************************************************************** +// Given a MetaData with ENC changes, apply those changes to this MetaData. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ApplyHeapDeltas( + CMiniMdRW &mdDelta) // Interface to MD with the ENC delta. +{ + if (mdDelta.IsMinimalDelta()) + { + return ApplyHeapDeltasWithMinimalDelta(mdDelta); + } + else + { + return ApplyHeapDeltasWithFullDelta(mdDelta); + } +}// CMiniMdRW::ApplyHeapDeltas + +__checkReturn +HRESULT +CMiniMdRW::ApplyHeapDeltasWithMinimalDelta( + CMiniMdRW &mdDelta) // Interface to MD with the ENC delta. +{ + HRESULT hr = S_OK; + + // Extend the heaps with EnC minimal delta + IfFailGo(m_StringHeap.AddStringHeap( + &(mdDelta.m_StringHeap), + 0)); // Start offset in the mdDelta + IfFailGo(m_BlobHeap.AddBlobHeap( + &(mdDelta.m_BlobHeap), + 0)); // Start offset in the mdDelta + IfFailGo(m_UserStringHeap.AddBlobHeap( + &(mdDelta.m_UserStringHeap), + 0)); // Start offset in the mdDelta + // We never do a minimal delta with the guid heap + IfFailGo(m_GuidHeap.AddGuidHeap( + &(mdDelta.m_GuidHeap), + m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap + +ErrExit: + return hr; +} // CMiniMdRW::ApplyHeapDeltasWithMinimalDelta + +__checkReturn +HRESULT +CMiniMdRW::ApplyHeapDeltasWithFullDelta( + CMiniMdRW &mdDelta) // Interface to MD with the ENC delta. +{ + HRESULT hr = S_OK; + + // Extend the heaps with EnC full delta + IfFailRet(m_StringHeap.AddStringHeap( + &(mdDelta.m_StringHeap), + m_StringHeap.GetUnalignedSize())); // Starting offset in the full delta string heap + IfFailRet(m_BlobHeap.AddBlobHeap( + &(mdDelta.m_BlobHeap), + m_BlobHeap.GetUnalignedSize())); // Starting offset in the full delta blob heap + IfFailRet(m_UserStringHeap.AddBlobHeap( + &(mdDelta.m_UserStringHeap), + m_UserStringHeap.GetUnalignedSize())); // Starting offset in the full delta user string heap + IfFailRet(m_GuidHeap.AddGuidHeap( + &(mdDelta.m_GuidHeap), + m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap + + return hr; +} // CMiniMdRW::ApplyHeapDeltasWithFullDelta + +//***************************************************************************** +// Driver for the delta process. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ApplyDelta( + CMiniMdRW &mdDelta) // Interface to MD with the ENC delta. +{ + HRESULT hr = S_OK; + ULONG iENC; // Loop control. + ULONG iRid; // RID of some record. + ULONG iNew; // RID of a new record. + int i; // Loop control. + ULONG ixTbl; // A table. + +#ifdef _DEBUG + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_ApplyDeltaBreak)) + { + _ASSERTE(!"CMiniMDRW::ApplyDelta()"); + } +#endif // _DEBUG + + // Init the suppressed column table. We know this one isn't zero... + if (m_SuppressedDeltaColumns[TBL_TypeDef] == 0) + { + m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList); + m_SuppressedDeltaColumns[TBL_PropertyMap] = (1 << PropertyMapRec::COL_PropertyList); + m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList); + m_SuppressedDeltaColumns[TBL_Method] = (1 << MethodRec::COL_ParamList); + m_SuppressedDeltaColumns[TBL_TypeDef] = (1 << TypeDefRec::COL_FieldList)|(1<<TypeDefRec::COL_MethodList); + } + + // Verify the version of the MD. + if (m_Schema.m_major != mdDelta.m_Schema.m_major || + m_Schema.m_minor != mdDelta.m_Schema.m_minor) + { + _ASSERTE(!"Version of Delta MetaData is a incompatible with current MetaData."); + //<TODO>@FUTURE: unique error in the future since we are not shipping ENC.</TODO> + return E_INVALIDARG; + } + + // verify MVIDs. + ModuleRec *pModDelta; + ModuleRec *pModBase; + IfFailGo(mdDelta.GetModuleRecord(1, &pModDelta)); + IfFailGo(GetModuleRecord(1, &pModBase)); + GUID GuidDelta; + GUID GuidBase; + IfFailGo(mdDelta.getMvidOfModule(pModDelta, &GuidDelta)); + IfFailGo(getMvidOfModule(pModBase, &GuidBase)); + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) && (GuidDelta != GuidBase)) + { + _ASSERTE(!"Delta MetaData has different base than current MetaData."); + return E_INVALIDARG; + } + + + // Let the other md prepare for sparse records. + IfFailGo(mdDelta.StartENCMap()); + + // Fix the heaps. + IfFailGo(ApplyHeapDeltas(mdDelta)); + + // Truncate some tables in preparation to copy in new ENCLog data. + for (i = 0; (ixTbl = m_TruncatedEncTables[i]) != (ULONG)-1; ++i) + { + m_Tables[ixTbl].Delete(); + IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount( + m_TableDefs[ixTbl].m_cbRec, + mdDelta.m_Schema.m_cRecs[ixTbl] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl)); + m_Schema.m_cRecs[ixTbl] = 0; + } + + // For each record in the ENC log... + for (iENC = 1; iENC <= mdDelta.m_Schema.m_cRecs[TBL_ENCLog]; ++iENC) + { + // Get the record, and the updated token. + ENCLogRec *pENC; + IfFailGo(mdDelta.GetENCLogRecord(iENC, &pENC)); + ENCLogRec *pENC2; + IfFailGo(AddENCLogRecord(&pENC2, &iNew)); + IfNullGo(pENC2); + ENCLogRec *pENC3; + _ASSERTE(iNew == iENC); + ULONG func = pENC->GetFuncCode(); + pENC2->SetFuncCode(pENC->GetFuncCode()); + pENC2->SetToken(pENC->GetToken()); + + // What kind of record is this? + if (IsRecId(pENC->GetToken())) + { // Non-token table + iRid = RidFromRecId(pENC->GetToken()); + ixTbl = TblFromRecId(pENC->GetToken()); + } + else + { // Token table. + iRid = RidFromToken(pENC->GetToken()); + ixTbl = GetTableForToken(pENC->GetToken()); + } + + RID rid_Ignore; + // Switch based on the function code. + switch (func) + { + case eDeltaMethodCreate: + // Next ENC record will define the new Method. + MethodRec *pMethodRecord; + IfFailGo(AddMethodRecord(&pMethodRecord, &rid_Ignore)); + IfFailGo(AddMethodToTypeDef(iRid, m_Schema.m_cRecs[TBL_Method])); + break; + + case eDeltaParamCreate: + // Next ENC record will define the new Param. This record is + // tricky because params will be re-ordered based on their sequence, + // but the sequence isn't set until the NEXT record is applied. + // So, for ParamCreate only, apply the param record delta before + // adding the parent-child linkage. + ParamRec *pParamRecord; + IfFailGo(AddParamRecord(&pParamRecord, &rid_Ignore)); + + // Should have recorded a Param delta after the Param add. + _ASSERTE(iENC<mdDelta.m_Schema.m_cRecs[TBL_ENCLog]); + IfFailGo(mdDelta.GetENCLogRecord(iENC+1, &pENC3)); + _ASSERTE(pENC3->GetFuncCode() == 0); + _ASSERTE(GetTableForToken(pENC3->GetToken()) == TBL_Param); + IfFailGo(ApplyTableDelta(mdDelta, TBL_Param, RidFromToken(pENC3->GetToken()), eDeltaFuncDefault)); + + // Now that Param record is OK, set up linkage. + IfFailGo(AddParamToMethod(iRid, m_Schema.m_cRecs[TBL_Param])); + break; + + case eDeltaFieldCreate: + // Next ENC record will define the new Field. + FieldRec *pFieldRecord; + IfFailGo(AddFieldRecord(&pFieldRecord, &rid_Ignore)); + IfFailGo(AddFieldToTypeDef(iRid, m_Schema.m_cRecs[TBL_Field])); + break; + + case eDeltaPropertyCreate: + // Next ENC record will define the new Property. + PropertyRec *pPropertyRecord; + IfFailGo(AddPropertyRecord(&pPropertyRecord, &rid_Ignore)); + IfFailGo(AddPropertyToPropertyMap(iRid, m_Schema.m_cRecs[TBL_Property])); + break; + + case eDeltaEventCreate: + // Next ENC record will define the new Event. + EventRec *pEventRecord; + IfFailGo(AddEventRecord(&pEventRecord, &rid_Ignore)); + IfFailGo(AddEventToEventMap(iRid, m_Schema.m_cRecs[TBL_Event])); + break; + + case eDeltaFuncDefault: + IfFailGo(ApplyTableDelta(mdDelta, ixTbl, iRid, func)); + break; + + default: + _ASSERTE(!"Unexpected function in ApplyDelta"); + IfFailGo(E_UNEXPECTED); + break; + } + } + m_Schema.m_cRecs[TBL_ENCLog] = mdDelta.m_Schema.m_cRecs[TBL_ENCLog]; + +ErrExit: + // Store the result for returning (IfFailRet will modify hr) + HRESULT hrReturn = hr; + IfFailRet(mdDelta.EndENCMap()); + + + return hrReturn; +} // CMiniMdRW::ApplyDelta + +//***************************************************************************** +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::StartENCMap() // S_OK or error. +{ + HRESULT hr = S_OK; + ULONG iENC; // Loop control. + ULONG ixTbl; // A table. + int ixTblPrev = -1; // Table previously seen. + + _ASSERTE(m_rENCRecs == 0); + + if (m_Schema.m_cRecs[TBL_ENCMap] == 0) + return S_OK; + + // Build an array of pointers into the ENCMap table for fast access to the ENCMap + // for each table. + m_rENCRecs = new (nothrow) ULONGARRAY; + IfNullGo(m_rENCRecs); + if (!m_rENCRecs->AllocateBlock(TBL_COUNT)) + IfFailGo(E_OUTOFMEMORY); + for (iENC = 1; iENC <= m_Schema.m_cRecs[TBL_ENCMap]; ++iENC) + { + ENCMapRec *pMap; + IfFailGo(GetENCMapRecord(iENC, &pMap)); + ixTbl = TblFromRecId(pMap->GetToken()); + _ASSERTE((int)ixTbl >= ixTblPrev); + _ASSERTE(ixTbl < TBL_COUNT); + _ASSERTE(ixTbl != TBL_ENCMap); + _ASSERTE(ixTbl != TBL_ENCLog); + if ((int)ixTbl == ixTblPrev) + continue; + // Catch up on any skipped tables. + while (ixTblPrev < (int)ixTbl) + { + (*m_rENCRecs)[++ixTblPrev] = iENC; + } + } + while (ixTblPrev < TBL_COUNT-1) + { + (*m_rENCRecs)[++ixTblPrev] = iENC; + } + +ErrExit: + return hr; +} // CMiniMdRW::StartENCMap + +//***************************************************************************** +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::EndENCMap() +{ + if (m_rENCRecs != NULL) + { + delete m_rENCRecs; + m_rENCRecs = NULL; + } + + return S_OK; +} // CMiniMdRW::EndENCMap diff --git a/src/coreclr/md/enc/metamodelrw.cpp b/src/coreclr/md/enc/metamodelrw.cpp new file mode 100644 index 00000000000..c48dc5a36b3 --- /dev/null +++ b/src/coreclr/md/enc/metamodelrw.cpp @@ -0,0 +1,8906 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaModelRW.cpp +// + +// +// Implementation for the Read/Write MiniMD code. +// +//***************************************************************************** +#include "stdafx.h" +#include <limits.h> +#include <posterror.h> +#include <metamodelrw.h> +#include <stgio.h> +#include <stgtiggerstorage.h> +#include "mdlog.h" +#include "rwutil.h" +#include "../compiler/importhelper.h" +#include "metadata.h" +#include "streamutil.h" + +#include "../hotdata/hotdataformat.h" + +#ifdef FEATURE_PREJIT +#include "corcompile.h" +#endif + +#ifdef _MSC_VER +#pragma intrinsic(memcpy) +#endif + +//********** RidMap *********************************************************** +typedef CDynArray<RID> RIDMAP; + + +//********** Types. *********************************************************** +#define INDEX_ROW_COUNT_THRESHOLD 25 + + +//********** Locals. ********************************************************** +enum MetaDataSizeIndex +{ + // Standard MetaData sizes (from VBA library). + MDSizeIndex_Standard = 0, + // Minimal MetaData sizes used mainly by Reflection.Emit for small assemblies (emitting 1 type per + // assembly). + // Motivated by the performance requirement in collectible types. + MDSizeIndex_Minimal = 1, + + MDSizeIndex_Count +}; // enum MetaDataSizeIndex + +// Gets index of MetaData sizes used to access code:g_PoolSizeInfo, code:g_HashSize and code:g_TblSizeInfo. +static +enum MetaDataSizeIndex +GetMetaDataSizeIndex(const OptionValue *pOptionValue) +{ + if (pOptionValue->m_InitialSize == MDInitialSizeMinimal) + { + return MDSizeIndex_Minimal; + } + _ASSERTE(pOptionValue->m_InitialSize == MDInitialSizeDefault); + return MDSizeIndex_Standard; +} // GetSizeHint + +#define IX_STRING_POOL 0 +#define IX_US_BLOB_POOL 1 +#define IX_GUID_POOL 2 +#define IX_BLOB_POOL 3 + +static +const ULONG +g_PoolSizeInfo[MDSizeIndex_Count][4][2] = +{ + { // Standard pool sizes { Size in bytes, Number of buckets in hash } (code:MDSizeIndex_Standard). + {20000, 449}, // Strings + {5000, 150}, // User literal string blobs + {256, 16}, // Guids + {20000, 449} // Blobs + }, + { // Minimal pool sizes { Size in bytes, Number of buckets in hash } (code:MDSizeIndex_Minimal). + {300, 10}, // Strings + {50, 5}, // User literal string blobs + {16, 3}, // Guids + {200, 10} // Blobs + } +}; + +static +const ULONG +g_HashSize[MDSizeIndex_Count] = +{ + 257, // Standard MetaData size (code:MDSizeIndex_Standard). + 50 // Minimal MetaData size (code:MDSizeIndex_Minimal). +}; + +static +const ULONG +g_TblSizeInfo[MDSizeIndex_Count][TBL_COUNT] = +{ + // Standard table sizes (code:MDSizeIndex_Standard). + { + 1, // Module + 90, // TypeRef + 65, // TypeDef + 0, // FieldPtr + 400, // Field + 0, // MethodPtr + 625, // Method + 0, // ParamPtr + 1200, // Param + 6, // InterfaceImpl + 500, // MemberRef + 400, // Constant + 650, // CustomAttribute + 0, // FieldMarshal + 0, // DeclSecurity + 0, // ClassLayout + 0, // FieldLayout + 175, // StandAloneSig + 0, // EventMap + 0, // EventPtr + 0, // Event + 5, // PropertyMap + 0, // PropertyPtr + 25, // Property + 45, // MethodSemantics + 20, // MethodImpl + 0, // ModuleRef + 0, // TypeSpec + 0, // ImplMap + 0, // FieldRVA + 0, // ENCLog + 0, // ENCMap + 0, // Assembly + 0, // AssemblyProcessor + 0, // AssemblyOS + 0, // AssemblyRef + 0, // AssemblyRefProcessor + 0, // AssemblyRefOS + 0, // File + 0, // ExportedType + 0, // ManifestResource + 0, // NestedClass + 0, // GenericParam + 0, // MethodSpec + 0, // GenericParamConstraint +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + /* Dummy tables to fill the gap to 0x30 */ + 0, // Dummy1 + 0, // Dummy2 + 0, // Dummy3 + /* Actual portable PDB tables */ + 0, // Document + 0, // MethodDebugInformation + 0, // LocalScope + 0, // LocalVariable + 0, // LocalConstant + 0, // ImportScope + // TODO: + // 0, // StateMachineMethod + // 0, // CustomDebugInformation +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + }, + // Minimal table sizes (code:MDSizeIndex_Minimal). + { + 1, // Module + 2, // TypeRef + 2, // TypeDef + 0, // FieldPtr + 2, // Field + 0, // MethodPtr + 2, // Method + 0, // ParamPtr + 0, // Param + 0, // InterfaceImpl + 1, // MemberRef + 0, // Constant + 0, // CustomAttribute + 0, // FieldMarshal + 0, // DeclSecurity + 0, // ClassLayout + 0, // FieldLayout + 0, // StandAloneSig + 0, // EventMap + 0, // EventPtr + 0, // Event + 0, // PropertyMap + 0, // PropertyPtr + 0, // Property + 0, // MethodSemantics + 0, // MethodImpl + 0, // ModuleRef + 0, // TypeSpec + 0, // ImplMap + 0, // FieldRVA + 0, // ENCLog + 0, // ENCMap + 1, // Assembly + 0, // AssemblyProcessor + 0, // AssemblyOS + 1, // AssemblyRef + 0, // AssemblyRefProcessor + 0, // AssemblyRefOS + 0, // File + 0, // ExportedType + 0, // ManifestResource + 0, // NestedClass + 0, // GenericParam + 0, // MethodSpec + 0, // GenericParamConstraint +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + /* Dummy tables to fill the gap to 0x30 */ + 0, // Dummy1 + 0, // Dummy2 + 0, // Dummy3 + /* Actual portable PDB tables */ + 0, // Document + 0, // MethodDebugInformation + 0, // LocalScope + 0, // LocalVariable + 0, // LocalConstant + 0, // ImportScope + // TODO: + // 0, // StateMachineMethod + // 0, // CustomDebugInformation +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + } +}; // g_TblSizeInfo + +struct TblIndex +{ + ULONG m_iName; // Name column. + ULONG m_iParent; // Parent column, if any. + ULONG m_Token; // Token of the table. +}; + +// Table to drive generic named-item indexing. +const TblIndex g_TblIndex[TBL_COUNT] = +{ + {(ULONG) -1, (ULONG) -1, mdtModule}, // Module + {TypeRefRec::COL_Name, (ULONG) -1, mdtTypeRef}, // TypeRef + {TypeDefRec::COL_Name, (ULONG) -1, mdtTypeDef}, // TypeDef + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldPtr + {(ULONG) -1, (ULONG) -1, mdtFieldDef}, // Field + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // MethodPtr + {(ULONG) -1, (ULONG) -1, mdtMethodDef}, // Method + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ParamPtr + {(ULONG) -1, (ULONG) -1, mdtParamDef}, // Param + {(ULONG) -1, (ULONG) -1, mdtInterfaceImpl}, // InterfaceImpl + {MemberRefRec::COL_Name, MemberRefRec::COL_Class, mdtMemberRef}, // MemberRef + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // Constant + {(ULONG) -1, (ULONG) -1, mdtCustomAttribute},// CustomAttribute + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldMarshal + {(ULONG) -1, (ULONG) -1, mdtPermission}, // DeclSecurity + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ClassLayout + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldLayout + {(ULONG) -1, (ULONG) -1, mdtSignature}, // StandAloneSig + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // EventMap + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // EventPtr + {(ULONG) -1, (ULONG) -1, mdtEvent}, // Event + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // PropertyMap + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // PropertyPtr + {(ULONG) -1, (ULONG) -1, mdtProperty}, // Property + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // MethodSemantics + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // MethodImpl + {(ULONG) -1, (ULONG) -1, mdtModuleRef}, // ModuleRef + {(ULONG) -1, (ULONG) -1, mdtTypeSpec}, // TypeSpec + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ImplMap <TODO>@FUTURE: Check that these are the right entries here.</TODO> + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // FieldRVA <TODO>@FUTURE: Check that these are the right entries here.</TODO> + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ENCLog + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // ENCMap + {(ULONG) -1, (ULONG) -1, mdtAssembly}, // Assembly <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyProcessor <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyOS <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, mdtAssemblyRef}, // AssemblyRef <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyRefProcessor <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // AssemblyRefOS <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, mdtFile}, // File <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, mdtExportedType}, // ExportedType <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, mdtManifestResource},// ManifestResource <TODO>@FUTURE: Update with the right number.</TODO> + {(ULONG) -1, (ULONG) -1, (ULONG) -1}, // NestedClass + {(ULONG) -1, (ULONG) -1, mdtGenericParam}, // GenericParam + {(ULONG) -1, (ULONG) -1, mdtMethodSpec}, // MethodSpec + {(ULONG) -1, (ULONG) -1, mdtGenericParamConstraint},// GenericParamConstraint +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + /* Dummy tables to fill the gap to 0x30 */ + {(ULONG)-1, (ULONG)-1, (ULONG)-1}, // Dummy1 + {(ULONG)-1, (ULONG)-1, (ULONG)-1}, // Dummy2 + {(ULONG)-1, (ULONG)-1, (ULONG)-1}, // Dummy3 + /* Actual portable PDB tables */ + {(ULONG)-1, (ULONG)-1, mdtDocument}, // Document + {(ULONG)-1, (ULONG)-1, mdtMethodDebugInformation},// MethodDebugInformation + {(ULONG)-1, (ULONG)-1, mdtLocalScope}, // LocalScope + {(ULONG)-1, (ULONG)-1, mdtLocalVariable}, // LocalVariable + {(ULONG)-1, (ULONG)-1, mdtLocalConstant}, // LocalConstant + {(ULONG) -1, (ULONG) -1, mdtImportScope}, // ImportScope + // TODO: + // {(ULONG) -1, (ULONG) -1, mdtStateMachineMethod},// StateMachineMethod + // {(ULONG) -1, (ULONG) -1, mdtCustomDebugInformation},// CustomDebugInformation +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB +}; + +ULONG CMiniMdRW::m_TruncatedEncTables[] = +{ + TBL_ENCLog, + TBL_ENCMap, + (ULONG) -1 +}; + +//***************************************************************************** +// Given a token type, return the table index. +//***************************************************************************** +ULONG CMiniMdRW::GetTableForToken( // Table index, or -1. + mdToken tkn) // Token to find. +{ + ULONG ixTbl; // Loop control. + ULONG type = TypeFromToken(tkn); + + // Get the type -- if a string, no associated table. + if (type >= mdtString) + return (ULONG) -1; + // Table number is same as high-byte of token. + ixTbl = type >> 24; + // Make sure. + _ASSERTE(g_TblIndex[ixTbl].m_Token == type); + + return ixTbl; +} // CMiniMdRW::GetTableForToken + +//***************************************************************************** +// Given a Table index, return the Token type. +//***************************************************************************** +mdToken CMiniMdRW::GetTokenForTable( // Token type, or -1. + ULONG ixTbl) // Table index. +{ + _ASSERTE(g_TblIndex[ixTbl].m_Token == (ixTbl<<24) || g_TblIndex[ixTbl].m_Token == (ULONG) -1); + return g_TblIndex[ixTbl].m_Token; +} // CMiniMdRW::GetTokenForTable + +//***************************************************************************** +// Helper classes for sorting MiniMdRW tables. +//***************************************************************************** +class CQuickSortMiniMdRW +{ +protected: + CMiniMdRW &m_MiniMd; // The MiniMd with the data. + ULONG m_ixTbl; // The table. + ULONG m_ixCol; // The column. + int m_iCount; // How many items in array. + int m_iElemSize; // Size of one element. + RIDMAP *m_pRidMap; // Rid map that need to be swapped as we swap data + bool m_bMapToken; // MapToken handling desired. + + BYTE m_buf[128]; // For swapping. + + HRESULT getRow(UINT32 nIndex, void **ppRecord) + { + return m_MiniMd.m_Tables[m_ixTbl].GetRecord(nIndex, reinterpret_cast<BYTE **>(ppRecord)); + } + void SetSorted() { m_MiniMd.SetSorted(m_ixTbl, true); } + + HRESULT PrepMapTokens() + { + HRESULT hr = S_OK; + + // If remap notifications are desired, prepare to collect the info in a RIDMAP. + if (m_bMapToken) + { + _ASSERTE(m_pRidMap == NULL); // Don't call twice. + IfNullGo(m_pRidMap = new (nothrow) RIDMAP); + if (!m_pRidMap->AllocateBlock(m_iCount + 1)) + { + delete m_pRidMap; + m_pRidMap = NULL; + IfFailGo(E_OUTOFMEMORY); + } + for (int i=0; i<= m_iCount; ++i) + *(m_pRidMap->Get(i)) = i; + } + + ErrExit: + return hr; + } // CQuickSortMiniMdRW::PrepMapTokens + + __checkReturn + HRESULT DoMapTokens() + { + HRESULT hr; + if (m_bMapToken) + { + mdToken typ = m_MiniMd.GetTokenForTable(m_ixTbl); + for (int i=1; i<=m_iCount; ++i) + { + IfFailRet(m_MiniMd.MapToken(*(m_pRidMap->Get(i)), i, typ)); + } + } + return S_OK; + } // CQuickSortMiniMdRW::DoMapTokens + +public: + CQuickSortMiniMdRW( + CMiniMdRW &MiniMd, // MiniMd with the data. + ULONG ixTbl, // The table. + ULONG ixCol, // The column. + bool bMapToken) // If true, MapToken handling desired. + : m_MiniMd(MiniMd), + m_ixTbl(ixTbl), + m_ixCol(ixCol), + m_pRidMap(NULL), + m_bMapToken(bMapToken) + { + m_iElemSize = m_MiniMd.m_TableDefs[m_ixTbl].m_cbRec; + _ASSERTE(m_iElemSize <= (int) sizeof(m_buf)); + } + + ~CQuickSortMiniMdRW() + { + if (m_bMapToken) + { + if (m_pRidMap) + { + m_pRidMap->Clear(); + delete m_pRidMap; + m_pRidMap = NULL; + } + m_bMapToken = false; + } + } // CQuickSortMiniMdRW::~CQuickSortMiniMdRW + + // set the RidMap + void SetRidMap(RIDMAP *pRidMap) { m_pRidMap = pRidMap; } + + //***************************************************************************** + // Call to sort the array. + //***************************************************************************** + HRESULT Sort() + { + HRESULT hr = S_OK; + + INDEBUG(m_MiniMd.Debug_CheckIsLockedForWrite();) + + _ASSERTE(m_MiniMd.IsSortable(m_ixTbl)); + m_iCount = m_MiniMd.GetCountRecs(m_ixTbl); + + // If remap notifications are desired, prepare to collect the info in a RIDMAP. + IfFailGo(PrepMapTokens()); + + // We are going to sort tables. Invalidate the hash tables + if ( m_MiniMd.m_pLookUpHashs[m_ixTbl] != NULL ) + { + delete m_MiniMd.m_pLookUpHashs[m_ixTbl]; + m_MiniMd.m_pLookUpHashs[m_ixTbl] = NULL; + } + + IfFailGo(SortRange(1, m_iCount)); + + // The table is sorted until its next change. + SetSorted(); + + // If remap notifications were desired, send them. + IfFailGo(DoMapTokens()); + + ErrExit: + return hr; + } // CQuickSortMiniMdRW::Sort + + //***************************************************************************** + // Call to check whether the array is sorted without altering it. + //***************************************************************************** + HRESULT CheckSortedWithNoDuplicates() + { + HRESULT hr = S_OK; + int iCount = m_MiniMd.GetCountRecs(m_ixTbl); + int nResult; + + m_MiniMd.SetSorted(m_ixTbl, false); + + for (int i = 1; i < iCount; i++) + { + IfFailGo(Compare(i, i+1, &nResult)); + + if (nResult >= 0) + { + return S_OK; + } + } + + // The table is sorted until its next change. + SetSorted(); + + ErrExit: + return hr; + } // CQuickSortMiniMdRW::CheckSortedWithNoDuplicates + + //***************************************************************************** + // Override this function to do the comparison. + //***************************************************************************** + __checkReturn + HRESULT Compare( + int iLeft, // First item to compare. + int iRight, // Second item to compare. + int *pnResult) // -1, 0, or 1 + { + HRESULT hr; + void *pLeft; + void *pRight; + IfFailRet(getRow(iLeft, &pLeft)); + IfFailRet(getRow(iRight, &pRight)); + ULONG ulLeft = m_MiniMd.GetCol(m_ixTbl, m_ixCol, pLeft); + ULONG ulRight = m_MiniMd.GetCol(m_ixTbl, m_ixCol, pRight); + + if (ulLeft < ulRight) + { + *pnResult = -1; + return S_OK; + } + if (ulLeft == ulRight) + { + *pnResult = 0; + return S_OK; + } + *pnResult = 1; + return S_OK; + } // CQuickSortMiniMdRW::Compare + +private: + __checkReturn + HRESULT SortRange( + int iLeft, + int iRight) + { + HRESULT hr; + int iLast; + int nResult; + + for (;;) + { + // if less than two elements you're done. + if (iLeft >= iRight) + { + return S_OK; + } + + // The mid-element is the pivot, move it to the left. + IfFailRet(Compare(iLeft, (iLeft+iRight)/2, &nResult)); + if (nResult != 0) + { + IfFailRet(Swap(iLeft, (iLeft+iRight)/2)); + } + iLast = iLeft; + + // move everything that is smaller than the pivot to the left. + for (int i = iLeft+1; i <= iRight; i++) + { + IfFailRet(Compare(i, iLeft, &nResult)); + if (nResult < 0) + { + IfFailRet(Swap(i, ++iLast)); + } + } + + // Put the pivot to the point where it is in between smaller and larger elements. + IfFailRet(Compare(iLeft, iLast, &nResult)); + if (nResult != 0) + { + IfFailRet(Swap(iLeft, iLast)); + } + + // Sort each partition. + int iLeftLast = iLast - 1; + int iRightFirst = iLast + 1; + if (iLeftLast - iLeft < iRight - iRightFirst) + { // Left partition is smaller, sort it recursively + IfFailRet(SortRange(iLeft, iLeftLast)); + // Tail call to sort the right (bigger) partition + iLeft = iRightFirst; + //iRight = iRight; + continue; + } + else + { // Right partition is smaller, sort it recursively + IfFailRet(SortRange(iRightFirst, iRight)); + // Tail call to sort the left (bigger) partition + //iLeft = iLeft; + iRight = iLeftLast; + continue; + } + } + } // CQuickSortMiniMdRW::SortRange + +protected: + __checkReturn + inline HRESULT Swap( + int iFirst, + int iSecond) + { + HRESULT hr; + void *pFirst; + void *pSecond; + if (iFirst == iSecond) + { + return S_OK; + } + + PREFAST_ASSUME_MSG(m_iElemSize <= (int) sizeof(m_buf), "The MetaData table row has to fit into buffer for swapping."); + + IfFailRet(getRow(iFirst, &pFirst)); + IfFailRet(getRow(iSecond, &pSecond)); + memcpy(m_buf, pFirst, m_iElemSize); + memcpy(pFirst, pSecond, m_iElemSize); + memcpy(pSecond, m_buf, m_iElemSize); + if (m_pRidMap != NULL) + { + RID ridTemp; + ridTemp = *(m_pRidMap->Get(iFirst)); + *(m_pRidMap->Get(iFirst)) = *(m_pRidMap->Get(iSecond)); + *(m_pRidMap->Get(iSecond)) = ridTemp; + } + return S_OK; + } // CQuickSortMiniMdRW::Swap + +}; // class CQuickSortMiniMdRW + +class CStableSortMiniMdRW : public CQuickSortMiniMdRW +{ +public: + CStableSortMiniMdRW( + CMiniMdRW &MiniMd, // MiniMd with the data. + ULONG ixTbl, // The table. + ULONG ixCol, // The column. + bool bMapToken) // Is MapToken handling desired. + : CQuickSortMiniMdRW(MiniMd, ixTbl, ixCol, bMapToken) + {} + + //***************************************************************************** + // Call to sort the array. + //***************************************************************************** + __checkReturn + HRESULT Sort() + { + int i; // Outer loop counter. + int j; // Inner loop counter. + int bSwap; // Early out. + HRESULT hr = S_OK; + int nResult; + + _ASSERTE(m_MiniMd.IsSortable(m_ixTbl)); + m_iCount = m_MiniMd.GetCountRecs(m_ixTbl); + + // If remap notifications are desired, prepare to collect the info in a RIDMAP. + IfFailGo(PrepMapTokens()); + + for (i=m_iCount; i>1; --i) + { + bSwap = 0; + for (j=1; j<i; ++j) + { + IfFailGo(Compare(j, j+1, &nResult)); + if (nResult > 0) + { + IfFailGo(Swap(j, j+1)); + bSwap = 1; + } + } + // If made a full pass w/o swaps, done. + if (!bSwap) + break; + } + + // The table is sorted until its next change. + SetSorted(); + + // If remap notifications were desired, send them. + IfFailGo(DoMapTokens()); + + ErrExit: + return hr; + } // CStableSortMiniMdRW::Sort + +}; // class CStableSortMiniMdRW + +//------------------------------------------------------------------------- +#define SORTER(tbl,key) CQuickSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, false); +#define SORTER_WITHREMAP(tbl,key) CQuickSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, true); +#define STABLESORTER(tbl,key) CStableSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, false); +#define STABLESORTER_WITHREMAP(tbl,key) CStableSortMiniMdRW sort##tbl(*this, TBL_##tbl, tbl##Rec::COL_##key, true); +//------------------------------------------------------------------------- + + + +//********** Code. ************************************************************ + + +//***************************************************************************** +// Ctor / dtor. +//***************************************************************************** +#ifdef _DEBUG +static bool bENCDeltaOnly = false; +#endif +CMiniMdRW::CMiniMdRW() + : m_pMemberRefHash(0), + m_pMemberDefHash(0), + m_pNamedItemHash(0), + m_pHandler(0), + m_cbSaveSize(0), + m_fIsReadOnly(false), + m_bPreSaveDone(false), + m_bPostGSSMod(false), + m_pMethodMap(0), + m_pFieldMap(0), + m_pPropertyMap(0), + m_pEventMap(0), + m_pParamMap(0), + m_pFilterTable(0), + m_pHostFilter(0), + m_pTokenRemapManager(0), + m_fMinimalDelta(FALSE), + m_rENCRecs(0) +{ +#ifdef _DEBUG + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_EncDelta)) + { + bENCDeltaOnly = true; + } + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_MiniMDBreak)) + { + _ASSERTE(!"CMiniMdRW::CMiniMdRW()"); + } +#endif // _DEBUG + + ZeroMemory(&m_OptionValue, sizeof(OptionValue)); + + // initialize the embeded lookuptable struct. Further initialization, after constructor. + for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl) + { + m_pVS[ixTbl] = 0; + m_pLookUpHashs[ixTbl] = 0; + } + + // Assume that we can sort tables as needed. + memset(m_bSortable, 1, sizeof(m_bSortable)); + + // Initialize the global array of Ptr table indices. + g_PtrTableIxs[TBL_Field].m_ixtbl = TBL_FieldPtr; + g_PtrTableIxs[TBL_Field].m_ixcol = FieldPtrRec::COL_Field; + g_PtrTableIxs[TBL_Method].m_ixtbl = TBL_MethodPtr; + g_PtrTableIxs[TBL_Method].m_ixcol = MethodPtrRec::COL_Method; + g_PtrTableIxs[TBL_Param].m_ixtbl = TBL_ParamPtr; + g_PtrTableIxs[TBL_Param].m_ixcol = ParamPtrRec::COL_Param; + g_PtrTableIxs[TBL_Property].m_ixtbl = TBL_PropertyPtr; + g_PtrTableIxs[TBL_Property].m_ixcol = PropertyPtrRec::COL_Property; + g_PtrTableIxs[TBL_Event].m_ixtbl = TBL_EventPtr; + g_PtrTableIxs[TBL_Event].m_ixcol = EventPtrRec::COL_Event; + + // AUTO_GROW initialization + m_maxRid = m_maxIx = 0; + m_limIx = USHRT_MAX >> 1; + m_limRid = USHRT_MAX >> AUTO_GROW_CODED_TOKEN_PADDING; + m_eGrow = eg_ok; +#ifdef _DEBUG + { + ULONG iMax, iCdTkn; + for (iMax=0, iCdTkn=0; iCdTkn<CDTKN_COUNT; ++iCdTkn) + { + CCodedTokenDef const *pCTD = &g_CodedTokens[iCdTkn]; + if (pCTD->m_cTokens > iMax) + iMax = pCTD->m_cTokens; + } + // If assert fires, change define for AUTO_GROW_CODED_TOKEN_PADDING. + _ASSERTE(CMiniMdRW::m_cb[iMax] == AUTO_GROW_CODED_TOKEN_PADDING); + } + dbg_m_pLock = NULL; +#endif //_DEBUG + +} // CMiniMdRW::CMiniMdRW + +CMiniMdRW::~CMiniMdRW() +{ + // Un-initialize the embeded lookuptable struct + for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl) + { + if (m_pVS[ixTbl]) + { + m_pVS[ixTbl]->Uninit(); + delete m_pVS[ixTbl]; + } + if ( m_pLookUpHashs[ixTbl] != NULL ) + delete m_pLookUpHashs[ixTbl]; + + } + if (m_pFilterTable) + delete m_pFilterTable; + + if (m_rENCRecs) + delete [] m_rENCRecs; + + if (m_pHandler) + m_pHandler->Release(), m_pHandler = 0; + if (m_pHostFilter) + m_pHostFilter->Release(); + if (m_pMemberRefHash) + delete m_pMemberRefHash; + if (m_pMemberDefHash) + delete m_pMemberDefHash; + if (m_pNamedItemHash) + delete m_pNamedItemHash; + if (m_pMethodMap) + delete m_pMethodMap; + if (m_pFieldMap) + delete m_pFieldMap; + if (m_pPropertyMap) + delete m_pPropertyMap; + if (m_pEventMap) + delete m_pEventMap; + if (m_pParamMap) + delete m_pParamMap; + if (m_pTokenRemapManager) + delete m_pTokenRemapManager; +} // CMiniMdRW::~CMiniMdRW + + +//***************************************************************************** +// return all found CAs in an enumerator +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CommonEnumCustomAttributeByName( + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + bool fStopAtFirstFind, // [IN] just find the first one + HENUMInternal *phEnum) // enumerator to fill up +{ + HRESULT hr = S_OK; + HRESULT hrRet = S_FALSE; // Assume that we won't find any + ULONG ridStart, ridEnd; // Loop start and endpoints. + CLookUpHash *pHashTable = m_pLookUpHashs[TBL_CustomAttribute]; + + _ASSERTE(phEnum != NULL); + + HENUMInternal::ZeroEnum(phEnum); + + HENUMInternal::InitDynamicArrayEnum(phEnum); + + phEnum->m_tkKind = mdtCustomAttribute; + + if (pHashTable) + { + // table is not sorted and hash is not built so we have to create a dynamic array + // create the dynamic enumerator. + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + iHash = HashCustomAttribute(tkObj); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailGo(CompareCustomAttribute( tkObj, szName, RidFromToken(p->tok))); + if (hr == S_OK) + { + hrRet = S_OK; + + // If here, found a match. + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(p->tok, mdtCustomAttribute))); + if (fStopAtFirstFind) + goto ErrExit; + } + } + } + else + { + // Get the list of custom values for the parent object. + if ( IsSorted(TBL_CustomAttribute) ) + { + IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart)); + // If found none, done. + if (ridStart == 0) + goto ErrExit; + } + else + { + // linear scan of entire table. + ridStart = 1; + ridEnd = getCountCustomAttributes() + 1; + } + + // Look for one with the given name. + for (; ridStart < ridEnd; ++ridStart) + { + IfFailGo(CompareCustomAttribute( tkObj, szName, ridStart)); + if (hr == S_OK) + { + // If here, found a match. + hrRet = S_OK; + IfFailGo( HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(ridStart, mdtCustomAttribute))); + if (fStopAtFirstFind) + goto ErrExit; + } + } + } + +ErrExit: + if (FAILED(hr)) + return hr; + return hrRet; +} // CMiniMdRW::CommonEnumCustomAttributeByName + + + +//***************************************************************************** +// return just the blob value of the first found CA matching the query. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CommonGetCustomAttributeByNameEx( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + mdCustomAttribute *ptkCA, // [OUT] put custom attribute token here + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData) // [OUT] Put size of data here. +{ + HRESULT hr; + const void *pData; + ULONG cbData; + HENUMInternal hEnum; + mdCustomAttribute ca; + CustomAttributeRec *pRec; + + hr = CommonEnumCustomAttributeByName(tkObj, szName, true, &hEnum); + if (hr != S_OK) + goto ErrExit; + + if (ppData != NULL || ptkCA != NULL) + { + // now get the record out. + if (ppData == 0) + ppData = &pData; + if (pcbData == 0) + pcbData = &cbData; + + + if (HENUMInternal::EnumNext(&hEnum, &ca)) + { + IfFailGo(GetCustomAttributeRecord(RidFromToken(ca), &pRec)); + IfFailGo(getValueOfCustomAttribute(pRec, reinterpret_cast<const BYTE **>(ppData), pcbData)); + if (ptkCA) + *ptkCA = ca; + } + else + { + _ASSERTE(!"Enum returned no items after EnumInit returned S_OK"); + hr = S_FALSE; + } + } +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + return hr; +} // CMiniMdRW::CommonGetCustomAttributeByName + +//***************************************************************************** +// unmark everything in this module +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::UnmarkAll() +{ + HRESULT hr = NOERROR; + ULONG ulSize = 0; + ULONG ixTbl; + FilterTable *pFilter; + + // find the max rec count with all tables + for (ixTbl = 0; ixTbl < TBL_COUNT; ++ixTbl) + { + if (GetCountRecs(ixTbl) > ulSize) + ulSize = GetCountRecs(ixTbl); + } + IfNullGo(pFilter = GetFilterTable()); + IfFailGo(pFilter->UnmarkAll(this, ulSize)); + +ErrExit: + return hr; +} // CMiniMdRW::UnmarkAll + + +//***************************************************************************** +// mark everything in this module +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::MarkAll() +{ + HRESULT hr = NOERROR; + ULONG ulSize = 0; + ULONG ixTbl; + FilterTable *pFilter; + + // find the max rec count with all tables + for (ixTbl = 0; ixTbl < TBL_COUNT; ++ixTbl) + { + if (GetCountRecs(ixTbl) > ulSize) + ulSize = GetCountRecs(ixTbl); + } + IfNullGo(pFilter = GetFilterTable()); + IfFailGo(pFilter->MarkAll(this, ulSize)); + +ErrExit: + return hr; +} // CMiniMdRW::MarkAll + +//***************************************************************************** +// This will trigger FilterTable to be created +//***************************************************************************** +FilterTable *CMiniMdRW::GetFilterTable() +{ + if (m_pFilterTable == NULL) + { + m_pFilterTable = new (nothrow) FilterTable; + } + return m_pFilterTable; +} + + +//***************************************************************************** +// Calculate the map between TypeRef and TypeDef +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CalculateTypeRefToTypeDefMap() +{ + HRESULT hr = NOERROR; + ULONG index; + TypeRefRec *pTypeRefRec; + LPCSTR szName; + LPCSTR szNamespace; + mdToken td; + mdToken tkResScope; + + PREFIX_ASSUME(GetTypeRefToTypeDefMap() != NULL); + + for (index = 1; index <= m_Schema.m_cRecs[TBL_TypeRef]; index++) + { + IfFailRet(GetTypeRefRecord(index, &pTypeRefRec)); + + // Get the name and namespace of the TypeRef. + IfFailRet(getNameOfTypeRef(pTypeRefRec, &szName)); + IfFailRet(getNamespaceOfTypeRef(pTypeRefRec, &szNamespace)); + tkResScope = getResolutionScopeOfTypeRef(pTypeRefRec); + + // If the resolutionScope is an AssemblyRef, then the type is + // external, even if it has the same name as a type in this scope. + if (TypeFromToken(tkResScope) == mdtAssemblyRef) + continue; + + // Iff the name is found in the typedef table, then use + // that value instead. Won't be found if typeref is trully external. + hr = ImportHelper::FindTypeDefByName(this, szNamespace, szName, + (TypeFromToken(tkResScope) == mdtTypeRef) ? tkResScope : mdTokenNil, + &td); + if (hr != S_OK) + { + // don't propagate the error in the Find + hr = NOERROR; + continue; + } + *(GetTypeRefToTypeDefMap()->Get(index)) = td; + } + + return hr; +} // CMiniMdRW::CalculateTypeRefToTypeDefMap + + +//***************************************************************************** +// Set a remap handler. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SetHandler( + IUnknown *pIUnk) +{ + if (m_pHandler != NULL) + { + m_pHandler->Release(); + m_pHandler = NULL; + } + + if (pIUnk != NULL) + { + // ignore the error for QI the IHostFilter + pIUnk->QueryInterface(IID_IHostFilter, reinterpret_cast<void**>(&m_pHostFilter)); + + return pIUnk->QueryInterface(IID_IMapToken, reinterpret_cast<void**>(&m_pHandler)); + } + + return S_OK; +} // CMiniMdRW::SetHandler + +//***************************************************************************** +// Set a Options +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SetOption( + OptionValue *pOptionValue) +{ + HRESULT hr = NOERROR; + ULONG ixTbl = 0; + int i; + + m_OptionValue = *pOptionValue; + + // Turn off delta metadata bit -- can't be used due to EE assumptions about delta PEs. + // Inspect ApplyEditAndContinue for details. + // To enable this, use the EnableDeltaMetadataGeneration/DisableDeltaMetadataGeneration accessors. + _ASSERTE((m_OptionValue.m_UpdateMode & MDUpdateDelta) != MDUpdateDelta); + +#ifdef _DEBUG + if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC && + bENCDeltaOnly) + m_OptionValue.m_UpdateMode |= MDUpdateDelta; +#endif + + // if a scope is previously updated as incremental, then it should not be open again + // with full update for read/write. + // + if ((m_Schema.m_heaps & CMiniMdSchema::HAS_DELETE) && + (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull && + !m_fIsReadOnly) + { + IfFailGo( CLDB_E_BADUPDATEMODE ); + } + + if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateIncremental) + m_Schema.m_heaps |= CMiniMdSchema::HAS_DELETE; + + // Set the value of sortable based on the options. + switch (m_OptionValue.m_UpdateMode & MDUpdateMask) + { + case MDUpdateFull: + // Always sortable. + for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl) + m_bSortable[ixTbl] = 1; + break; + case MDUpdateENC: + // Never sortable. + for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl) + m_bSortable[ixTbl] = 0; + + // Truncate some tables. + for (i=0; (ixTbl = m_TruncatedEncTables[i]) != (ULONG) -1; ++i) + { + m_Tables[ixTbl].Delete(); + IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount( + m_TableDefs[ixTbl].m_cbRec, + 0 + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl)); + m_Schema.m_cRecs[ixTbl] = 0; + } + + // Out-of-order is expected in an ENC scenario, never an error. + m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderNone; + + break; + case MDUpdateIncremental: + // Sortable if no external token. + for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl) + m_bSortable[ixTbl] = (GetTokenForTable(ixTbl) == (ULONG) -1); + break; + case MDUpdateExtension: + // Never sortable. + for (ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl) + m_bSortable[ixTbl] = 0; + break; + default: + _ASSERTE(!"Internal error -- unknown save mode"); + return E_INVALIDARG; + } + + // If this is an ENC session, track the generations. + if (!m_fIsReadOnly && (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC) + { +#ifdef FEATURE_METADATA_EMIT + ModuleRec *pMod; + GUID encid; + + // Get the module record. + IfFailGo(GetModuleRecord(1, &pMod)); + +/* Do we really want to do this? This would reset the metadata each time we changed an option + // Copy EncId as BaseId. + uVal = GetCol(TBL_Module, ModuleRec::COL_EncId, pMod); + PutCol(TBL_Module, ModuleRec::COL_EncBaseId, pMod, uVal); +*/ + // Allocate a new GUID for EncId. + IfFailGo(CoCreateGuid(&encid)); + IfFailGo(PutGuid(TBL_Module, ModuleRec::COL_EncId, pMod, encid)); +#else //!FEATURE_METADATA_EMIT + IfFailGo(E_INVALIDARG); +#endif //!FEATURE_METADATA_EMIT + } + +ErrExit: + return hr; +} // CMiniMdRW::SetOption + +//***************************************************************************** +// Get Options +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetOption( + OptionValue *pOptionValue) +{ + *pOptionValue = m_OptionValue; + return S_OK; +} // CMiniMdRW::GetOption + +//***************************************************************************** +// Smart MapToken. Only calls client if token really changed. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::MapToken( // Return value from user callback. + RID from, // Old rid. + RID to, // New rid. + mdToken tkn) // Token type. +{ + HRESULT hr = S_OK; + TOKENREC *pTokenRec; + MDTOKENMAP *pMovementMap; + // If not change, done. + if (from == to) + return S_OK; + + pMovementMap = GetTokenMovementMap(); + _ASSERTE(GetTokenMovementMap() != NULL); + if (pMovementMap != NULL) + IfFailRet(pMovementMap->AppendRecord( TokenFromRid(from, tkn), false, TokenFromRid(to, tkn), &pTokenRec )); + + // Notify client. + if (m_pHandler != NULL) + { + LOG((LOGMD, "CMiniMdRW::MapToken (remap): from 0x%08x to 0x%08x\n", TokenFromRid(from,tkn), TokenFromRid(to,tkn))); + return m_pHandler->Map(TokenFromRid(from,tkn), TokenFromRid(to,tkn)); + } + else + { + return hr; + } +} // CMiniMdCreate::MapToken + +//***************************************************************************** +// Set max, lim, based on data. +//***************************************************************************** +void +CMiniMdRW::ComputeGrowLimits( + int bSmall) // large or small tables? +{ + if (bSmall) + { + // Tables will need to grow if any value exceeds what a two-byte column can hold. + m_maxRid = m_maxIx = 0; + m_limIx = USHRT_MAX >> 1; + m_limRid = USHRT_MAX >> AUTO_GROW_CODED_TOKEN_PADDING; + m_eGrow = eg_ok; + } + else + { + // Tables are already large + m_maxRid = m_maxIx = UINT32_MAX; + m_limIx = USHRT_MAX << 1; + m_limRid = USHRT_MAX << 1; + m_eGrow = eg_grown; + } +} // CMiniMdRW::ComputeGrowLimits + +//***************************************************************************** +// Initialization of a new writable MiniMd's pools. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::InitPoolOnMem( + int iPool, // The pool to initialize. + void *pbData, // The data from which to init. + ULONG cbData, // Size of data. + int fIsReadOnly) // Is the memory read-only? +{ + HRESULT hr; + + switch (iPool) + { + case MDPoolStrings: + if (pbData == NULL) + { // Creates new empty string heap with default empty string entry + IfFailRet(m_StringHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + else + { + IfFailRet(m_StringHeap.Initialize( + MetaData::DataBlob((BYTE *)pbData, cbData), + !fIsReadOnly)); + } + break; + case MDPoolGuids: + if (pbData == NULL) + { // Creates new empty guid heap + IfFailRet(m_GuidHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + else + { + IfFailRet(m_GuidHeap.Initialize( + MetaData::DataBlob((BYTE *)pbData, cbData), + !fIsReadOnly)); + } + break; + case MDPoolBlobs: + if (pbData == NULL) + { + if (IsMinimalDelta()) + { // It's EnC minimal delta, don't include default empty blob + IfFailRet(m_BlobHeap.InitializeEmpty_WithoutDefaultEmptyBlob( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + else + { // Creates new empty blob heap with default empty blob entry + IfFailRet(m_BlobHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + } + else + { + IfFailRet(m_BlobHeap.Initialize( + MetaData::DataBlob((BYTE *)pbData, cbData), + !fIsReadOnly)); + } + break; + case MDPoolUSBlobs: + if (pbData == NULL) + { + if (IsMinimalDelta()) + { // It's EnC minimal delta, don't include default empty user string + IfFailRet(m_UserStringHeap.InitializeEmpty_WithoutDefaultEmptyBlob( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + else + { // Creates new empty user string heap (with default empty !!!blob!!! entry) + // Note: backaward compatiblity: doesn't add default empty user string, but default empty + // blob entry + IfFailRet(m_UserStringHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + } + else + { + IfFailRet(m_UserStringHeap.Initialize( + MetaData::DataBlob((BYTE *)pbData, cbData), + !fIsReadOnly)); + } + break; + default: + hr = E_INVALIDARG; + } + return hr; +} // CMiniMdRW::InitPoolOnMem + +//***************************************************************************** +// Initialization of a new writable MiniMd +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::InitOnMem( + const void *pvBuf, // The data from which to init. + ULONG ulBufLen, // The data size + int fIsReadOnly) // Is the memory read-only? +{ + HRESULT hr = S_OK; + UINT32 cbSchemaSize; // Size of the schema structure. + S_UINT32 cbTotalSize; // Size of all data used. + BYTE *pBuf = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(pvBuf)); + int i; + + // post contruction initialize the embeded lookuptable struct + for (ULONG ixTbl = 0; ixTbl < m_TblCount; ++ixTbl) + { + if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols) + { + if (m_pVS[ixTbl] == NULL) + { + m_pVS[ixTbl] = new (nothrow) VirtualSort; + IfNullGo(m_pVS[ixTbl]); + + m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this); + } + } + } + + // Uncompress the schema from the buffer into our structures. + IfFailGo(SchemaPopulate(pvBuf, ulBufLen, (ULONG *)&cbSchemaSize)); + + if (m_fMinimalDelta) + IfFailGo(InitWithLargeTables()); + + // Initialize the pointers to the rest of the data. + pBuf += cbSchemaSize; + cbTotalSize = S_UINT32(cbSchemaSize); + for (i=0; i<(int)m_TblCount; ++i) + { + if (m_Schema.m_cRecs[i] > 0) + { + // Size of one table is rowsize * rowcount. + S_UINT32 cbTableSize = + S_UINT32(m_TableDefs[i].m_cbRec) * + S_UINT32(m_Schema.m_cRecs[i]); + if (cbTableSize.IsOverflow()) + { + Debug_ReportError("Table is too big, its size overflows."); + IfFailGo(METADATA_E_INVALID_FORMAT); + } + cbTotalSize += cbTableSize; + if (cbTotalSize.IsOverflow()) + { + Debug_ReportError("Total tables size is too big, their total size overflows."); + IfFailGo(METADATA_E_INVALID_FORMAT); + } + IfFailGo(m_Tables[i].Initialize( + m_TableDefs[i].m_cbRec, + MetaData::DataBlob(pBuf, cbTableSize.Value()), + !fIsReadOnly)); // fCopyData + INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i)); + pBuf += cbTableSize.Value(); + } + else + { + IfFailGo(m_Tables[i].InitializeEmpty_WithRecordCount( + m_TableDefs[i].m_cbRec, + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i)); + } + } + + // If the metadata is being opened for read/write, all the updateable columns + // need to be the same width. + if (!fIsReadOnly) + { + // variable to indicate if tables are large, small or mixed. + int fMixed = false; + int iSize = 0; + CMiniColDef *pCols; // The col defs to init. + int iCol; + + // Look at all the tables, or until mixed sizes are discovered. + for (i=0; i<(int)m_TblCount && !fMixed; i++) + { // Look at all the columns of the table. + pCols = m_TableDefs[i].m_pColDefs; + for (iCol = 0; iCol < m_TableDefs[i].m_cCols && !fMixed; iCol++) + { // If not a fixed size column... + if (!IsFixedType(m_TableDefs[i].m_pColDefs[iCol].m_Type)) + { // If this is the first non-fixed size column... + if (iSize == 0) + { // remember its size. + iSize = m_TableDefs[i].m_pColDefs[iCol].m_cbColumn; + } + else if (iSize != m_TableDefs[i].m_pColDefs[iCol].m_cbColumn) + { // Not first non-fixed size, so if a different size + // the table has mixed column sizes. + fMixed = true; + } + } + } + } + if (fMixed) + { + // grow everything to large + IfFailGo(ExpandTables()); + ComputeGrowLimits(FALSE /* ! small*/); + } + else if (iSize == 2) + { + // small schema + ComputeGrowLimits(TRUE /* small */); + } + else + { + // large schema + ComputeGrowLimits(FALSE /* ! small */); + } + } + else + { + // Set the limits so we will know when to grow the database. + ComputeGrowLimits(TRUE /* small */); + } + + // Track records that this MD started with. + m_StartupSchema = m_Schema; + + m_fIsReadOnly = fIsReadOnly ? 1 : 0; + +ErrExit: + return hr; +} // CMiniMdRW::InitOnMem + +//***************************************************************************** +// Validate cross-stream consistency. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PostInit( + int iLevel) +{ + return S_OK; +} // CMiniMdRW::PostInit + +//***************************************************************************** +// Init a CMiniMdRW from the data in a CMiniMd [RO]. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::InitOnRO( + CMiniMd *pMd, // The MiniMd to update from. + int fIsReadOnly) // Will updates be allowed? +{ + HRESULT hr = S_OK; + ULONG i; // Loop control. + + // Init the schema. + IfFailGo(SchemaPopulate(*pMd)); + + // Allocate VS structs for tables with key columns. + for (ULONG ixTbl = 0; ixTbl < m_TblCount; ++ixTbl) + { + if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols) + { + m_pVS[ixTbl] = new (nothrow) VirtualSort; + IfNullGo(m_pVS[ixTbl]); + + m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this); + } + } + + // Copy over the column definitions. + for (i = 0; i < m_TblCount; ++i) + { + _ASSERTE(m_TableDefs[i].m_cCols == pMd->m_TableDefs[i].m_cCols); + m_TableDefs[i].m_cbRec = pMd->m_TableDefs[i].m_cbRec; + IfFailGo(SetNewColumnDefinition(&(m_TableDefs[i]), pMd->m_TableDefs[i].m_pColDefs, i)); + } + + // Initialize string heap + if (pMd->m_StringHeap.GetUnalignedSize() > 0) + { + IfFailGo(m_StringHeap.InitializeFromStringHeap( + &(pMd->m_StringHeap), + !fIsReadOnly)); + } + else + { + IfFailGo(m_StringHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + + // Initialize user string heap + if (pMd->m_UserStringHeap.GetUnalignedSize() > 0) + { + IfFailGo(m_UserStringHeap.InitializeFromBlobHeap( + &(pMd->m_UserStringHeap), + !fIsReadOnly)); + } + else + { + IfFailGo(m_UserStringHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + + // Initialize guid heap + if (pMd->m_GuidHeap.GetSize() > 0) + { + IfFailGo(m_GuidHeap.InitializeFromGuidHeap( + &(pMd->m_GuidHeap), + !fIsReadOnly)); + } + else + { + IfFailGo(m_GuidHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + + // Initialize blob heap + if (pMd->m_BlobHeap.GetUnalignedSize() > 0) + { + IfFailGo(m_BlobHeap.InitializeFromBlobHeap( + &(pMd->m_BlobHeap), + !fIsReadOnly)); + } + else + { + IfFailGo(m_BlobHeap.InitializeEmpty( + 0 + COMMA_INDEBUG_MD(!fIsReadOnly))); + } + + // Init the record pools + for (i = 0; i < m_TblCount; ++i) + { + if (m_Schema.m_cRecs[i] > 0) + { + IfFailGo(m_Tables[i].InitializeFromTable( + &(pMd->m_Tables[i]), + m_TableDefs[i].m_cbRec, + m_Schema.m_cRecs[i], + !fIsReadOnly)); // fCopyData + INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i)); + + // We set this bit to indicate the compressed, read-only tables are always sorted + // <TODO>This is not true for all tables, so we should set it correctly and flush out resulting bugs</TODO> + SetSorted(i, true); + } + else + { + IfFailGo(m_Tables[i].InitializeEmpty_WithRecordCount( + m_TableDefs[i].m_cbRec, + 2 + COMMA_INDEBUG_MD(!fIsReadOnly))); + INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i)); + // An empty table can be considered unsorted. + SetSorted(i, false); + } + } + + // Set the limits so we will know when to grow the database. + ComputeGrowLimits(TRUE /* small */); + + // Track records that this MD started with. + m_StartupSchema = m_Schema; + + m_fIsReadOnly = fIsReadOnly ? 1 : 0; + +ErrExit: + return hr; +} // CMiniMdRW::InitOnRO + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + +// This checks that column sizes are reasonable for their types +// The sizes could still be too small to hold all values in the range, or larger +// than they needed, but there must exist some scenario where this size is the +// one we would use. +// As long as this validation passes + we verify that the records actually +// have space for columns of this size then the worst thing that malicious +// data could do is be slightly inneficient, or be unable to address all their data +HRESULT _ValidateColumnSize(BYTE trustedColumnType, BYTE untrustedColumnSize) +{ + // Is the field a RID into a table? + if (trustedColumnType <= iCodedTokenMax) + { + if (untrustedColumnSize != sizeof(USHORT) && untrustedColumnSize != sizeof(ULONG)) + return CLDB_E_FILE_CORRUPT; + } + else + { // Fixed type. + switch (trustedColumnType) + { + case iBYTE: + if (untrustedColumnSize != 1) + return CLDB_E_FILE_CORRUPT; + break; + case iSHORT: + case iUSHORT: + if (untrustedColumnSize != 2) + return CLDB_E_FILE_CORRUPT; + break; + case iLONG: + case iULONG: + if (untrustedColumnSize != 4) + return CLDB_E_FILE_CORRUPT; + break; + case iSTRING: + case iGUID: + case iBLOB: + if (untrustedColumnSize != 2 && untrustedColumnSize != 4) + return CLDB_E_FILE_CORRUPT; + break; + default: + _ASSERTE(!"Unexpected schema type"); + return CLDB_E_FILE_CORRUPT; + } + } + return S_OK; +} + +__checkReturn +HRESULT CMiniMdRW::InitOnCustomDataSource(IMDCustomDataSource* pDataSource) +{ + HRESULT hr = S_OK; + ULONG i; // Loop control. + ULONG key; + BOOL fIsReadOnly = TRUE; + MetaData::DataBlob stringPoolData; + MetaData::DataBlob userStringPoolData; + MetaData::DataBlob guidHeapData; + MetaData::DataBlob blobHeapData; + MetaData::DataBlob tableRecordData; + CMiniTableDef tableDef; + BOOL sortable = FALSE; + + + // the data source owns all the memory backing the storage pools, so we need to ensure it stays alive + // after this method returns. When the CMiniMdRW is destroyed the reference will be released. + pDataSource->AddRef(); + m_pCustomDataSource = pDataSource; + + // Copy over the schema. + IfFailGo(pDataSource->GetSchema(&m_Schema)); + + // Is this the "native" version of the metadata for this runtime? + if ((m_Schema.m_major != METAMODEL_MAJOR_VER) || (m_Schema.m_minor != METAMODEL_MINOR_VER)) + { + // We don't support this version of the metadata + Debug_ReportError("Unsupported version of MetaData."); + return PostError(CLDB_E_FILE_OLDVER, m_Schema.m_major, m_Schema.m_minor); + } + + // How big are the various pool inidices? + m_iStringsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_STRING_4) ? 0xffffffff : 0xffff; + m_iGuidsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_GUID_4) ? 0xffffffff : 0xffff; + m_iBlobsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_BLOB_4) ? 0xffffffff : 0xffff; + + // Copy over TableDefs, column definitions and allocate VS structs for tables with key columns. + for (ULONG ixTbl = 0; ixTbl < m_TblCount; ++ixTbl) + { + IfFailGo(pDataSource->GetTableDef(ixTbl, &tableDef)); + const CMiniTableDef* pTemplate = GetTableDefTemplate(ixTbl); + + // validate that the table def looks safe + // we only allow some very limited differences between the standard template and the data source + key = (pTemplate->m_iKey < pTemplate->m_cCols) ? pTemplate->m_iKey : 0xFF; + if (key != tableDef.m_iKey) { IfFailGo(CLDB_E_FILE_CORRUPT); } + if (pTemplate->m_cCols != tableDef.m_cCols) { IfFailGo(CLDB_E_FILE_CORRUPT); } + ULONG cbRec = 0; + for (ULONG i = 0; i < pTemplate->m_cCols; i++) + { + if (tableDef.m_pColDefs == NULL) { IfFailGo(CLDB_E_FILE_CORRUPT); } + if (pTemplate->m_pColDefs[i].m_Type != tableDef.m_pColDefs[i].m_Type) { IfFailGo(CLDB_E_FILE_CORRUPT); } + IfFailGo(_ValidateColumnSize(pTemplate->m_pColDefs[i].m_Type, tableDef.m_pColDefs[i].m_cbColumn)); + // sometimes, but not always, it seems like columns get alignment padding + // we'll allow it if we see it + if (cbRec > tableDef.m_pColDefs[i].m_oColumn) { IfFailGo(CLDB_E_FILE_CORRUPT); } + if (tableDef.m_pColDefs[i].m_oColumn > AlignUp(cbRec, tableDef.m_pColDefs[i].m_cbColumn)) { IfFailGo(CLDB_E_FILE_CORRUPT); } + cbRec = tableDef.m_pColDefs[i].m_oColumn + tableDef.m_pColDefs[i].m_cbColumn; + } + if (tableDef.m_cbRec != cbRec) { IfFailGo(CLDB_E_FILE_CORRUPT); } + + // tabledef passed validation, copy it in + m_TableDefs[ixTbl].m_iKey = tableDef.m_iKey; + m_TableDefs[ixTbl].m_cCols = tableDef.m_cCols; + m_TableDefs[ixTbl].m_cbRec = tableDef.m_cbRec; + IfFailGo(SetNewColumnDefinition(&(m_TableDefs[ixTbl]), tableDef.m_pColDefs, ixTbl)); + if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols) + { + m_pVS[ixTbl] = new (nothrow)VirtualSort; + IfNullGo(m_pVS[ixTbl]); + + m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this); + } + } + + // Initialize string heap + IfFailGo(pDataSource->GetStringHeap(&stringPoolData)); + m_StringHeap.Initialize(stringPoolData, !fIsReadOnly); + + // Initialize user string heap + IfFailGo(pDataSource->GetUserStringHeap(&userStringPoolData)); + m_UserStringHeap.Initialize(userStringPoolData, !fIsReadOnly); + + // Initialize guid heap + IfFailGo(pDataSource->GetGuidHeap(&guidHeapData)); + m_GuidHeap.Initialize(guidHeapData, !fIsReadOnly); + + // Initialize blob heap + IfFailGo(pDataSource->GetBlobHeap(&blobHeapData)); + m_BlobHeap.Initialize(blobHeapData, !fIsReadOnly); + + // Init the record pools + for (i = 0; i < m_TblCount; ++i) + { + IfFailGo(pDataSource->GetTableRecords(i, &tableRecordData)); + // sanity check record counts and table sizes, this also ensures that cbRec*m_cRecs[x] doesn't overflow + if (m_Schema.m_cRecs[i] > 1000000) { IfFailGo(CLDB_E_FILE_CORRUPT); } + if (tableRecordData.GetSize() < m_TableDefs[i].m_cbRec * m_Schema.m_cRecs[i]) { IfFailGo(CLDB_E_FILE_CORRUPT); } + m_Tables[i].Initialize(m_TableDefs[i].m_cbRec, tableRecordData, !fIsReadOnly); + + IfFailGo(pDataSource->GetTableSortable(i, &sortable)); + m_bSortable[i] = sortable; + } + + // Set the limits so we will know when to grow the database. + ComputeGrowLimits(TRUE /* small */); + + // Track records that this MD started with. + m_StartupSchema = m_Schema; + + m_fIsReadOnly = fIsReadOnly; + +ErrExit: + return hr; +} +#endif + +//***************************************************************************** +// Convert a read-only to read-write. Copies data. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ConvertToRW() +{ + HRESULT hr = S_OK; + int i; // Loop control. + + // Check for already done. + if (!m_fIsReadOnly) + return hr; + + // If this is a minimal delta, then we won't allow it to be RW + if (IsMinimalDelta()) + return CLDB_E_INCOMPATIBLE; + + IfFailGo(m_StringHeap.MakeWritable()); + IfFailGo(m_GuidHeap.MakeWritable()); + IfFailGo(m_UserStringHeap.MakeWritable()); + IfFailGo(m_BlobHeap.MakeWritable()); + + // Init the record pools + for (i = 0; i < (int)m_TblCount; ++i) + { + IfFailGo(m_Tables[i].MakeWritable()); + } + + // Grow the tables. + IfFailGo(ExpandTables()); + + // Track records that this MD started with. + m_StartupSchema = m_Schema; + + // No longer read-only. + m_fIsReadOnly = false; + +ErrExit: + return hr; +} // CMiniMdRW::ConvertToRW + +//***************************************************************************** +// Initialization of a new writable MiniMd +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::InitNew() +{ + HRESULT hr = S_OK; + int i; // Loop control. + + // Initialize the Schema. + IfFailGo(m_Schema.InitNew(m_OptionValue.m_MetadataVersion)); + + // Allocate VS structs for tables with key columns. + for (ULONG ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + if (m_TableDefs[ixTbl].m_iKey < m_TableDefs[ixTbl].m_cCols) + { + m_pVS[ixTbl] = new (nothrow) VirtualSort; + IfNullGo(m_pVS[ixTbl]); + + m_pVS[ixTbl]->Init(ixTbl, m_TableDefs[ixTbl].m_iKey, this); + } + } + + enum MetaDataSizeIndex sizeIndex; + sizeIndex = GetMetaDataSizeIndex(&m_OptionValue); + if ((sizeIndex == MDSizeIndex_Standard) || (sizeIndex == MDSizeIndex_Minimal)) + { + // OutputDebugStringA("Default small tables enabled\n"); + // How big are the various pool inidices? + m_Schema.m_heaps = 0; + // How many rows in various tables? + for (i = 0; i < (int)m_TblCount; ++i) + { + m_Schema.m_cRecs[i] = 0; + } + + // Compute how many bits required to hold. + m_Schema.m_rid = 1; + m_maxRid = m_maxIx = 0; + m_limIx = USHRT_MAX >> 1; + m_limRid = USHRT_MAX >> AUTO_GROW_CODED_TOKEN_PADDING; + m_eGrow = eg_ok; + } + + // Now call base class function to calculate the offsets, sizes. + IfFailGo(SchemaPopulate2(NULL)); + + // Initialize the record heaps. + for (i = 0; i < (int)m_TblCount; ++i) + { // Don't really have any records yet. + m_Schema.m_cRecs[i] = 0; + IfFailGo(m_Tables[i].InitializeEmpty_WithRecordCount( + m_TableDefs[i].m_cbRec, + g_TblSizeInfo[sizeIndex][i] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + INDEBUG_MD(m_Tables[i].Debug_SetTableInfo(NULL, i)); + + // Create tables as un-sorted. We hope to add all records, then sort just once. + SetSorted(i, false); + } + + // Initialize heaps + IfFailGo(m_StringHeap.InitializeEmpty_WithItemsCount( + g_PoolSizeInfo[sizeIndex][IX_STRING_POOL][0], + g_PoolSizeInfo[sizeIndex][IX_STRING_POOL][1] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + IfFailGo(m_BlobHeap.InitializeEmpty_WithItemsCount( + g_PoolSizeInfo[sizeIndex][IX_BLOB_POOL][0], + g_PoolSizeInfo[sizeIndex][IX_BLOB_POOL][1] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + IfFailGo(m_UserStringHeap.InitializeEmpty_WithItemsCount( + g_PoolSizeInfo[sizeIndex][IX_US_BLOB_POOL][0], + g_PoolSizeInfo[sizeIndex][IX_US_BLOB_POOL][1] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + IfFailGo(m_GuidHeap.InitializeEmpty_WithItemsCount( + g_PoolSizeInfo[sizeIndex][IX_GUID_POOL][0], + g_PoolSizeInfo[sizeIndex][IX_GUID_POOL][1] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + + // Track records that this MD started with. + m_StartupSchema = m_Schema; + + // New db is never read-only. + m_fIsReadOnly = false; + +ErrExit: + return hr; +} // CMiniMdRW::InitNew + +#ifdef FEATURE_PREJIT +//***************************************************************************** +// Helper function to determine the size of hot tables +//***************************************************************************** +static int ShiftCount(ULONG itemCount, ULONG hotItemCount) +{ + // figure out how many bits are needed to represent the highest rid + ULONG highestRid = itemCount; + int bitCount = 0; + while ((1UL<<bitCount) <= highestRid) + bitCount++; + int shiftCount = bitCount > 8 ? bitCount - 8 : 0; + + // tune the shift count so that we don't need to search more than 4 hot entries on average. + while ((hotItemCount >> shiftCount) > 4) + shiftCount++; + if (shiftCount > 16) + shiftCount = 16; + return shiftCount; +} // ShiftCount + +//***************************************************************************** +// Helper function to qsort hot tokens +//***************************************************************************** + +typedef struct _TokenIndexPair +{ + mdToken token; + WORD index; +} TokenIndexPair; + +static WORD shiftCount; +static int __cdecl TokenCmp(const void *a, const void *b) +{ + mdToken ta = ((const TokenIndexPair *)a)->token; + mdToken tb = ((const TokenIndexPair *)b)->token; + if (shiftCount > 0) + { + // shiftCount is the number of low order bits that are used to index + // into the first level table. The below swaps high and low order bits so + // the values with common low order bits end up together after the sort. + ta = (ta >> shiftCount) | ((ta & ((1<<shiftCount)-1)) << (32-shiftCount)); + tb = (tb >> shiftCount) | ((tb & ((1<<shiftCount)-1)) << (32-shiftCount)); + } + if (ta < tb) + return -1; + else if (ta > tb) + return 1; + else + return 0; +} + +//***************************************************************************** +// A wrapper for metadata's use of CorProfileData::GetHotTokens that recognizes tokens +// flagged with ProfilingFlags_MetaDataSearch and reinterprets them into a corresponding +// set of ProfilingFlags_MetaData tokens. +// +// If you are reading this because you are changing the implementation of one of the searches +// in CMiniMdBase, it should be a mechanical process to copy the new search code below and +// change the row accesses into setting values in the rowFlags array. +// +// Doing so allows us to fix the problem that incremental IBC is fundamentally not suited to +// metadata searches. +// +// For instance, consider the following scenario: +// - You gather IBC data on a scenario that does a metadata binary search +// - The data comes from build X where the table is of size 100 and the target is in row 18 +// - This means the intermediate values touched are rows 50, 25, and 12 +// - You then apply this IBC data to build Y which has changed to include 20 new entries to start the table +// - Naively, incremental IBC would have remapped these tokens and predicted accesses at rows 70, 35, 32, and 38 +// - But this is wrong! And very bad for working set. The search will actually touch 60, 30, and 45 on the way to 38 +// +// The solution is to only store rows in IBC data that were touched intentionally, either as direct +// accesses (with ProfilingFlags_MetaData) or as the result of searches (ProfilingFlags_MetaDataSearch). +// We then expand these "search tokens" here into the set of accesses that would occur on the current +// table as we do our various types of metadata search for them. +// +// Currently, we infer touches for the following types of access: +// - Direct access (getRow) +// - Binary search (CMiniMdBase::vSearchTable or CMiniMdBase::vSearchTableNotGreater) +// - Bounds of a multi-element span (CMiniMdBase::SearchTableForMultipleRows) +// +// In theory, we could have different flags for each type of search (e.g. binary, multiple-row, etc) and +// avoid any over-reporting of intermediate tokens, but in practice the IBC flag bits are scarce and +// measurements show a minimal (<1%) amount of over-reporting. +// +//***************************************************************************** + +enum HotTokenFlags +{ + HotTokenFlags_Cold = 0x0, + HotTokenFlags_ProfiledAccess = 0x1, + HotTokenFlags_IntermediateInBinarySearch = 0x2, + HotTokenFlags_BoundingMultipleRowSearch = 0x4 +}; + +__checkReturn +HRESULT +CMiniMdRW::GetHotMetadataTokensSearchAware( + CorProfileData *pProfileData, + ULONG ixTbl, + ULONG *pResultCount, + mdToken *tokenBuffer, + ULONG maxCount) +{ + HRESULT hr = S_OK; + ULONG resultCount = 0; + + ULONG metadataAccessFlag = 1<<ProfilingFlags_MetaData; + ULONG metadataSearchFlag = 1<<ProfilingFlags_MetaDataSearch; + + // Query the profile data to determine the number of hot search tokens + ULONG numSearchTokens = pProfileData->GetHotTokens(ixTbl, metadataSearchFlag, metadataSearchFlag, NULL, 0); + ULONG cRecs = GetCountRecs(ixTbl); + + if (numSearchTokens == 0 || cRecs == 0) + { + // If there are none, we can simply return the hot access tokens without doing any interesting work + resultCount = pProfileData->GetHotTokens(ixTbl, metadataAccessFlag, metadataAccessFlag, tokenBuffer, maxCount); + } + else + { + // But if there are hot search tokens, we need to infer what intermediate rows will be touched by our various types of metadata searching. + // To do so, retrieve all hot tokens and allocate temporary storage to use to mark which rows should be considered hot and for what reason + // (i.e. an array of HotTokenFlags, one per entry in the table, indexed by RID). + ULONG numAccessTokens = pProfileData->GetHotTokens(ixTbl, metadataAccessFlag, metadataAccessFlag, NULL, 0); + + NewArrayHolder<mdToken> searchTokens = new (nothrow) mdToken[numSearchTokens]; + IfNullGo(searchTokens); + NewArrayHolder<mdToken> accessTokens = new (nothrow) mdToken[numAccessTokens]; + IfNullGo(accessTokens); + NewArrayHolder<BYTE> rowFlags = new (nothrow) BYTE[cRecs + 1]; + IfNullGo(rowFlags); + + pProfileData->GetHotTokens(ixTbl, metadataSearchFlag, metadataSearchFlag, searchTokens, numSearchTokens); + pProfileData->GetHotTokens(ixTbl, metadataAccessFlag, metadataAccessFlag, accessTokens, numAccessTokens); + + // Initially, consider all rows cold + memset(rowFlags, HotTokenFlags_Cold, cRecs + 1); + + // Category 1: Rows may have been touched directly (getRow) + // Simply mark the corresponding entry to each access token + for (ULONG i = 0; i < numAccessTokens; ++i) + { + RID rid = RidFromToken(accessTokens[i]); + + if (rid <= cRecs) + { + rowFlags[rid] |= HotTokenFlags_ProfiledAccess; + } + } + + // Category 2: Rows may have been intermediate touches in a binary search (CMiniMdBase::vSearchTable or CMiniMdBase::vSearchTableNotGreater) + // A search token may indicate where a binary search stopped, so for each of them compute and mark the intermediate set of rows that would have been touched + for (ULONG i = 0; i < numSearchTokens; ++i) + { + RID rid = RidFromToken(searchTokens[i]); + + ULONG lo = 1; + ULONG hi = cRecs; + + while (lo <= hi) + { + ULONG mid = (lo + hi) / 2; + + if (mid <= cRecs) + { + rowFlags[mid] |= HotTokenFlags_IntermediateInBinarySearch; + } + + if (mid == rid) + { + break; + } + + if (mid < rid) + lo = mid + 1; + else + hi = mid - 1; + } + } + + // Category 3: Rows may have been touched to find the bounds of a multiple element span (CMiniMdBase::SearchTableForMultipleRows) + // A search token will indicate where the search stopped, so mark the first row before and after each that was not itself touched + for (ULONG i = 0; i < numSearchTokens; ++i) + { + RID rid = RidFromToken(searchTokens[i]); + + for (RID r = rid - 1; r >= 1 && r <= cRecs; --r) + { + if ((rowFlags[r] & HotTokenFlags_ProfiledAccess) == 0) + { + rowFlags[r] |= HotTokenFlags_BoundingMultipleRowSearch; + break; + } + } + + for (RID r = rid + 1; r <= cRecs; ++r) + { + if ((rowFlags[r] & HotTokenFlags_ProfiledAccess) == 0) + { + rowFlags[r] |= HotTokenFlags_BoundingMultipleRowSearch; + break; + } + } + } + + // Now walk back over our temporary storage, counting and possibly returning the computed hot tokens + resultCount = 0; + for (ULONG i = 1; i <= cRecs; ++i) + { + if (rowFlags[i] != HotTokenFlags_Cold) + { + if (tokenBuffer != NULL && resultCount < maxCount) + tokenBuffer[resultCount] = TokenFromRid(i, ixTbl << 24); + resultCount++; + } + } + } + + if (pResultCount) + *pResultCount = resultCount; + + ErrExit: + return hr; +} // CMiniMdRW::GetHotMetadataTokensSearchAware + + +#endif //FEATURE_PREJIT + +//***************************************************************************** +// Determine how big the tables would be when saved. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetFullSaveSize( + CorSaveSize fSave, // [IN] cssAccurate or cssQuick. + UINT32 *pcbSaveSize, // [OUT] Put the size here. + DWORD *pbSaveCompressed, // [OUT] Will the saved data be fully compressed? + MetaDataReorderingOptions reorderingOptions, // [IN] Metadata reordering options + CorProfileData *pProfileData) // [IN] Optional IBC profile data for working set optimization +{ + HRESULT hr = S_OK; + CMiniTableDef sTempTable; // Definition for a temporary table. + CQuickArray<CMiniColDef> rTempCols; // Definition for a temp table's columns. + BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema. + ULONG cbAlign; // Bytes needed for alignment. + UINT32 cbTable; // Bytes in a table. + UINT32 cbTotal; // Bytes written. + int i; // Loop control. + + _ASSERTE(m_bPreSaveDone); +#ifndef FEATURE_PREJIT + _ASSERTE(pProfileData == NULL); +#endif //!FEATURE_PREJIT + + // Determine if the stream is "fully compressed", ie no pointer tables. + *pbSaveCompressed = true; + for (i=0; i<(int)m_TblCount; ++i) + { + if (HasIndirectTable(i)) + { + *pbSaveCompressed = false; + break; + } + } + + // Build the header. + CMiniMdSchema Schema = m_Schema; + IfFailGo(m_StringHeap.GetAlignedSize(&cbTable)); + if (cbTable > USHRT_MAX) + { + Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4; + } + else + { + Schema.m_heaps &= ~CMiniMdSchema::HEAP_STRING_4; + } + + IfFailGo(m_BlobHeap.GetAlignedSize(&cbTable)); + if (cbTable > USHRT_MAX) + { + Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4; + } + else + { + Schema.m_heaps &= ~CMiniMdSchema::HEAP_BLOB_4; + } + + if (m_GuidHeap.GetSize() > USHRT_MAX) + { + Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4; + } + else + { + Schema.m_heaps &= ~CMiniMdSchema::HEAP_GUID_4; + } + + cbTotal = 0; + // schema isn't saved for the hot metadata + if (pProfileData == NULL) + { + cbTotal = Schema.SaveTo(SchemaBuf); + if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0) + cbTotal += cbAlign; + } + + // For each table... + ULONG ixTbl; + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + if (GetCountRecs(ixTbl)) + { + // Determine how big the compressed table will be. + + // Allocate a def for the temporary table. + sTempTable = m_TableDefs[ixTbl]; + if (m_eGrow == eg_grown) + { + IfFailGo(rTempCols.ReSizeNoThrow(sTempTable.m_cCols)); + sTempTable.m_pColDefs = rTempCols.Ptr(); + + // Initialize temp table col defs based on actual counts of data in the + // real tables. + IfFailGo(InitColsForTable(Schema, ixTbl, &sTempTable, 1, FALSE)); + } + + cbTable = sTempTable.m_cbRec * GetCountRecs(ixTbl); + +#ifdef FEATURE_PREJIT + if (pProfileData != NULL) + { + ULONG itemCount = GetCountRecs(ixTbl); + + // determine number of rows touched in this table as indicated by IBC profile data + ULONG hotItemCount = 0; + IfFailGo(GetHotMetadataTokensSearchAware(pProfileData, ixTbl, &hotItemCount, NULL, 0)); + + // assume ManifestResource table is touched completely if touched at all or any hot metadata at all so far + // this is because it's searched linearly, and IBC data misses an unsuccessful search + // after module load + if (ixTbl == TBL_ManifestResource && (hotItemCount > 0 || cbTotal != 0)) + hotItemCount = itemCount; + + // if the hot subset of the rows along with their side lookup tables will occupy more space + // than the full table, keep the full table to both save space and access time. + if (hotItemCount <= USHRT_MAX && itemCount <= USHRT_MAX && m_TableDefs[ixTbl].m_cbRec <= SHRT_MAX) + { + ULONG estimatedSizeUsingSubsetCopy = hotItemCount * (sizeof(WORD) + sizeof(BYTE) + m_TableDefs[ixTbl].m_cbRec); + ULONG estimatedSizeUsingFullCopy = itemCount * m_TableDefs[ixTbl].m_cbRec; + + if (estimatedSizeUsingSubsetCopy > estimatedSizeUsingFullCopy) + hotItemCount = itemCount; + } + + // first level table is array of WORD, so we can't handle more than 2**16 hot items + if (hotItemCount > USHRT_MAX) + hotItemCount = 0; + + cbTable = 0; + if (hotItemCount > 0) + { + cbTotal = Align4(cbTotal); + cbTable = 5*sizeof(DWORD) + sizeof(WORD); // header: count, 4 offsets, shift count + shiftCount = ShiftCount(itemCount, hotItemCount); + if (hotItemCount < itemCount) + { + cbTable += ((1<<shiftCount) + 1) * sizeof(WORD); // 1st level table + cbTable += hotItemCount*sizeof(BYTE); // 2nd level table + cbTable += hotItemCount*sizeof(WORD); // Index mapping table + } + cbTable = Align4(cbTable); // align hot metadata on 4-byte boundary + cbTable += sTempTable.m_cbRec * hotItemCount; // size of hot metadata + + LOG((LOGMD, "CMiniMdRW::GetFullSaveSize: table %2d %5d items %3d hot items %2d shift count %4d total size\n", ixTbl, itemCount, hotItemCount, shiftCount, cbTable)); + } + else + LOG((LOGMD, "CMiniMdRW::GetFullSaveSize: table %2d %5d items\n", ixTbl, itemCount)); + } +#endif //FEATURE_PREJIT + cbTotal += cbTable; + } + } + + // Pad with at least 2 bytes and align on 4 bytes. + cbAlign = Align4(cbTotal) - cbTotal; + if (cbAlign < 2) + cbAlign += 4; + cbTotal += cbAlign; + + if (pProfileData != NULL) + { +#ifdef FEATURE_PREJIT + UINT32 cbHotHeapsSize = 0; + + IfFailGo(GetHotPoolsSaveSize(&cbHotHeapsSize, reorderingOptions, pProfileData)); + cbTotal += cbHotHeapsSize; + + if (cbTotal <= 4) + cbTotal = 0; + else + cbTotal += sizeof(UINT32) + m_TblCount*sizeof(UINT32) + + 2 * sizeof(UINT32); // plus the size of hot metadata header +#endif //FEATURE_PREJIT + } + else + { + m_cbSaveSize = cbTotal; + } + + LOG((LOGMD, "CMiniMdRW::GetFullSaveSize: Total %ssize = %d\n", pProfileData ? "hot " : "", cbTotal)); + + *pcbSaveSize = cbTotal; + +ErrExit: + return hr; +} // CMiniMdRW::GetFullSaveSize + +//***************************************************************************** +// GetSaveSize for saving just the delta (ENC) data. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetENCSaveSize( // S_OK or error. + UINT32 *pcbSaveSize) // [OUT] Put the size here. +{ + HRESULT hr = S_OK; + BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema. + ULONG cbAlign; // Bytes needed for alignment. + UINT32 cbTable; // Bytes in a table. + UINT32 cbTotal; // Bytes written. + ULONG ixTbl; // Loop control. + + // If not saving deltas, defer to full GetSaveSize. + if ((m_OptionValue.m_UpdateMode & MDUpdateDelta) != MDUpdateDelta) + { + DWORD bCompressed; + return GetFullSaveSize(cssAccurate, pcbSaveSize, &bCompressed); + } + + // Make sure the minimal deltas have expanded tables + IfFailRet(ExpandTables()); + + // Build the header. + CMiniMdSchema Schema = m_Schema; + + if (m_rENCRecs != NULL) + { + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + Schema.m_cRecs[ixTbl] = m_rENCRecs[ixTbl].Count(); + } + else + { + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + Schema.m_cRecs[ixTbl] = 0; + } + + Schema.m_cRecs[TBL_Module] = m_Schema.m_cRecs[TBL_Module]; + Schema.m_cRecs[TBL_ENCLog] = m_Schema.m_cRecs[TBL_ENCLog]; + Schema.m_cRecs[TBL_ENCMap] = m_Schema.m_cRecs[TBL_ENCMap]; + + cbTotal = Schema.SaveTo(SchemaBuf); + if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0) + cbTotal += cbAlign; + + // Accumulate size of each table... + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { // ENC tables are special. + if (ixTbl == TBL_ENCLog || ixTbl == TBL_ENCMap || ixTbl == TBL_Module) + cbTable = m_Schema.m_cRecs[ixTbl] * m_TableDefs[ixTbl].m_cbRec; + else + cbTable = Schema.m_cRecs[ixTbl] * m_TableDefs[ixTbl].m_cbRec; + cbTotal += cbTable; + } + + // Pad with at least 2 bytes and align on 4 bytes. + cbAlign = Align4(cbTotal) - cbTotal; + if (cbAlign < 2) + cbAlign += 4; + cbTotal += cbAlign; + + *pcbSaveSize = cbTotal; + m_cbSaveSize = cbTotal; + +//ErrExit: + return hr; +} // CMiniMdRW::GetENCSaveSize + + +#ifdef FEATURE_PREJIT + +// Determine the size of the hot blob data +// +__checkReturn +HRESULT +CMiniMdRW::GetHotPoolsSaveSize( + UINT32 *pcbSize, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr = S_OK; + UINT32 cbSavedDirSize = 0; + UINT32 cbSavedHeapsSize = 0; + + StreamUtil::NullStream stream; + IfFailGo(SaveHotPoolsToStream( + &stream, + reorderingOptions, + pProfileData, + &cbSavedDirSize, + &cbSavedHeapsSize)); + *pcbSize = cbSavedDirSize + cbSavedHeapsSize; + +ErrExit: + return hr; +} // CMiniMdRW::GetHotPoolsSaveSize + +#endif //FEATURE_PREJIT + + +//***************************************************************************** +// Determine how big the tables would be when saved. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetSaveSize( + CorSaveSize fSave, // [IN] cssAccurate or cssQuick. + UINT32 *pcbSaveSize, // [OUT] Put the size here. + DWORD *pbSaveCompressed, // [OUT] Will the saved data be fully compressed? + MetaDataReorderingOptions reorderingOptions, // [IN] Optional metadata reordering options + CorProfileData *pProfileData) // [IN] Optional IBC profile data for working set optimization +{ + HRESULT hr; + + // Prepare the data for save. + IfFailGo(PreSave()); + + switch (m_OptionValue.m_UpdateMode & MDUpdateMask) + { + case MDUpdateFull: + hr = GetFullSaveSize(fSave, pcbSaveSize, pbSaveCompressed, reorderingOptions, pProfileData); + break; + case MDUpdateIncremental: + case MDUpdateExtension: + case MDUpdateENC: + hr = GetFullSaveSize(fSave, pcbSaveSize, pbSaveCompressed, NoReordering, pProfileData); + // never save compressed if it is incremental compilation. + *pbSaveCompressed = false; + break; + case MDUpdateDelta: + *pbSaveCompressed = false; + hr = GetENCSaveSize(pcbSaveSize); + break; + default: + _ASSERTE(!"Internal error -- unknown save mode"); + return E_INVALIDARG; + } + +ErrExit: + return hr; +} // CMiniMdRW::GetSaveSize + +//***************************************************************************** +// Determine how big a pool would be when saved full size. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetFullPoolSaveSize( // S_OK or error. + int iPool, // The pool of interest. + UINT32 *pcbSaveSize) // [OUT] Put the size here. +{ + HRESULT hr; + + switch (iPool) + { + case MDPoolStrings: + hr = m_StringHeap.GetAlignedSize(pcbSaveSize); + break; + case MDPoolGuids: + *pcbSaveSize = m_GuidHeap.GetSize(); + hr = S_OK; + break; + case MDPoolBlobs: + hr = m_BlobHeap.GetAlignedSize(pcbSaveSize); + break; + case MDPoolUSBlobs: + hr = m_UserStringHeap.GetAlignedSize(pcbSaveSize); + break; + default: + hr = E_INVALIDARG; + } + + return hr; +} // CMiniMdRW::GetFullPoolSaveSize + +//***************************************************************************** +// Determine how big a pool would be when saved ENC size. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetENCPoolSaveSize( + int iPool, // The pool of interest. + UINT32 *pcbSaveSize) // [OUT] Put the size here. +{ + HRESULT hr; + + switch (iPool) + { + case MDPoolStrings: + IfFailRet(m_StringHeap.GetEnCSessionAddedHeapSize_Aligned(pcbSaveSize)); + hr = S_OK; + break; + case MDPoolGuids: + // We never save delta guid heap, we save full guid heap everytime + *pcbSaveSize = m_GuidHeap.GetSize(); + hr = S_OK; + break; + case MDPoolBlobs: + IfFailRet(m_BlobHeap.GetEnCSessionAddedHeapSize_Aligned(pcbSaveSize)); + hr = S_OK; + break; + case MDPoolUSBlobs: + IfFailRet(m_UserStringHeap.GetEnCSessionAddedHeapSize_Aligned(pcbSaveSize)); + hr = S_OK; + break; + default: + hr = E_INVALIDARG; + } + + return hr; +} // CMiniMdRW::GetENCPoolSaveSize + +//***************************************************************************** +// Determine how big a pool would be when saved. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::GetPoolSaveSize( + int iPool, // The pool of interest. + UINT32 *pcbSaveSize) // [OUT] Put the size here. +{ + HRESULT hr; + + switch (m_OptionValue.m_UpdateMode & MDUpdateMask) + { + case MDUpdateFull: + case MDUpdateIncremental: + case MDUpdateExtension: + case MDUpdateENC: + hr = GetFullPoolSaveSize(iPool, pcbSaveSize); + break; + case MDUpdateDelta: + hr = GetENCPoolSaveSize(iPool, pcbSaveSize); + break; + default: + _ASSERTE(!"Internal error -- unknown save mode"); + return E_INVALIDARG; + } + + return hr; +} // CMiniMdRW::GetPoolSaveSize + +//***************************************************************************** +// Is the given pool empty? +//***************************************************************************** +int CMiniMdRW::IsPoolEmpty( // True or false. + int iPool) // The pool of interest. +{ + switch (iPool) + { + case MDPoolStrings: + return m_StringHeap.IsEmpty(); + case MDPoolGuids: + return m_GuidHeap.IsEmpty(); + case MDPoolBlobs: + return m_BlobHeap.IsEmpty(); + case MDPoolUSBlobs: + return m_UserStringHeap.IsEmpty(); + } + return true; +} // CMiniMdRW::IsPoolEmpty + +// -------------------------------------------------------------------------------------- +// +// Gets user string (*Data) at index (nIndex) and fills the index (*pnNextIndex) of the next user string +// in the heap. +// Returns S_OK and fills the string (*pData) and the next index (*pnNextIndex). +// Returns S_FALSE if the index (nIndex) is not valid user string index. +// Returns error code otherwise. +// Clears *pData and sets *pnNextIndex to 0 on error or S_FALSE. +// +__checkReturn +HRESULT +CMiniMdRW::GetUserStringAndNextIndex( + UINT32 nIndex, + MetaData::DataBlob *pData, + UINT32 *pnNextIndex) +{ + HRESULT hr = S_OK; + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + + // First check that the index is valid to avoid debug error reporting + // If this turns out to be slow, then we can add a new API to BlobHeap "GetBlobWithSizePrefix_DontFail" + // to merge this check with following GetBlobWithSizePrefix call + if (!m_UserStringHeap.IsValidIndex(nIndex)) + { + return S_FALSE; + } + + // Get user string at index nIndex (verifies that the user string is in the heap) + IfFailGo(m_UserStringHeap.GetBlobWithSizePrefix( + nIndex, + pData)); + _ASSERTE(hr == S_OK); + + // Get index behind the user string - doesn't overflow, because the user string is in the heap + *pnNextIndex = nIndex + pData->GetSize(); + + UINT32 cbUserStringSize_Ignore; + if (!pData->GetCompressedU(&cbUserStringSize_Ignore)) + { + Debug_ReportInternalError("There's a bug, because previous call to GetBlobWithSizePrefix succeeded."); + IfFailGo(METADATA_E_INTERNAL_ERROR); + } + return S_OK; + +ErrExit: + // Fill output parameters on error + *pnNextIndex = 0; + pData->Clear(); + + return hr; +} // CMiniMdRW::GetUserStringAndNextIndex + +//***************************************************************************** +// Initialized TokenRemapManager +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::InitTokenRemapManager() +{ + HRESULT hr = NOERROR; + + if (m_pTokenRemapManager == NULL) + { + // allocate TokenRemapManager + m_pTokenRemapManager = new (nothrow) TokenRemapManager; + IfNullGo(m_pTokenRemapManager); + } + + // initialize the ref to def optimization map + IfFailGo( m_pTokenRemapManager->ClearAndEnsureCapacity(m_Schema.m_cRecs[TBL_TypeRef], m_Schema.m_cRecs[TBL_MemberRef])); + +ErrExit: + return hr; +} // CMiniMdRW::InitTokenRemapManager + +//***************************************************************************** +// Debug code to check whether a table's objects can have custom attributes +// attached. +//***************************************************************************** +#ifdef _DEBUG +bool CMiniMdRW::CanHaveCustomAttribute( // Can a given table have a custom attribute token? + ULONG ixTbl) // Table in question. +{ + mdToken tk = GetTokenForTable(ixTbl); + size_t ix; + for (ix=0; ix<g_CodedTokens[CDTKN_HasCustomAttribute].m_cTokens; ++ix) + if (g_CodedTokens[CDTKN_HasCustomAttribute].m_pTokens[ix] == tk) + return true; + return false; +} // CMiniMdRW::CanHaveCustomAttribute +#endif //_DEBUG + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +//--------------------------------------------------------------------------------------- +// +// Perform any available pre-save optimizations. +// +__checkReturn +HRESULT +CMiniMdRW::PreSaveFull() +{ + HRESULT hr = S_OK; + RID ridPtr; // A RID from a pointer table. + + if (m_bPreSaveDone) + return hr; + + // Don't yet know what the save size will be. + m_cbSaveSize = 0; + m_bSaveCompressed = false; + + // Convert any END_OF_TABLE values for tables with child pointer tables. + IfFailGo(ConvertMarkerToEndOfTable( + TBL_TypeDef, + TypeDefRec::COL_MethodList, + m_Schema.m_cRecs[TBL_Method] + 1, + m_Schema.m_cRecs[TBL_TypeDef])); + IfFailGo(ConvertMarkerToEndOfTable( + TBL_TypeDef, + TypeDefRec::COL_FieldList, + m_Schema.m_cRecs[TBL_Field] + 1, + m_Schema.m_cRecs[TBL_TypeDef])); + IfFailGo(ConvertMarkerToEndOfTable( + TBL_Method, + MethodRec::COL_ParamList, + m_Schema.m_cRecs[TBL_Param]+1, + m_Schema.m_cRecs[TBL_Method])); + IfFailGo(ConvertMarkerToEndOfTable( + TBL_PropertyMap, + PropertyMapRec::COL_PropertyList, + m_Schema.m_cRecs[TBL_Property] + 1, + m_Schema.m_cRecs[TBL_PropertyMap])); + IfFailGo(ConvertMarkerToEndOfTable( + TBL_EventMap, + EventMapRec::COL_EventList, + m_Schema.m_cRecs[TBL_Event] + 1, + m_Schema.m_cRecs[TBL_EventMap])); + + // If there is a handler and in "Full" mode, eliminate the intermediate tables. + if ((m_pHandler != NULL) && ((m_OptionValue.m_UpdateMode &MDUpdateMask) == MDUpdateFull)) + { + // If there is a handler, and not in E&C, save as fully compressed. + m_bSaveCompressed = true; + + // Temporary tables for new Fields, Methods, Params and FieldLayouts. + MetaData::TableRW newFields; + IfFailGo(newFields.InitializeEmpty_WithRecordCount( + m_TableDefs[TBL_Field].m_cbRec, + m_Schema.m_cRecs[TBL_Field] + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(newFields.Debug_SetTableInfo("TBL_Field", TBL_Field)); + + MetaData::TableRW newMethods; + IfFailGo(newMethods.InitializeEmpty_WithRecordCount( + m_TableDefs[TBL_Method].m_cbRec, + m_Schema.m_cRecs[TBL_Method] + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(newMethods.Debug_SetTableInfo("TBL_Method", TBL_Method)); + + MetaData::TableRW newParams; + IfFailGo(newParams.InitializeEmpty_WithRecordCount( + m_TableDefs[TBL_Param].m_cbRec, + m_Schema.m_cRecs[TBL_Param] + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(newParams.Debug_SetTableInfo("TBL_Param", TBL_Param)); + + MetaData::TableRW newEvents; + IfFailGo(newEvents.InitializeEmpty_WithRecordCount( + m_TableDefs[TBL_Event].m_cbRec, + m_Schema.m_cRecs[TBL_Event] + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(newEvents.Debug_SetTableInfo("TBL_Event", TBL_Event)); + + MetaData::TableRW newPropertys; + IfFailGo(newPropertys.InitializeEmpty_WithRecordCount( + m_TableDefs[TBL_Property].m_cbRec, + m_Schema.m_cRecs[TBL_Property] + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(newPropertys.Debug_SetTableInfo("TBL_Property", TBL_Property)); + + // If we have any indirect table for Field or Method and we are about to reorder these + // tables, the MemberDef hash table will be invalid after the token movement. So invalidate + // the hash. + if ((HasIndirectTable(TBL_Field) || HasIndirectTable(TBL_Method)) && (m_pMemberDefHash != NULL)) + { + delete m_pMemberDefHash; + m_pMemberDefHash = NULL; + } + + // Enumerate fields and copy. + if (HasIndirectTable(TBL_Field)) + { + for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Field]; ++ridPtr) + { + BYTE * pOldPtr; + IfFailGo(m_Tables[TBL_FieldPtr].GetRecord(ridPtr, &pOldPtr)); + RID ridOld; + ridOld = GetCol(TBL_FieldPtr, FieldPtrRec::COL_Field, pOldPtr); + BYTE * pOld; + IfFailGo(m_Tables[TBL_Field].GetRecord(ridOld, &pOld)); + RID ridNew; + BYTE * pNew; + IfFailGo(newFields.AddRecord(&pNew, (UINT32 *)&ridNew)); + _ASSERTE(ridNew == ridPtr); + memcpy(pNew, pOld, m_TableDefs[TBL_Field].m_cbRec); + + // Let the caller know of the token change. + IfFailGo(MapToken(ridOld, ridNew, mdtFieldDef)); + } + } + + // Enumerate methods and copy. + if (HasIndirectTable(TBL_Method) || HasIndirectTable(TBL_Param)) + { + for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Method]; ++ridPtr) + { + MethodRec * pOld; + RID ridOld; + BYTE * pNew = NULL; + if (HasIndirectTable(TBL_Method)) + { + BYTE * pOldPtr; + IfFailGo(m_Tables[TBL_MethodPtr].GetRecord(ridPtr, &pOldPtr)); + ridOld = GetCol(TBL_MethodPtr, MethodPtrRec::COL_Method, pOldPtr); + IfFailGo(GetMethodRecord(ridOld, &pOld)); + RID ridNew; + IfFailGo(newMethods.AddRecord(&pNew, (UINT32 *)&ridNew)); + _ASSERTE(ridNew == ridPtr); + memcpy(pNew, pOld, m_TableDefs[TBL_Method].m_cbRec); + + // Let the caller know of the token change. + IfFailGo(MapToken(ridOld, ridNew, mdtMethodDef)); + } + else + { + ridOld = ridPtr; + IfFailGo(GetMethodRecord(ridPtr, &pOld)); + } + + // Handle the params of the method. + if (HasIndirectTable(TBL_Method)) + { + IfFailGo(PutCol(TBL_Method, MethodRec::COL_ParamList, pNew, newParams.GetRecordCount() + 1)); + } + RID ixStart = getParamListOfMethod(pOld); + RID ixEnd; + IfFailGo(getEndParamListOfMethod(ridOld, &ixEnd)); + for (; ixStart<ixEnd; ++ixStart) + { + RID ridParam; + if (HasIndirectTable(TBL_Param)) + { + BYTE * pOldPtr; + IfFailGo(m_Tables[TBL_ParamPtr].GetRecord(ixStart, &pOldPtr)); + ridParam = GetCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pOldPtr); + } + else + { + ridParam = ixStart; + } + BYTE * pOldRecord; + IfFailGo(m_Tables[TBL_Param].GetRecord(ridParam, &pOldRecord)); + RID ridNew; + BYTE * pNewRecord; + IfFailGo(newParams.AddRecord(&pNewRecord, (UINT32 *)&ridNew)); + memcpy(pNewRecord, pOldRecord, m_TableDefs[TBL_Param].m_cbRec); + + // Let the caller know of the token change. + IfFailGo(MapToken(ridParam, ridNew, mdtParamDef)); + } + } + } + + // Get rid of EventPtr and PropertyPtr table as well + // Enumerate fields and copy. + if (HasIndirectTable(TBL_Event)) + { + for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Event]; ++ridPtr) + { + BYTE * pOldPtr; + IfFailGo(m_Tables[TBL_EventPtr].GetRecord(ridPtr, &pOldPtr)); + RID ridOld; + ridOld = GetCol(TBL_EventPtr, EventPtrRec::COL_Event, pOldPtr); + BYTE * pOld; + IfFailGo(m_Tables[TBL_Event].GetRecord(ridOld, &pOld)); + RID ridNew; + BYTE * pNew; + IfFailGo(newEvents.AddRecord(&pNew, (UINT32 *)&ridNew)); + _ASSERTE(ridNew == ridPtr); + memcpy(pNew, pOld, m_TableDefs[TBL_Event].m_cbRec); + + // Let the caller know of the token change. + IfFailGo(MapToken(ridOld, ridNew, mdtEvent)); + } + } + + if (HasIndirectTable(TBL_Property)) + { + for (ridPtr = 1; ridPtr <= m_Schema.m_cRecs[TBL_Property]; ++ridPtr) + { + BYTE * pOldPtr; + IfFailGo(m_Tables[TBL_PropertyPtr].GetRecord(ridPtr, &pOldPtr)); + RID ridOld; + ridOld = GetCol(TBL_PropertyPtr, PropertyPtrRec::COL_Property, pOldPtr); + BYTE * pOld; + IfFailGo(m_Tables[TBL_Property].GetRecord(ridOld, &pOld)); + RID ridNew; + BYTE * pNew; + IfFailGo(newPropertys.AddRecord(&pNew, (UINT32 *)&ridNew)); + _ASSERTE(ridNew == ridPtr); + memcpy(pNew, pOld, m_TableDefs[TBL_Property].m_cbRec); + + // Let the caller know of the token change. + IfFailGo(MapToken(ridOld, ridNew, mdtProperty)); + } + } + + + // Replace the old tables with the new, sorted ones. + if (HasIndirectTable(TBL_Field)) + { + m_Tables[TBL_Field].Delete(); + IfFailGo(m_Tables[TBL_Field].InitializeFromTable( + &newFields, + TRUE)); // fCopyData + } + if (HasIndirectTable(TBL_Method)) + { + m_Tables[TBL_Method].Delete(); + IfFailGo(m_Tables[TBL_Method].InitializeFromTable( + &newMethods, + TRUE)); // fCopyData + } + if (HasIndirectTable(TBL_Method) || HasIndirectTable(TBL_Param)) + { + m_Tables[TBL_Param].Delete(); + IfFailGo(m_Tables[TBL_Param].InitializeFromTable( + &newParams, + TRUE)); // fCopyData + } + if (HasIndirectTable(TBL_Property)) + { + m_Tables[TBL_Property].Delete(); + IfFailGo(m_Tables[TBL_Property].InitializeFromTable( + &newPropertys, + TRUE)); // fCopyData + } + if (HasIndirectTable(TBL_Event)) + { + m_Tables[TBL_Event].Delete(); + IfFailGo(m_Tables[TBL_Event].InitializeFromTable( + &newEvents, + TRUE)); // fCopyData + } + + // Empty the pointer tables table. + m_Schema.m_cRecs[TBL_FieldPtr] = 0; + m_Schema.m_cRecs[TBL_MethodPtr] = 0; + m_Schema.m_cRecs[TBL_ParamPtr] = 0; + m_Schema.m_cRecs[TBL_PropertyPtr] = 0; + m_Schema.m_cRecs[TBL_EventPtr] = 0; + + // invalidated the parent look up tables + if (m_pMethodMap) + { + delete m_pMethodMap; + m_pMethodMap = NULL; + } + if (m_pFieldMap) + { + delete m_pFieldMap; + m_pFieldMap = NULL; + } + if (m_pPropertyMap) + { + delete m_pPropertyMap; + m_pPropertyMap = NULL; + } + if (m_pEventMap) + { + delete m_pEventMap; + m_pEventMap = NULL; + } + if (m_pParamMap) + { + delete m_pParamMap; + m_pParamMap = NULL; + } + } + + // Do the ref to def fixup before fix up with token movement + IfFailGo(FixUpRefToDef()); + + //////////////////////////////////////////////////////////////////////////// + // + // We now need to do two kinds of fixups, and the two fixups interact with + // each other. + // 1) We need to sort several tables for binary searching. + // 2) We need to fixup any references to other tables, which may have + // changed due to ref-to-def, ptr-table elimination, or sorting. + // + + + // First do fixups. Some of these are then sorted based on fixed-up columns. + + IfFailGo(FixUpTable(TBL_MemberRef)); + IfFailGo(FixUpTable(TBL_MethodSemantics)); + IfFailGo(FixUpTable(TBL_Constant)); + IfFailGo(FixUpTable(TBL_FieldMarshal)); + IfFailGo(FixUpTable(TBL_MethodImpl)); + IfFailGo(FixUpTable(TBL_DeclSecurity)); + IfFailGo(FixUpTable(TBL_ImplMap)); + IfFailGo(FixUpTable(TBL_FieldRVA)); + IfFailGo(FixUpTable(TBL_FieldLayout)); + + if (SupportsGenerics()) + { + IfFailGo(FixUpTable(TBL_GenericParam)); + IfFailGo(FixUpTable(TBL_MethodSpec)); + } + + // Now sort any tables that are allowed to have custom attributes. + // This block for tables sorted in full mode only -- basically + // tables for which we hand out tokens. + if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull) + { + if (SupportsGenerics()) + { + // Sort the GenericParam table by the Owner. + // Don't disturb the sequence ordering within Owner + STABLESORTER_WITHREMAP(GenericParam, Owner); + IfFailGo(sortGenericParam.Sort()); + } + + // Sort the InterfaceImpl table by class. + STABLESORTER_WITHREMAP(InterfaceImpl, Class); + IfFailGo(sortInterfaceImpl.Sort()); + + // Sort the DeclSecurity table by parent. + SORTER_WITHREMAP(DeclSecurity, Parent); + IfFailGo(sortDeclSecurity.Sort()); + } + + // The GenericParamConstraint table is parented to the GenericParam table, + // so it needs fixup after sorting GenericParam table. + if (SupportsGenerics()) + { + IfFailGo(FixUpTable(TBL_GenericParamConstraint)); + + // After fixing up the GenericParamConstraint table, we can then + // sort it. + if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull) + { + // Sort the GenericParamConstraint table by the Owner. + // Don't disturb the sequence ordering within Owner + STABLESORTER_WITHREMAP(GenericParamConstraint, Owner); + IfFailGo(sortGenericParamConstraint.Sort()); + } + } + // Fixup the custom attribute table. After this, do not sort any table + // that is allowed to have a custom attribute. + IfFailGo(FixUpTable(TBL_CustomAttribute)); + + // Sort tables for binary searches. + if (((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull) || + ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateIncremental)) + { + // Sort tables as required + //------------------------------------------------------------------------- + // Module order is preserved + // TypeRef order is preserved + // TypeDef order is preserved + // Field grouped and pointed to by TypeDef + // Method grouped and pointed to by TypeDef + // Param grouped and pointed to by Method + // InterfaceImpl sorted here + // MemberRef order is preserved + // Constant sorted here + // CustomAttribute sorted INCORRECTLY!! here + // FieldMarshal sorted here + // DeclSecurity sorted here + // ClassLayout created in order with TypeDefs + // FieldLayout grouped and pointed to by ClassLayouts + // StandaloneSig order is preserved + // TypeSpec order is preserved + // EventMap created in order at conversion (by Event Parent) + // Event sorted by Parent at conversion + // PropertyMap created in order at conversion (by Property Parent) + // Property sorted by Parent at conversion + // MethodSemantics sorted by Association at conversion. + // MethodImpl sorted here. + // Sort the constant table by parent. + // Sort the nested class table by NestedClass. + // Sort the generic par table by Owner + // MethodSpec order is preserved + + // Always sort Constant table + _ASSERTE(!CanHaveCustomAttribute(TBL_Constant)); + SORTER(Constant, Parent); + sortConstant.Sort(); + + // Always sort the FieldMarshal table by Parent. + _ASSERTE(!CanHaveCustomAttribute(TBL_FieldMarshal)); + SORTER(FieldMarshal, Parent); + sortFieldMarshal.Sort(); + + // Always sort the MethodSematics + _ASSERTE(!CanHaveCustomAttribute(TBL_MethodSemantics)); + SORTER(MethodSemantics, Association); + sortMethodSemantics.Sort(); + + // Always Sort the ClassLayoutTable by parent. + _ASSERTE(!CanHaveCustomAttribute(TBL_ClassLayout)); + SORTER(ClassLayout, Parent); + sortClassLayout.Sort(); + + // Always Sort the FieldLayoutTable by parent. + _ASSERTE(!CanHaveCustomAttribute(TBL_FieldLayout)); + SORTER(FieldLayout, Field); + sortFieldLayout.Sort(); + + // Always Sort the ImplMap table by the parent. + _ASSERTE(!CanHaveCustomAttribute(TBL_ImplMap)); + SORTER(ImplMap, MemberForwarded); + sortImplMap.Sort(); + + // Always Sort the FieldRVA table by the Field. + _ASSERTE(!CanHaveCustomAttribute(TBL_FieldRVA)); + SORTER(FieldRVA, Field); + sortFieldRVA.Sort(); + + // Always Sort the NestedClass table by the NestedClass. + _ASSERTE(!CanHaveCustomAttribute(TBL_NestedClass)); + SORTER(NestedClass, NestedClass); + sortNestedClass.Sort(); + + // Always Sort the MethodImpl table by the Class. + _ASSERTE(!CanHaveCustomAttribute(TBL_MethodImpl)); + SORTER(MethodImpl, Class); + sortMethodImpl.Sort(); + + // Some tokens are not moved in ENC mode; only "full" mode. + if ((m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateFull) + { + // Sort the CustomAttribute table by parent. + _ASSERTE(!CanHaveCustomAttribute(TBL_CustomAttribute)); + SORTER_WITHREMAP(CustomAttribute, Parent); + IfFailGo(sortCustomAttribute.Sort()); + } + + // Determine if the PropertyMap and EventMap are already sorted, and set the flag appropriately + SORTER(PropertyMap, Parent); + sortPropertyMap.CheckSortedWithNoDuplicates(); + + SORTER(EventMap, Parent); + sortEventMap.CheckSortedWithNoDuplicates(); + + //------------------------------------------------------------------------- + } // enclosing scope required for initialization ("goto" above skips initialization). + + m_bPreSaveDone = true; + + // send the Ref->Def optmization notification to host + if (m_pHandler != NULL) + { + TOKENMAP * ptkmap = GetMemberRefToMemberDefMap(); + PREFIX_ASSUME(ptkmap != NULL); // RegMeta always inits this. + MDTOKENMAP * ptkRemap = GetTokenMovementMap(); + int iCount = m_Schema.m_cRecs[TBL_MemberRef]; + mdToken tkTo; + mdToken tkDefTo; + int i; + MemberRefRec * pMemberRefRec; // A MemberRefRec. + const COR_SIGNATURE * pvSig; // Signature of the MemberRef. + ULONG cbSig; // Size of the signature blob. + + // loop through all LocalVar + for (i = 1; i <= iCount; i++) + { + tkTo = *(ptkmap->Get(i)); + if (RidFromToken(tkTo) != mdTokenNil) + { + // so far, the parent of memberref can be changed to only fielddef or methoddef + // or it will remain unchanged. + // + _ASSERTE((TypeFromToken(tkTo) == mdtFieldDef) || (TypeFromToken(tkTo) == mdtMethodDef)); + + IfFailGo(GetMemberRefRecord(i, &pMemberRefRec)); + IfFailGo(getSignatureOfMemberRef(pMemberRefRec, &pvSig, &cbSig)); + + // Don't turn mr's with vararg's into defs, because the variable portion + // of the call is kept in the mr signature. + if ((pvSig != NULL) && isCallConv(*pvSig, IMAGE_CEE_CS_CALLCONV_VARARG)) + continue; + + // ref is optimized to the def + + // now remap the def since def could be moved again. + tkDefTo = ptkRemap->SafeRemap(tkTo); + + // when Def token moves, it will not change type!! + _ASSERTE(TypeFromToken(tkTo) == TypeFromToken(tkDefTo)); + LOG((LOGMD, "MapToken (remap): from 0x%08x to 0x%08x\n", TokenFromRid(i, mdtMemberRef), tkDefTo)); + m_pHandler->Map(TokenFromRid(i, mdtMemberRef), tkDefTo); + } + } + } + + // Ok, we've applied all of the token remaps. Make sure we don't apply them again in the future + if (GetTokenMovementMap() != NULL) + IfFailGo(GetTokenMovementMap()->EmptyMap()); + +ErrExit: + + return hr; +} // CMiniMdRW::PreSaveFull + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//--------------------------------------------------------------------------------------- +// +// ENC-specific pre-safe work. +// +__checkReturn +HRESULT +CMiniMdRW::PreSaveEnc() +{ + HRESULT hr; + int iNew; // Insertion point for new tokens. + ULONG *pul; // Found token. + ULONG iRid; // RID from a token. + ULONG ixTbl; // Table from an ENC record. + ULONG cRecs; // Count of records in a table. + + IfFailGo(PreSaveFull()); + + // Turn off pre-save bit so that we can add ENC map records. + m_bPreSaveDone = false; + + if (m_Schema.m_cRecs[TBL_ENCLog]) + { // Keep track of ENC recs we've seen. + _ASSERTE(m_rENCRecs == 0); + m_rENCRecs = new (nothrow) ULONGARRAY[m_TblCount]; + IfNullGo(m_rENCRecs); + + // Create the temporary table. + MetaData::TableRW tempTable; + IfFailGo(tempTable.InitializeEmpty_WithRecordCount( + m_TableDefs[TBL_ENCLog].m_cbRec, + m_Schema.m_cRecs[TBL_ENCLog] + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(tempTable.Debug_SetTableInfo("TBL_ENCLog", TBL_ENCLog)); + + // For each row in the data. + RID rid; + ULONG iKept=0; + for (rid=1; rid<=m_Schema.m_cRecs[TBL_ENCLog]; ++rid) + { + ENCLogRec *pFrom; + IfFailGo(m_Tables[TBL_ENCLog].GetRecord(rid, reinterpret_cast<BYTE **>(&pFrom))); + + // Keep this record? + if (pFrom->GetFuncCode() == 0) + { // No func code. Skip if we've seen this token before. + + // What kind of record is this? + if (IsRecId(pFrom->GetToken())) + { // Non-token table + iRid = RidFromRecId(pFrom->GetToken()); + ixTbl = TblFromRecId(pFrom->GetToken()); + } + else + { // Token table. + iRid = RidFromToken(pFrom->GetToken()); + ixTbl = GetTableForToken(pFrom->GetToken()); + + } + + RIDBinarySearch searcher((UINT32 *)m_rENCRecs[ixTbl].Ptr(), m_rENCRecs[ixTbl].Count()); + pul = (ULONG *)(searcher.Find((UINT32 *)&iRid, &iNew)); + // If we found the token, don't keep the record. + if (pul != 0) + { + LOG((LOGMD, "PreSave ENCLog skipping duplicate token %d", pFrom->GetToken())); + continue; + } + // First time token was seen, so keep track of it. + IfNullGo(pul = m_rENCRecs[ixTbl].Insert(iNew)); + *pul = iRid; + } + + // Keeping the record, so allocate the new record to hold it. + ++iKept; + RID ridNew; + ENCLogRec *pTo; + IfFailGo(tempTable.AddRecord(reinterpret_cast<BYTE **>(&pTo), (UINT32 *)&ridNew)); + _ASSERTE(ridNew == iKept); + + // copy the data. + *pTo = *pFrom; + } + + // Keep the expanded table. + m_Tables[TBL_ENCLog].Delete(); + IfFailGo(m_Tables[TBL_ENCLog].InitializeFromTable( + &tempTable, + TRUE)); // fCopyData + INDEBUG_MD(m_Tables[TBL_ENCLog].Debug_SetTableInfo("TBL_ENCLog", TBL_ENCLog)); + m_Schema.m_cRecs[TBL_ENCLog] = iKept; + + // If saving only deltas, build the ENC Map table. + if (((m_OptionValue.m_UpdateMode & MDUpdateDelta)) == MDUpdateDelta) + { + cRecs = 0; + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + cRecs += m_rENCRecs[ixTbl].Count(); + } + m_Tables[TBL_ENCMap].Delete(); + + m_Schema.m_cRecs[TBL_ENCMap] = 0; + + IfFailGo(m_Tables[TBL_ENCMap].InitializeEmpty_WithRecordCount( + m_TableDefs[TBL_ENCMap].m_cbRec, + cRecs + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(m_Tables[TBL_ENCMap].Debug_SetTableInfo("TBL_ENCMap", TBL_ENCMap)); + cRecs = 0; + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + ENCMapRec *pNew; + ULONG nNew; + for (int i=0; i<m_rENCRecs[ixTbl].Count(); ++i) + { + IfFailGo(AddENCMapRecord(&pNew, &nNew)); // pre-allocated for all rows. + _ASSERTE(nNew == ++cRecs); + _ASSERTE(TblFromRecId(RecIdFromRid(m_rENCRecs[ixTbl][i], ixTbl)) < m_TblCount); + pNew->SetToken(RecIdFromRid(m_rENCRecs[ixTbl][i], ixTbl)); + } + } + } + } + + // Turn pre-save bit back on. + m_bPreSaveDone = true; + +ErrExit: + return hr; +} // CMiniMdRW::PreSaveEnc + +//***************************************************************************** +// Perform any appropriate pre-save optimization or reorganization. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PreSave( + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr = S_OK; + +#ifdef _DEBUG + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_PreSaveBreak)) + { + _ASSERTE(!"CMiniMdRW::PreSave()"); + } +#endif //_DEBUG + + if (m_bPreSaveDone) + return hr; + +#ifdef FEATURE_PREJIT + // Reorganization should be done at ngen time only + if( reorderingOptions & ReArrangeStringPool ) + { + EX_TRY + { + OrganizeStringPool(pProfileData); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions) + IfFailRet(hr); + } +#endif // FEATURE_PREJIT + + switch (m_OptionValue.m_UpdateMode & MDUpdateMask) + { + case MDUpdateFull: + case MDUpdateIncremental: + case MDUpdateExtension: + hr = PreSaveFull(); + break; + // PreSaveEnc removes duplicate entries in the ENCLog table, + // which we need to do regardless if we're saving a full MD + // or a minimal delta. + case MDUpdateDelta: + case MDUpdateENC: + hr = PreSaveEnc(); + break; + default: + _ASSERTE(!"Internal error -- unknown save mode"); + return E_INVALIDARG; + } + + return hr; +} // CMiniMdRW::PreSave + +//***************************************************************************** +// Perform any necessary post-save cleanup. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PostSave() +{ + if (m_rENCRecs) + { + delete [] m_rENCRecs; + m_rENCRecs = 0; + } + + m_bPreSaveDone = false; + + return S_OK; +} // CMiniMdRW::PostSave + +//***************************************************************************** +// Save the tables to the stream. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SaveFullTablesToStream( + IStream *pIStream, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr; + CMiniTableDef sTempTable; // Definition for a temporary table. + CQuickArray<CMiniColDef> rTempCols; // Definition for a temp table's columns. + BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema. + ULONG cbAlign; // Bytes needed for alignment. + UINT32 cbTable; // Bytes in a table. + UINT32 cbTotal; // Bytes written. + static const unsigned char zeros[8] = {0}; // For padding and alignment. + +#ifndef FEATURE_PREJIT + _ASSERTE(pProfileData == NULL); +#endif //!FEATURE_PREJIT + + // Write the header. + CMiniMdSchema Schema = m_Schema; + IfFailGo(m_StringHeap.GetAlignedSize(&cbTable)); + if (cbTable > USHRT_MAX) + { + Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4; + } + else + { + Schema.m_heaps &= ~CMiniMdSchema::HEAP_STRING_4; + } + + if (m_GuidHeap.GetSize() > USHRT_MAX) + { + Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4; + } + else + { + Schema.m_heaps &= ~CMiniMdSchema::HEAP_GUID_4; + } + + IfFailGo(m_BlobHeap.GetAlignedSize(&cbTable)); + if (cbTable > USHRT_MAX) + { + Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4; + } + else + { + Schema.m_heaps &= ~CMiniMdSchema::HEAP_BLOB_4; + } + + cbTotal = 0; + if (pProfileData == NULL) + { + cbTotal = Schema.SaveTo(SchemaBuf); + IfFailGo(pIStream->Write(SchemaBuf, cbTotal, 0)); + if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0) + IfFailGo(pIStream->Write(&hr, cbAlign, 0)); + cbTotal += cbAlign; + } + + ULONG headerOffset[TBL_COUNT]; + _ASSERTE(m_TblCount <= TBL_COUNT); + + ULONG ixTbl; + // For each table... + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + headerOffset[ixTbl] = ~0U; + + ULONG itemCount = GetCountRecs(ixTbl); + if (itemCount) + { +#ifdef FEATURE_PREJIT + ULONG hotItemCount = 0; + + NewArrayHolder<mdToken> hotItemList = NULL; + NewArrayHolder<TokenIndexPair> indexMapping = NULL; + + // check if we were asked to generate the hot tables + if (pProfileData != NULL) + { + // obtain the number of tokens in this table whose metadata was touched + IfFailGo(GetHotMetadataTokensSearchAware(pProfileData, ixTbl, &hotItemCount, NULL, 0)); + + // assume ManifestResource table is touched completely if touched at all or any hot metadata at all so far + // this is because it's searched linearly, and IBC data misses an unsuccessful search + // after module load + if (ixTbl == TBL_ManifestResource && (hotItemCount > 0 || cbTotal != 0)) + hotItemCount = itemCount; + + // if the hot subset of the rows along with their side lookup tables will occupy more space + // than the full table, keep the full table to save both space and access time. + if (hotItemCount <= USHRT_MAX && itemCount <= USHRT_MAX && m_TableDefs[ixTbl].m_cbRec <= SHRT_MAX) + { + ULONG estimatedSizeUsingSubsetCopy = hotItemCount * (sizeof(WORD) + sizeof(BYTE) + m_TableDefs[ixTbl].m_cbRec); + ULONG estimatedSizeUsingFullCopy = itemCount * m_TableDefs[ixTbl].m_cbRec; + + if (estimatedSizeUsingSubsetCopy > estimatedSizeUsingFullCopy) + hotItemCount = itemCount; + } + + // first level table is array of WORD, so we can't handle more than 2**16 hot items + if (hotItemCount > USHRT_MAX) + hotItemCount = 0; + + // only generate additional table if any hot items at all + if (hotItemCount > 0) + { + if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0) + IfFailGo(pIStream->Write(&hr, cbAlign, 0)); + cbTotal += cbAlign; + + headerOffset[ixTbl] = cbTotal; + + // write first part of header: hot item count + IfFailGo(pIStream->Write(&hotItemCount, sizeof(hotItemCount), 0)); + cbTotal += sizeof(hotItemCount); + + ULONG offset = 0; + if (hotItemCount < itemCount) + { + // obtain the tokens whose metadata was touched + hotItemList = new (nothrow) mdToken[hotItemCount]; + IfNullGo(hotItemList); + IfFailGo(GetHotMetadataTokensSearchAware(pProfileData, ixTbl, NULL, hotItemList, hotItemCount)); + + // construct an array of token-index pairs and save the original order of the tokens in pProfileData->GetHotTokens + // we want to write hot rows in this order to preserve the ordering optimizations done by IbcMerge + indexMapping = new (nothrow) TokenIndexPair[hotItemCount]; + IfNullGo(indexMapping); + + for (DWORD i = 0; i < hotItemCount; i++) + { + indexMapping[i].token = hotItemList[i]; + indexMapping[i].index = (WORD)i; + } + + // figure out how big the first level table should be + // and sort tokens accordingly + shiftCount = ShiftCount(itemCount, hotItemCount); + qsort(indexMapping, hotItemCount, sizeof(indexMapping[0]), TokenCmp); + + // each table has a header that consists of the hotItemCount, offsets to + // the first and second level tables, an offset to the actual data, and the + // shiftCount that determines the size of the first level table. + // see class HotTableHeader in metamodelro.h + + // we have already written the hotItemCount above. + + // so now write the offset of the first level table (just after the header) + offset = sizeof(hotItemCount) + 4*sizeof(offset) + sizeof(shiftCount); + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + + // figure out first level table size (1 extra entry at the end) + ULONG firstLevelCount = (1<<shiftCount)+1; + offset += firstLevelCount*sizeof(WORD); + + // write offset of second level table. + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + + // second level table has a byte-sized entry for each hot item + offset += hotItemCount*sizeof(BYTE); + + // write offset of index mapping table. + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + + // index mapping table has a word-sized entry for each hot item + offset += hotItemCount*sizeof(WORD); + + // actual data is just behind it, but 4-byte aligned + offset = Align4(offset); + + // write offset of actual hot metadata + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + + // write shiftCount + IfFailGo(pIStream->Write(&shiftCount, sizeof(shiftCount), 0)); + cbTotal += sizeof(shiftCount); + + // allocate tables + NewArrayHolder<WORD> firstLevelTable = new (nothrow) WORD[firstLevelCount]; + IfNullGo(firstLevelTable); + NewArrayHolder<BYTE> secondLevelTable = new (nothrow) BYTE[hotItemCount]; + IfNullGo(secondLevelTable); + NewArrayHolder<WORD> indexMappingTable = new (nothrow) WORD[hotItemCount]; + IfNullGo(indexMappingTable); + + // fill out the tables + ULONG nextFirstLevelIndex = 0; + for (DWORD i = 0; i < hotItemCount; i++) + { + // second level table contains the high order bits for each hot rid + secondLevelTable[i] = (BYTE)(RidFromToken(indexMapping[i].token) >> shiftCount); + + // the index into the first level table is the low order bits. + ULONG firstLevelIndex = indexMapping[i].token & ((1<<shiftCount)-1); + + // first level indicates where to start searching in the second level table + while (nextFirstLevelIndex <= firstLevelIndex) + firstLevelTable[nextFirstLevelIndex++] = (WORD)i; + + // index mapping table converts the index of this hot rid in the second level table + // to the index of the hot data in the cached rows + indexMappingTable[i] = indexMapping[i].index; + } + // fill remaining entries + while (nextFirstLevelIndex < firstLevelCount) + firstLevelTable[nextFirstLevelIndex++] = (WORD)hotItemCount; + + // write first level table + IfFailGo(pIStream->Write(firstLevelTable, sizeof(firstLevelTable[0])*firstLevelCount, 0)); + cbTotal += sizeof(firstLevelTable[0])*firstLevelCount; + + // write second level table + IfFailGo(pIStream->Write(secondLevelTable, sizeof(secondLevelTable[0])*hotItemCount, 0)); + cbTotal += sizeof(secondLevelTable[0])*hotItemCount; + + // write index mapping table + IfFailGo(pIStream->Write(indexMappingTable, sizeof(indexMappingTable[0])*hotItemCount, 0)); + cbTotal += sizeof(indexMappingTable[0])*hotItemCount; + + // NewArrayHolder for firstLevelTable and secondLevelTable going out of scope - no delete[] necessary + } + else + { + // in case the whole table is touched, omit the tables + // we still have a full header though with zero offsets for these tables. + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + + // offset for actual data points immediately after the header + offset += sizeof(hotItemCount) + 4*sizeof(offset) + sizeof(shiftCount); + offset = Align4(offset); + IfFailGo(pIStream->Write(&offset, sizeof(offset), 0)); + cbTotal += sizeof(offset); + shiftCount = 0; + + // write shift count + IfFailGo(pIStream->Write(&shiftCount, sizeof(shiftCount), 0)); + cbTotal += sizeof(shiftCount); + } + if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0) + IfFailGo(pIStream->Write(&hr, cbAlign, 0)); + cbTotal += cbAlign; + _ASSERTE(cbTotal == headerOffset[ixTbl] + offset); + } + } +#endif //FEATURE_PREJIT + + // Compress the records by allocating a new, temporary, table and + // copying the rows from the one to the new. + + // If the table was grown, shrink it as much as possible. + if (m_eGrow == eg_grown) + { + + // Allocate a def for the temporary table. + sTempTable = m_TableDefs[ixTbl]; + IfFailGo(rTempCols.ReSizeNoThrow(sTempTable.m_cCols)); + sTempTable.m_pColDefs = rTempCols.Ptr(); + + // Initialize temp table col defs based on actual counts of data in the + // real tables. + IfFailGo(InitColsForTable(Schema, ixTbl, &sTempTable, 1, FALSE)); + + // Create the temporary table. + MetaData::TableRW tempTable; + IfFailGo(tempTable.InitializeEmpty_WithRecordCount( + sTempTable.m_cbRec, + m_Schema.m_cRecs[ixTbl] + COMMA_INDEBUG_MD(TRUE))); + INDEBUG_MD(tempTable.Debug_SetTableInfo(NULL, ixTbl)); + + // For each row in the data. + RID rid; + for (rid=1; rid<=m_Schema.m_cRecs[ixTbl]; ++rid) + { + RID ridNew; + BYTE *pRow; + IfFailGo(m_Tables[ixTbl].GetRecord(rid, &pRow)); + BYTE *pNew; + IfFailGo(tempTable.AddRecord(&pNew, (UINT32 *)&ridNew)); + _ASSERTE(rid == ridNew); + + // For each column. + for (ULONG ixCol=0; ixCol<sTempTable.m_cCols; ++ixCol) + { + // Copy the data to the temp table. + ULONG ulVal = GetCol(ixTbl, ixCol, pRow); + IfFailGo(PutCol(rTempCols[ixCol], pNew, ulVal)); + } + } // Persist the temp table to the stream. +#ifdef FEATURE_PREJIT + if (pProfileData != NULL) + { + // only write out the hot rows as indicated by profile data + for (DWORD i = 0; i < hotItemCount; i++) + { + BYTE *pRow; + IfFailGo(tempTable.GetRecord( + hotItemList != NULL ? RidFromToken(hotItemList[i]) : i + 1, + &pRow)); + IfFailGo(pIStream->Write(pRow, sTempTable.m_cbRec, 0)); + } + cbTable = sTempTable.m_cbRec*hotItemCount; + } + else +#endif //FEATURE_PREJIT + { + IfFailGo(tempTable.GetRecordsDataSize(&cbTable)); + _ASSERTE(cbTable == sTempTable.m_cbRec * GetCountRecs(ixTbl)); + IfFailGo(tempTable.SaveToStream( + pIStream)); + } + cbTotal += cbTable; + } + else + { // Didn't grow, so just persist directly to stream. +#ifdef FEATURE_PREJIT + if (pProfileData != NULL) + { + // only write out the hot rows as indicated by profile data + for (DWORD i = 0; i < hotItemCount; i++) + { + BYTE *pRow; + IfFailGo(m_Tables[ixTbl].GetRecord( + hotItemList != NULL ? RidFromToken(hotItemList[i]) : i + 1, + &pRow)); + IfFailGo(pIStream->Write(pRow, m_TableDefs[ixTbl].m_cbRec, 0)); + } + cbTable = m_TableDefs[ixTbl].m_cbRec*hotItemCount; + } + else +#endif //FEATURE_PREJIT + { + IfFailGo(m_Tables[ixTbl].GetRecordsDataSize(&cbTable)); + _ASSERTE(cbTable == m_TableDefs[ixTbl].m_cbRec * GetCountRecs(ixTbl)); + IfFailGo(m_Tables[ixTbl].SaveToStream( + pIStream)); + } + cbTotal += cbTable; + } + // NewArrayHolder hotItemList going out of scope - no delete [] necessary + } + } + + // Pad with at least 2 bytes and align on 4 bytes. + cbAlign = Align4(cbTotal) - cbTotal; + if (cbAlign < 2) + cbAlign += 4; + IfFailGo(pIStream->Write(zeros, cbAlign, 0)); + cbTotal += cbAlign; + _ASSERTE((m_cbSaveSize == 0) || (m_cbSaveSize == cbTotal) || (pProfileData != NULL)); + +#ifdef FEATURE_PREJIT + if (pProfileData != NULL) + { + // #WritingHotMetaData write hot table directory (HotTableDirectory in MetaModelRO.h) + + // first write magic + ULONG magic = 0x484f4e44; + IfFailGo(pIStream->Write(&magic, sizeof(magic), 0)); + + // compute offsets to table headers + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + if (headerOffset[ixTbl] != ~0u) + { + headerOffset[ixTbl] -= cbTotal; + } + else + { + headerOffset[ixTbl] = 0; + } + + // write the offsets to the table headers + IfFailGo(pIStream->Write(headerOffset, sizeof(headerOffset), 0)); + cbTotal += sizeof(magic) + sizeof(headerOffset); + + UINT32 cbPoolDirSize = 0; + UINT32 cbSavedHeapsSize = 0; + + IfFailGo(SaveHotPoolsToStream( + pIStream, + reorderingOptions, + pProfileData, + &cbPoolDirSize, + &cbSavedHeapsSize)); + + // write hot metadata (including pools) header + IfFailGo(StreamUtil::WriteToStream(pIStream, (DWORD)(cbSavedHeapsSize + cbPoolDirSize))); + IfFailGo(StreamUtil::WriteToStream(pIStream, (DWORD)cbPoolDirSize)); + } +#endif //FEATURE_PREJIT + +ErrExit: + return hr; +} // CMiniMdRW::SaveFullTablesToStream + +//***************************************************************************** +// Check to see if it is safe to reorder the string pool +// The existing implementation of metadata tables is such that string offsets in different tables +// may have different sizes. +// Since we are going to reorder the string pool, offsets of strings would change and that may +// cause overflows if tables have string offsets with different sizes +//***************************************************************************** +BOOL CMiniMdRW::IsSafeToReorderStringPool() +{ +#ifdef FEATURE_PREJIT + BYTE lastColumnSize=0; + ULONG ixTbl=0, ixCol=0; + for (ixTbl=0; ixTbl<m_TblCount; ixTbl++) + { + // for every column in this row + for (ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ixCol++) + { + // proceed only when the column type is iSTRING + if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING) + { + if(lastColumnSize == 0) + { + lastColumnSize = m_TableDefs[ixTbl].m_pColDefs[ixCol].m_cbColumn; + } + else if(lastColumnSize != m_TableDefs[ixTbl].m_pColDefs[ixCol].m_cbColumn) + { + return FALSE; + } + } + } + } + return TRUE; +#else + return FALSE; +#endif // FEATURE_PREJIT +} // CMiniMdRW::IsSafeToReorderStringPool + +//***************************************************************************** +// Function to mark hot strings in the marks array based on the token information +// in profile data +//***************************************************************************** +VOID CMiniMdRW::MarkHotStrings(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize) +{ +#ifdef FEATURE_PREJIT + if(pProfileData != NULL) + { + ULONG hotItemCount = pProfileData->GetHotTokens( TBL_COUNT + MDPoolStrings, 1 << ProfilingFlags_MetaData, 1 << ProfilingFlags_MetaData, NULL, 0 ); + if(hotItemCount > 0) + { + NewArrayHolder< ULONG > hotItemList = new ULONG[hotItemCount]; + + // get hot tokens + pProfileData->GetHotTokens( TBL_COUNT + MDPoolStrings, 1 << ProfilingFlags_MetaData, 1 << ProfilingFlags_MetaData, reinterpret_cast<mdToken *>(&hotItemList[0]), hotItemCount ); + + for ( ULONG i=0; i<hotItemCount; ++i ) + { + // convert tokens to rids + ULONG ulStringOffset = RidFromToken(hotItemList[i]); + + if (ulStringOffset >= poolSize) + ThrowHR(E_UNEXPECTED); + + pMarks[ulStringOffset] = ReorderData::ProfileData; + } + } + } +#endif // FEATURE_PREJIT +} // CMiniMdRW::MarkHotStrings + +//******************************************************************************* +// Function to mark hot strings referenced by hot tables based on token information in profile data +//******************************************************************************* +VOID CMiniMdRW::MarkStringsInHotTables(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize) +{ +#ifdef FEATURE_PREJIT + ULONG ixTbl=0, ixCol=0; + ULONG hotItemCount=0; + RID hotRID=0; + BYTE *pHotRow=NULL; + + if(pProfileData != NULL) + { + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + NewArrayHolder<mdToken> hotItemList = NULL; + // obtain the number of tokens in this table whose metadata was touched + hotItemCount = pProfileData->GetHotTokens(ixTbl, 1<<ProfilingFlags_MetaData, 1<<ProfilingFlags_MetaData, NULL, 0); + + // obtain the tokens whose metadata was touched + if(hotItemCount > 0) + { + hotItemList = new mdToken[hotItemCount]; + pProfileData->GetHotTokens(ixTbl, 1<<ProfilingFlags_MetaData, 1<<ProfilingFlags_MetaData, hotItemList, hotItemCount); + } + + // for every column in this hot row + for (ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ++ixCol) + { + // add the string to the string pool only if it hasn't been added yet + if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING) + { + // for every hot token in the list + for(ULONG item=0; item<hotItemCount; item++) + { + // get the rid from the token + hotRID = RidFromToken(hotItemList[item]); + IfFailThrow(m_Tables[ixTbl].GetRecord(hotRID, &pHotRow)); + _ASSERTE(pHotRow != NULL); + + // get column for string; this will get me the current string offset + ULONG ulStringOffset = GetCol(ixTbl, ixCol, pHotRow); + + if (ulStringOffset >= poolSize) + ThrowHR(E_UNEXPECTED); + + pMarks[ulStringOffset] = ReorderData::ProfileData; + } + } + } + } + } +#endif // FEATURE_PREJIT +} // CMiniMdRW::MarkStringsInHotTables + +//***************************************************************************** +// Function to mark strings referenced by the different metadata tables +//***************************************************************************** +VOID CMiniMdRW::MarkStringsInTables(BYTE * pMarks, ULONG poolSize) +{ +#ifdef FEATURE_PREJIT + for (ULONG ixTbl=0; ixTbl<m_TblCount; ixTbl++) + { + // for every row in the table + for (RID ridOld=1; ridOld<=m_Schema.m_cRecs[ixTbl]; ridOld++) + { + // lets assume we do not have any references to the stringpool + BOOL fHasStringData = FALSE; + + // for every column in this row + for (ULONG ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ixCol++) + { + // proceed only when the column type is iSTRING + if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING) + { + fHasStringData = TRUE; + // get the current record + BYTE *pOldRow; + IfFailThrow(m_Tables[ixTbl].GetRecord(ridOld, &pOldRow)); + + // get column for string; this will get me the current string offset + ULONG ulStringOffset = GetCol(ixTbl, ixCol, pOldRow); + + // ignore empty strings, they are not moving anywhere + if(ulStringOffset == 0) + continue; + + if (ulStringOffset >= poolSize) + ThrowHR(E_UNEXPECTED); + + BYTE ulBucketType=0; + + switch(ixTbl) + { + case TBL_Method: + ulBucketType = IsMdPublic(GetCol(TBL_Method, MethodRec::COL_Flags, pOldRow)) + ? ReorderData::PublicData + : ReorderData::NonPublicData; + break; + case TBL_Field: + ulBucketType = IsFdPublic(GetCol(TBL_Field, FieldRec::COL_Flags, pOldRow)) + ? ReorderData::PublicData + : ReorderData::NonPublicData; + break; + case TBL_TypeDef: + ulBucketType = IsTdPublic(GetCol(TBL_TypeDef, TypeDefRec::COL_Flags, pOldRow)) + ? ReorderData::PublicData + : ReorderData::NonPublicData; + break; + case TBL_ManifestResource: + ulBucketType = IsMrPublic(GetCol(TBL_ManifestResource, ManifestResourceRec::COL_Flags, pOldRow)) + ? ReorderData::PublicData + : ReorderData::NonPublicData; + break; + default: + ulBucketType = ReorderData::OtherData; + break; + } + + if (pMarks[ulStringOffset] == ReorderData::Undefined || pMarks[ulStringOffset] > ulBucketType) + pMarks[ulStringOffset] = ulBucketType; + } + } + if (!fHasStringData) + break; + } + } +#endif // FEATURE_PREJIT +} // CMiniMdRW::MarkStringsInTables + +// -------------------------------------------------------------------------------------- +// +// Function to mark duplicate strings in the mark array. This step is basically to take care of +// strings that have the same tail. +// Throws on error. +// +VOID CMiniMdRW::MarkDuplicateStrings(BYTE * pMarks, ULONG poolSize) +{ +#ifdef FEATURE_PREJIT + ULONG offset=1; + while (offset<poolSize) + { + if (pMarks[offset] == ReorderData::Undefined) + { + offset++; + continue; + } + + LPCSTR pszString; + IfFailThrow(m_StringHeap.GetString(offset, &pszString)); + + ULONG start = offset; + ULONG end = offset + (ULONG)strlen(pszString); + + BYTE tag = pMarks[offset]; + offset++; + + while (offset <= end) + { + if (pMarks[offset] != ReorderData::Undefined) + { + tag = min(pMarks[offset], tag); + pMarks[offset] = ReorderData::Duplicate; + } + offset++; + } + pMarks[start] = tag; + } +#endif // FEATURE_PREJIT +} // CMiniMdRW::MarkDuplicateStrings + +//***************************************************************************** +// Function to update the tables with the modified string offsets +//***************************************************************************** +VOID CMiniMdRW::FixStringsInTables() +{ +#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE) + for (ULONG ixTbl=0; ixTbl<m_TblCount; ixTbl++) + { + // for every row in the table + for (RID ridOld=1; ridOld<=m_Schema.m_cRecs[ixTbl]; ridOld++) + { + // lets assume we do not have any references to the stringpool + BOOL fHasStringData = FALSE; + + // for every column in this row + for (ULONG ixCol=0; ixCol<m_TableDefs[ixTbl].m_cCols; ixCol++) + { + // proceed only when the column type is iSTRING + if(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING) + { + fHasStringData = TRUE; + // get the current record + BYTE *pOldRow; + IfFailThrow(m_Tables[ixTbl].GetRecord(ridOld, &pOldRow)); + _ASSERTE(pOldRow != NULL); + + // get column for string; this will get me the current string offset + UINT32 nOldStringOffset = GetCol(ixTbl, ixCol, pOldRow); + + // ignore empty strings, they are not moving anywhere + if (nOldStringOffset == 0) + continue; + + UINT32 nNewStringOffset; + if (!m_StringPoolOffsetHash.Lookup(nOldStringOffset, &nNewStringOffset)) + ThrowHR(E_UNEXPECTED); + + IfFailThrow(PutCol(ixTbl, ixCol, pOldRow, nNewStringOffset)); + } + } + if (!fHasStringData) + break; + } + } +#endif // FEATURE_PREJIT +} // CMiniMdRW::FixStringsInTables + +// -------------------------------------------------------------------------------------- +// +// Function to fill the given string pool with strings from the existing string pool using the mark array. +// Throws on error. +// +VOID +CMiniMdRW::CreateReorderedStringPool( + MetaData::StringHeapRW *pStringHeap, + BYTE *pMarks, + ULONG cbHeapSize, + CorProfileData *pProfileData) +{ +#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE) + ULONG lastOldOffset = 0; + ULONG lastNewOffset = 0; + + // special handling of profile data so as to maintain the same order + // as the hot tokens in the CorProfileData object + if (pProfileData != NULL) + { + ULONG hotItems = pProfileData->GetHotTokens( + TBL_COUNT + MDPoolStrings, + 1 << ProfilingFlags_MetaData, + 1 << ProfilingFlags_MetaData, + NULL, + 0); + if ( hotItems ) + { + NewArrayHolder< ULONG > hotItemArr = new ULONG[ hotItems ]; + pProfileData->GetHotTokens( + TBL_COUNT + MDPoolStrings, + 1 << ProfilingFlags_MetaData, + 1 << ProfilingFlags_MetaData, + reinterpret_cast<mdToken *>(&hotItemArr[0]), + hotItems); + + // convert tokens to rids + for ( ULONG i = 0; i < hotItems ; ++i ) + { + UINT32 newOffset=0, start=0, end=0; + hotItemArr[i] = RidFromToken(hotItemArr[i]); + + for (UINT32 offset = hotItemArr[i]; offset >= 1; offset--) + { + if(pMarks[offset] == ReorderData::ProfileData) + { + LPCSTR szString; + IfFailThrow(m_StringHeap.GetString(offset, &szString)); + IfFailThrow(pStringHeap->AddString(szString, &newOffset)); + start = offset; + end = start + (UINT32)strlen(szString); + break; + } + } + + for (UINT32 offset = start; offset <end; offset++) + { + if(pMarks[offset] == ReorderData::ProfileData || pMarks[offset] == ReorderData::Duplicate) + { + m_StringPoolOffsetHash.Add(offset, newOffset); + } + newOffset++; + } + } + } + } + + for (BYTE priority = ReorderData::ProfileData; priority <= ReorderData::NonPublicData; priority++) + { + for (UINT32 offset = 1; offset < cbHeapSize; offset++) + { + // Since MinReorderBucketType is 0 and MaxReorderBucketType is 255, checking an unsigned BYTE against that gives a "comparison + // is always true" warning. Logically, the assert is: + // _ASSERTE(pMarks[offset] >= ReorderData::MinReorderBucketType && pMarks[offset] <= ReorderData::MaxReorderBucketType); + _ASSERTE(0 == ReorderData::MinReorderBucketType); + _ASSERTE(255 == ReorderData::MaxReorderBucketType); + _ASSERTE(sizeof(pMarks[0]) == 1); + + if (pMarks[offset] == priority) + { + UINT32 newOffset; + + if(!m_StringPoolOffsetHash.Lookup(offset, &newOffset)) + { + LPCSTR szString; + IfFailThrow(m_StringHeap.GetString(offset, &szString)); + IfFailThrow(pStringHeap->AddString(szString, &newOffset)); + m_StringPoolOffsetHash.Add(offset, newOffset); + + lastOldOffset = offset; + lastNewOffset = newOffset; + } + } + else + if (pMarks[offset] == ReorderData::Duplicate) + { + UINT32 newOffset; + if (lastNewOffset != 0 && !m_StringPoolOffsetHash.Lookup(offset, &newOffset)) + m_StringPoolOffsetHash.Add(offset, lastNewOffset + (offset - lastOldOffset)); + } + else + if (pMarks[offset] != ReorderData::Undefined) + { + lastNewOffset = 0; + } + } + } +#endif // FEATURE_PREJIT +} // CMiniMdRW::CreateReorderedStringPool + +// -------------------------------------------------------------------------------------- +// +// Function to reorganize the string pool based on IBC profile data (if available) and static analysis +// Throws on error. +// +VOID CMiniMdRW::OrganizeStringPool(CorProfileData *pProfileData) +{ +#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE) + if(!IsSafeToReorderStringPool()) + { + return; + } + + UINT32 cbStringHeapSize = m_StringHeap.GetUnalignedSize(); + + NewArrayHolder<BYTE> stringMarks = new BYTE[cbStringHeapSize]; + ZeroMemory(stringMarks, cbStringHeapSize); + + // Each string will be assigned a value based on its hotness in the Mark*() functions + // This list will be later traversed to place the strings in the right order in the string pool and also + // to update the references in the metadata tables + + // Mark all hot strings + MarkHotStrings(pProfileData, stringMarks, cbStringHeapSize); + + // Mark all strings in hot rows + MarkStringsInHotTables(pProfileData, stringMarks, cbStringHeapSize); + + // Mark all remaining strings + MarkStringsInTables(stringMarks, cbStringHeapSize); + + // Mark duplicates for interned strings + MarkDuplicateStrings(stringMarks, cbStringHeapSize); + + // Initalize the temporary string heap + MetaData::StringHeapRW tempStringHeap; + + IfFailThrow(tempStringHeap.InitializeEmpty( + cbStringHeapSize + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + + // We will use this hash for fixing the string references in the profile data + m_StringPoolOffsetHash.Reallocate(cbStringHeapSize); + + // Create the temporary string pool using the mark arrays + CreateReorderedStringPool(&tempStringHeap, stringMarks, cbStringHeapSize, pProfileData); + + // Update the tables with string offsets into the temporary string pool + FixStringsInTables(); + + // Replace the existing string pool with the modified version + m_StringHeap.Delete(); + IfFailThrow(m_StringHeap.InitializeFromStringHeap( + &tempStringHeap, + TRUE)); // fCopyData +#endif // FEATURE_PREJIT +} // CMiniMdRW::OrganizeStringPool + +#ifdef FEATURE_PREJIT + +// write hot data of the pools +// +__checkReturn +HRESULT +CMiniMdRW::SaveHotPoolsToStream( + IStream *pStream, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData, + UINT32 *pnPoolDirSize, + UINT32 *pnHeapsSavedSize) +{ + HRESULT hr = S_OK; + UINT32 rgHeapSavedSize[MDPoolCount] = { 0, 0, 0, 0 }; + + // save pools in the order they are described in MDPools enum + // + // we skip the hot string pool when we reorganize the string pool + if (!(reorderingOptions & ReArrangeStringPool)) + { + MetaData::HotHeapWriter stringHotHeapWriter(&m_StringHeap); + IfFailRet(SaveHotPoolToStream( + pStream, + pProfileData, + &stringHotHeapWriter, + &rgHeapSavedSize[MDPoolStrings])); + } + + // Save guid heap hot data + MetaData::HotHeapWriter guidsHotHeapWriter(&m_GuidHeap); + IfFailRet(SaveHotPoolToStream( + pStream, + pProfileData, + &guidsHotHeapWriter, + &rgHeapSavedSize[MDPoolGuids])); + + // Save blob heap hot data + MetaData::HotHeapWriter blobsHotHeapWriter( + &m_BlobHeap, + FALSE); // fUserStringHeap + IfFailRet(SaveHotPoolToStream( + pStream, + pProfileData, + &blobsHotHeapWriter, + &rgHeapSavedSize[MDPoolBlobs])); + + // Save user string heap hot data + MetaData::HotHeapWriter userStringsHotHeapWriter( + &m_UserStringHeap, + TRUE); // fUserStringHeap + IfFailRet(SaveHotPoolToStream( + pStream, + pProfileData, + &userStringsHotHeapWriter, + &rgHeapSavedSize[MDPoolUSBlobs])); + + // fix pool offsets, they need to point to the header of each saved pool + UINT32 nHeapEndOffset = 0; + for (int i = MDPoolCount; i-- > 0; ) + { + if (rgHeapSavedSize[i] != 0) + { + UINT32 nHeapSavedSize = rgHeapSavedSize[i]; + // Change size of the heap to the (negative) offset of its header + rgHeapSavedSize[i] = sizeof(struct MetaData::HotHeapHeader) + nHeapEndOffset; + nHeapEndOffset += nHeapSavedSize; + } + } + // Store size of all heaps + *pnHeapsSavedSize = nHeapEndOffset; + + // save hot pool dirs + *pnPoolDirSize = 0; + for (int i = 0; i < MDPoolCount; i++) + { + if (rgHeapSavedSize[i] != 0) + { + IfFailRet(StreamUtil::WriteToStream(pStream, i, pnPoolDirSize)); + IfFailRet(StreamUtil::WriteToStream(pStream, (ULONG)rgHeapSavedSize[i], pnPoolDirSize)); + } + } + + return S_OK; +} // CMiniMdRW::SaveHotPoolsToStream + +// write hot data of specific blob +// +__checkReturn +HRESULT +CMiniMdRW::SaveHotPoolToStream( + IStream *pStream, + CorProfileData *pProfileData, + MetaData::HotHeapWriter *pHotHeapWriter, + UINT32 *pnSavedSize) +{ + + _ASSERTE(pProfileData != NULL); + + HRESULT hr = S_OK; + // #CallToGetHotTokens + // see code:CMiniMdRW.SaveFullTablesToStream#WritingHotMetaData for the main caller of this. + if (pProfileData->GetHotTokens( + pHotHeapWriter->GetTableIndex(), + 1 << ProfilingFlags_MetaData, + 1 << ProfilingFlags_MetaData, + NULL, + 0) != 0) + { + IfFailRet(pHotHeapWriter->SaveToStream( + pStream, + pProfileData, + pnSavedSize)); + } + else + { + *pnSavedSize = 0; + } + + return S_OK; +} // CMiniMdRW::SaveHotPoolToStream + +#endif //FEATURE_PREJIT + +//***************************************************************************** +// Save the tables to the stream. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SaveENCTablesToStream( + IStream *pIStream) +{ + HRESULT hr; + BYTE SchemaBuf[sizeof(CMiniMdSchema)]; //Buffer for compressed schema. + ULONG cbAlign; // Bytes needed for alignment. + ULONG cbTable; // Bytes in a table. + ULONG cbTotal; // Bytes written. + ULONG ixTbl; // Table counter. + static const unsigned char zeros[8] = {0}; // For padding and alignment. + + // Make sure the minimal delta has a fully expanded table + IfFailRet(ExpandTables()); + + // Write the header. + CMiniMdSchema Schema = m_Schema; + Schema.m_heaps |= CMiniMdSchema::DELTA_ONLY; + + if (m_rENCRecs != NULL) + { + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + Schema.m_cRecs[ixTbl] = m_rENCRecs[ixTbl].Count(); + } + else + { + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + Schema.m_cRecs[ixTbl] = 0; + } + + Schema.m_cRecs[TBL_Module] = m_Schema.m_cRecs[TBL_Module]; + Schema.m_cRecs[TBL_ENCLog] = m_Schema.m_cRecs[TBL_ENCLog]; + Schema.m_cRecs[TBL_ENCMap] = m_Schema.m_cRecs[TBL_ENCMap]; + + cbTotal = Schema.SaveTo(SchemaBuf); + IfFailGo(pIStream->Write(SchemaBuf, cbTotal, 0)); + if ( (cbAlign = Align4(cbTotal) - cbTotal) != 0) + IfFailGo(pIStream->Write(&hr, cbAlign, 0)); + cbTotal += cbAlign; + + // For each table... + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + if (ixTbl == TBL_ENCLog || ixTbl == TBL_ENCMap || ixTbl == TBL_Module) + { + if (m_Schema.m_cRecs[ixTbl] == 0) + continue; // pretty strange if ENC has no enc data. + // Persist the ENC table. + IfFailGo(m_Tables[ixTbl].GetRecordsDataSize((UINT32 *)&cbTable)); + _ASSERTE(cbTable == m_TableDefs[ixTbl].m_cbRec * m_Schema.m_cRecs[ixTbl]); + cbTotal += cbTable; + IfFailGo(m_Tables[ixTbl].SaveToStream( + pIStream)); + } + else + if (Schema.m_cRecs[ixTbl]) + { + // Copy just the delta records. + + // Create the temporary table. + MetaData::TableRW tempTable; + IfFailGo(tempTable.InitializeEmpty_WithRecordCount( + m_TableDefs[ixTbl].m_cbRec, + Schema.m_cRecs[ixTbl] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + INDEBUG_MD(tempTable.Debug_SetTableInfo(NULL, ixTbl)); + + // For each row in the data. + RID rid; + for (ULONG iDelta=0; iDelta<Schema.m_cRecs[ixTbl]; ++iDelta) + { + RID ridNew; + rid = m_rENCRecs[ixTbl][iDelta]; + BYTE *pRow; + IfFailGo(m_Tables[ixTbl].GetRecord(rid, &pRow)); + BYTE *pNew; + IfFailGo(tempTable.AddRecord(&pNew, (UINT32 *)&ridNew)); + _ASSERTE(iDelta+1 == ridNew); + + memcpy(pNew, pRow, m_TableDefs[ixTbl].m_cbRec); + } + // Persist the temp table to the stream. + IfFailGo(tempTable.GetRecordsDataSize((UINT32 *)&cbTable)); + _ASSERTE(cbTable == m_TableDefs[ixTbl].m_cbRec * Schema.m_cRecs[ixTbl]); + cbTotal += cbTable; + IfFailGo(tempTable.SaveToStream( + pIStream)); + } + } + + // Pad with at least 2 bytes and align on 4 bytes. + cbAlign = Align4(cbTotal) - cbTotal; + if (cbAlign < 2) + cbAlign += 4; + IfFailGo(pIStream->Write(zeros, cbAlign, 0)); + cbTotal += cbAlign; + _ASSERTE(m_cbSaveSize == 0 || m_cbSaveSize == cbTotal); + +ErrExit: + return hr; +} // CMiniMdRW::SaveENCTablesToStream + +//***************************************************************************** +// Save the tables to the stream. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SaveTablesToStream( + IStream *pIStream, // The stream. + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr; + + // Prepare the data for save. + IfFailGo(PreSave()); + + switch (m_OptionValue.m_UpdateMode & MDUpdateMask) + { + case MDUpdateFull: + case MDUpdateIncremental: + case MDUpdateExtension: + case MDUpdateENC: + hr = SaveFullTablesToStream(pIStream, reorderingOptions, pProfileData); + break; + case MDUpdateDelta: + hr = SaveENCTablesToStream(pIStream); + break; + default: + _ASSERTE(!"Internal error -- unknown save mode"); + return E_INVALIDARG; + } + +ErrExit: + return hr; +} // CMiniMdRW::SaveTablesToStream + +//***************************************************************************** +// Save a full pool to the stream. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SaveFullPoolToStream( + int iPool, // The pool. + IStream *pStream) // The stream. +{ + HRESULT hr; + + switch (iPool) + { + case MDPoolStrings: + hr = m_StringHeap.SaveToStream_Aligned( + 0, // Start offset of the data to be stored + pStream); + break; + case MDPoolGuids: + hr = m_GuidHeap.SaveToStream( + pStream); + break; + case MDPoolBlobs: + hr = m_BlobHeap.SaveToStream_Aligned( + 0, // Start offset of the data to be stored + pStream); + break; + case MDPoolUSBlobs: + hr = m_UserStringHeap.SaveToStream_Aligned( + 0, // Start offset of the data to be stored + pStream); + break; + default: + hr = E_INVALIDARG; + } + + return hr; +} // CMiniMdRW::SaveFullPoolToStream + +//***************************************************************************** +// Save a ENC pool to the stream. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SaveENCPoolToStream( + int iPool, // The pool. + IStream *pIStream) // The stream. +{ + HRESULT hr; + + switch (iPool) + { + case MDPoolStrings: + { + UINT32 nEnCDeltaStartOffset = m_StringHeap.GetEnCSessionStartHeapSize(); + hr = m_StringHeap.SaveToStream_Aligned( + nEnCDeltaStartOffset, // Start offset of the data to be stored + pIStream); + break; + } + case MDPoolGuids: + { + // Save full Guid heap (we never save EnC delta) + hr = m_GuidHeap.SaveToStream( + pIStream); + break; + } + case MDPoolBlobs: + { + UINT32 nEnCDeltaStartOffset = m_BlobHeap.GetEnCSessionStartHeapSize(); + hr = m_BlobHeap.SaveToStream_Aligned( + nEnCDeltaStartOffset, // Start offset of the data to be stored + pIStream); + break; + } + case MDPoolUSBlobs: + { + UINT32 nEnCDeltaStartOffset = m_UserStringHeap.GetEnCSessionStartHeapSize(); + hr = m_UserStringHeap.SaveToStream_Aligned( + nEnCDeltaStartOffset, // Start offset of the data to be stored + pIStream); + break; + } + default: + hr = E_INVALIDARG; + } + + return hr; +} // CMiniMdRW::SaveENCPoolToStream + +//***************************************************************************** +// Save a pool to the stream. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SavePoolToStream( + int iPool, // The pool. + IStream *pIStream) // The stream. +{ + HRESULT hr; + switch (m_OptionValue.m_UpdateMode & MDUpdateMask) + { + case MDUpdateFull: + case MDUpdateIncremental: + case MDUpdateExtension: + case MDUpdateENC: + hr = SaveFullPoolToStream(iPool, pIStream); + break; + case MDUpdateDelta: + hr = SaveENCPoolToStream(iPool, pIStream); + break; + default: + _ASSERTE(!"Internal error -- unknown save mode"); + return E_INVALIDARG; + } + + return hr; +} // CMiniMdRW::SavePoolToStream + +//***************************************************************************** +// Expand a table from the initial (hopeful) 2-byte column sizes to the large +// (but always adequate) 4-byte column sizes. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ExpandTables() +{ + HRESULT hr = S_OK; + CMiniMdSchema Schema; // Temp schema by which to build tables. + ULONG ixTbl; // Table counter. + + // Allow function to be called many times. + if (m_eGrow == eg_grown) + return (S_OK); + + // OutputDebugStringA("Growing tables to large size.\n"); + + // Make pool indices the large size. + Schema.m_heaps = 0; + Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4; + Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4; + Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4; + + // Make Row counts the large size. + memset(Schema.m_cRecs, 0, sizeof(Schema.m_cRecs)); + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + Schema.m_cRecs[ixTbl] = USHRT_MAX+1; + + // Compute how many bits required to hold a rid. + Schema.m_rid = 16; + + for (ixTbl=0; ixTbl<m_TblCount; ++ixTbl) + { + IfFailGo(ExpandTableColumns(Schema, ixTbl)); + } + + // Things are bigger now. + m_Schema.m_rid = 16; + m_Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4; + m_Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4; + m_Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4; + m_iStringsMask = 0xffffffff; + m_iGuidsMask = 0xffffffff; + m_iBlobsMask = 0xffffffff; + + // Remember that we've grown. + m_eGrow = eg_grown; + m_maxRid = m_maxIx = UINT32_MAX; + +ErrExit: + return hr; +} // CMiniMdRW::ExpandTables + + +__checkReturn +HRESULT +CMiniMdRW::InitWithLargeTables() +{ + CMiniMdSchema Schema; // Temp schema by which to build tables. + HRESULT hr = S_OK; + + // Make pool indices the large size. + Schema.m_heaps = 0; + Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4; + Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4; + Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4; + + // Make Row counts the large size. + memset(Schema.m_cRecs, 0, sizeof(Schema.m_cRecs)); + for (int ixTbl=0; ixTbl<(int)m_TblCount; ++ixTbl) + Schema.m_cRecs[ixTbl] = USHRT_MAX+1; + + // Compute how many bits required to hold a rid. + Schema.m_rid = 16; + + // For each table... + for (int ixTbl=0; ixTbl<(int)m_TblCount; ++ixTbl) + { + IfFailRet(InitColsForTable(Schema, ixTbl, &m_TableDefs[ixTbl], 0, TRUE)); + } + + + // Things are bigger now. + m_Schema.m_rid = 16; + m_Schema.m_heaps |= CMiniMdSchema::HEAP_STRING_4; + m_Schema.m_heaps |= CMiniMdSchema::HEAP_GUID_4; + m_Schema.m_heaps |= CMiniMdSchema::HEAP_BLOB_4; + m_iStringsMask = 0xffffffff; + m_iGuidsMask = 0xffffffff; + + return hr; +}// CMiniMdRW::InitWithLargeTables + +//***************************************************************************** +// Expand the sizes of a tables columns according to a new schema. When this +// happens, all RID and Pool index columns expand from 2 to 4 bytes. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ExpandTableColumns( + CMiniMdSchema &Schema, + ULONG ixTbl) +{ + HRESULT hr; + CMiniTableDef sTempTable; // Definition for a temporary table. + CQuickBytes qbTempCols; + ULONG ixCol; // Column counter. + ULONG cbFixed; // Count of bytes that don't move. + CMiniColDef *pFromCols; // Definitions of "from" columns. + CMiniColDef *pToCols; // Definitions of "To" columns. + ULONG cMoveCols; // Count of columns to move. + ULONG cFixedCols; // Count of columns to move. + + // Allocate a def for the temporary table. + sTempTable = m_TableDefs[ixTbl]; + IfFailGo(qbTempCols.ReSizeNoThrow(sTempTable.m_cCols * sizeof(CMiniColDef) + 1)); + // Mark the array of columns as not allocated (not ALLOCATED_MEMORY_MARKER) for SetNewColumnDefinition + // call bellow (code:#SetNewColumnDefinition_call) + *(BYTE *)(qbTempCols.Ptr()) = 0; + sTempTable.m_pColDefs = (CMiniColDef *)((BYTE *)(qbTempCols.Ptr()) + 1); + + // Initialize temp table col defs based on counts of data in the tables. + IfFailGo(InitColsForTable(Schema, ixTbl, &sTempTable, 1, FALSE)); + + if (GetCountRecs(ixTbl) > 0) + { + // Analyze the column definitions to determine the unchanged vs changed parts. + cbFixed = 0; + for (ixCol = 0; ixCol < sTempTable.m_cCols; ++ixCol) + { + if (sTempTable.m_pColDefs[ixCol].m_oColumn != m_TableDefs[ixTbl].m_pColDefs[ixCol].m_oColumn || + sTempTable.m_pColDefs[ixCol].m_cbColumn != m_TableDefs[ixTbl].m_pColDefs[ixCol].m_cbColumn) + break; + cbFixed += sTempTable.m_pColDefs[ixCol].m_cbColumn; + } + if (ixCol == sTempTable.m_cCols) + { + // no column is changing. We are done. + goto ErrExit; + } + cFixedCols = ixCol; + pFromCols = &m_TableDefs[ixTbl].m_pColDefs[ixCol]; + pToCols = &sTempTable.m_pColDefs[ixCol]; + cMoveCols = sTempTable.m_cCols - ixCol; + for (; ixCol < sTempTable.m_cCols; ++ixCol) + { + _ASSERTE(sTempTable.m_pColDefs[ixCol].m_cbColumn == 4); + } + + // Create the temporary table. + MetaData::TableRW tempTable; + IfFailGo(tempTable.InitializeEmpty_WithRecordCount( + sTempTable.m_cbRec, + m_Schema.m_cRecs[ixTbl] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + INDEBUG_MD(tempTable.Debug_SetTableInfo(NULL, ixTbl)); + + // For each row in the data. + RID rid; // Row iterator. + + for (rid = 1; rid <= m_Schema.m_cRecs[ixTbl]; ++rid) + { + RID ridNew; + BYTE *pFrom; + BYTE *pTo; + + IfFailGo(m_Tables[ixTbl].GetRecord(rid, &pFrom)); + IfFailGo(tempTable.AddRecord(&pTo, (UINT32 *)&ridNew)); + _ASSERTE(rid == ridNew); + + // Move the fixed part. + memcpy(pTo, pFrom, cbFixed); + + // Expand the expanded parts. + for (ixCol = 0; ixCol < cMoveCols; ++ixCol) + { + if (m_TableDefs[ixTbl].m_pColDefs[cFixedCols + ixCol].m_cbColumn == sizeof(USHORT)) + { + // The places that access expect the int16 to be in the high bytes so we need to the extra swap + SET_UNALIGNED_VAL32((pTo + pToCols[ixCol].m_oColumn), VAL16(*(USHORT*)(pFrom + pFromCols[ixCol].m_oColumn))); + } + else + { + // In this case we're just copying the data over + memcpy(pTo + pToCols[ixCol].m_oColumn, pFrom + pFromCols[ixCol].m_oColumn, sizeof(ULONG)); + } + } + } + + // Keep the expanded table. + m_Tables[ixTbl].Delete(); + IfFailGo(m_Tables[ixTbl].InitializeFromTable( + &tempTable, + TRUE)); // fCopyData + INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl)); + } + else + { // No data, so just reinitialize. + m_Tables[ixTbl].Delete(); + IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount( + sTempTable.m_cbRec, + g_TblSizeInfo[0][ixTbl] + COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite + INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl)); + } + + //#SetNewColumnDefinition_call + // Keep the new column defs. + IfFailGo(SetNewColumnDefinition(&(m_TableDefs[ixTbl]), sTempTable.m_pColDefs, ixTbl)); + m_TableDefs[ixTbl].m_cbRec = sTempTable.m_cbRec; + +ErrExit: + return hr; +} // CMiniMdRW::ExpandTableColumns + + +//***************************************************************************** +// Used by caller to let us know save is completed. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::SaveDone() +{ + return PostSave(); +} // CMiniMdRW::SaveDone + +//***************************************************************************** +// General post-token-move table fixup. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FixUpTable( + ULONG ixTbl) // Index of table to fix. +{ + HRESULT hr = S_OK; + ULONG i, j; // Loop control. + ULONG cRows; // Count of rows in table. + void *pRec; // Pointer to row data. + mdToken tk; // A token. + ULONG rCols[16]; // List of columns with token data. + ULONG cCols; // Count of columns with token data. + + // If no remaps, nothing to do. + if (GetTokenMovementMap() == NULL) + return S_OK; + + // Find the columns with token data. + cCols = 0; + _ASSERTE(m_TableDefs[ixTbl].m_cCols <= 16); + for (i=0; i<m_TableDefs[ixTbl].m_cCols; ++i) + { + if (m_TableDefs[ixTbl].m_pColDefs[i].m_Type <= iCodedTokenMax) + rCols[cCols++] = i; + } + _ASSERTE(cCols); + if (cCols == 0) + return S_OK; + + cRows = m_Schema.m_cRecs[ixTbl]; + + // loop through all Rows + for (i = 1; i<=cRows; ++i) + { + IfFailGo(getRow(ixTbl, i, &pRec)); + for (j=0; j<cCols; ++j) + { + tk = GetToken(ixTbl, rCols[j], pRec); + tk = GetTokenMovementMap()->SafeRemap(tk); + IfFailGo(PutToken(ixTbl, rCols[j], pRec, tk)); + } + } + +ErrExit: + return hr; +} // CMiniMdRW::FixUpTable + + +//***************************************************************************** +// Fixup all the embedded ref to corresponding def before we remap tokens movement. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FixUpRefToDef() +{ + return NOERROR; +} // CMiniMdRW::FixUpRefToDef + +//***************************************************************************** +// Given a table with a pointer (index) to a sequence of rows in another +// table, get the RID of the end row. This is the STL-ish end; the first row +// not in the list. Thus, for a list of 0 elements, the start and end will +// be the same. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::Impl_GetEndRidForColumn( // The End rid. + UINT32 nTableIndex, + RID nRowIndex, + CMiniColDef &def, // Column containing the RID into other table. + UINT32 nTargetTableIndex, // The other table. + RID *pEndRid) +{ + HRESULT hr; + ULONG ixEnd; + void *pRow; + + // Last rid in range from NEXT record, or count of table, if last record. + _ASSERTE(nRowIndex <= m_Schema.m_cRecs[nTableIndex]); + if (nRowIndex < m_Schema.m_cRecs[nTableIndex]) + { + IfFailRet(getRow(nTableIndex, nRowIndex + 1, &pRow)); + ixEnd = getIX(pRow, def); + // We use a special value, 'END_OF_TABLE' (currently 0), to indicate + // end-of-table. If we find the special value we'll have to compute + // the value to return. If we don't find the special value, then + // the value is correct. + if (ixEnd != END_OF_TABLE) + { + *pEndRid = ixEnd; + return S_OK; + } + } + + // Either the child pointer value in the next row was END_OF_TABLE, or + // the row is the last row of the table. In either case, we must return + // a value which will work out to the END of the child table. That + // value depends on the value in the row itself -- if the row contains + // END_OF_TABLE, there are no children, and to make the subtraction + // work out, we return END_OF_TABLE for the END value. If the row + // contains some value, then we return the actual END count. + IfFailRet(getRow(nTableIndex, nRowIndex, &pRow)); + if (getIX(pRow, def) == END_OF_TABLE) + { + ixEnd = END_OF_TABLE; + } + else + { + ixEnd = m_Schema.m_cRecs[nTargetTableIndex] + 1; + } + + *pEndRid = ixEnd; + return S_OK; +} // CMiniMd::Impl_GetEndRidForColumn + +//***************************************************************************** +// Add a row to any table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddRecord( // S_OK or error. + UINT32 nTableIndex, // The table to expand. + void **ppRow, + RID *pRid) // Put RID here. +{ + HRESULT hr; + + _ASSERTE(nTableIndex < m_TblCount); + _ASSERTE(!m_bPreSaveDone && "Cannot add records after PreSave and before Save."); + IfFailRet(m_Tables[nTableIndex].AddRecord( + reinterpret_cast<BYTE **>(ppRow), + reinterpret_cast<UINT32 *>(pRid))); + if (*pRid > m_maxRid) + { + m_maxRid = *pRid; + if (m_maxRid > m_limRid && m_eGrow == eg_ok) + { + // OutputDebugStringA("Growing tables due to Record overflow.\n"); + m_eGrow = eg_grow, m_maxRid = m_maxIx = UINT32_MAX; + } + } + ++m_Schema.m_cRecs[nTableIndex]; + SetSorted(nTableIndex, false); + if (m_pVS[nTableIndex] != NULL) + { + m_pVS[nTableIndex]->m_isMapValid = false; + } + + return S_OK; +} // CMiniMdRW::AddRecord + +//***************************************************************************** +// Add a row to the TypeDef table, and initialize the pointers to other tables. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddTypeDefRecord( + TypeDefRec **ppRow, + RID *pnRowIndex) +{ + HRESULT hr; + IfFailRet(AddRecord(TBL_TypeDef, (void **)ppRow, pnRowIndex)); + + IfFailRet(PutCol(TBL_TypeDef, TypeDefRec::COL_MethodList, *ppRow, NewRecordPointerEndValue(TBL_Method))); + IfFailRet(PutCol(TBL_TypeDef, TypeDefRec::COL_FieldList, *ppRow, NewRecordPointerEndValue(TBL_Field))); + + return S_OK; +} // CMiniMdRW::AddTypeDefRecord + +//***************************************************************************** +// Add a row to the Method table, and initialize the pointers to other tables. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddMethodRecord( + MethodRec **ppRow, + RID *pnRowIndex) +{ + HRESULT hr; + IfFailRet(AddRecord(TBL_Method, (void **)ppRow, pnRowIndex)); + + IfFailRet(PutCol(TBL_Method, MethodRec::COL_ParamList, *ppRow, NewRecordPointerEndValue(TBL_Param))); + + return S_OK; +} // CMiniMdRW::AddMethodRecord + +//***************************************************************************** +// Add a row to the EventMap table, and initialize the pointers to other tables. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddEventMapRecord( + EventMapRec **ppRow, + RID *pnRowIndex) +{ + HRESULT hr; + IfFailRet(AddRecord(TBL_EventMap, (void **)ppRow, pnRowIndex)); + + IfFailRet(PutCol(TBL_EventMap, EventMapRec::COL_EventList, *ppRow, NewRecordPointerEndValue(TBL_Event))); + + SetSorted(TBL_EventMap, false); + + return S_OK; +} // CMiniMdRW::AddEventMapRecord + +//********************************************************************************* +// Add a row to the PropertyMap table, and initialize the pointers to other tables. +//********************************************************************************* +__checkReturn +HRESULT +CMiniMdRW::AddPropertyMapRecord( + PropertyMapRec **ppRow, + RID *pnRowIndex) +{ + HRESULT hr; + IfFailRet(AddRecord(TBL_PropertyMap, (void **)ppRow, pnRowIndex)); + + IfFailRet(PutCol(TBL_PropertyMap, PropertyMapRec::COL_PropertyList, *ppRow, NewRecordPointerEndValue(TBL_Property))); + + SetSorted(TBL_PropertyMap, false); + + return S_OK; +} // CMiniMdRW::AddPropertyMapRecord + +//***************************************************************************** +// converting a ANSI heap string to unicode string to an output buffer +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::Impl_GetStringW( + ULONG ix, + __out_ecount (cchBuffer) LPWSTR szOut, + ULONG cchBuffer, + ULONG *pcchBuffer) +{ + LPCSTR szString; // Single byte version. + int iSize; // Size of resulting string, in wide chars. + HRESULT hr = NOERROR; + + IfFailGo(getString(ix, &szString)); + + if (*szString == 0) + { + // If emtpy string "", return pccBuffer 0 + if ( szOut && cchBuffer ) + szOut[0] = W('\0'); + if ( pcchBuffer ) + *pcchBuffer = 0; + goto ErrExit; + } + if (!(iSize=::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, szOut, cchBuffer))) + { + // What was the problem? + DWORD dwNT = GetLastError(); + + // Not truncation? + if (dwNT != ERROR_INSUFFICIENT_BUFFER) + IfFailGo(HRESULT_FROM_NT(dwNT)); + + // Truncation error; get the size required. + if (pcchBuffer) + *pcchBuffer = ::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, NULL, 0); + + if ((szOut != NULL) && (cchBuffer > 0)) + { // null-terminate the truncated output string + szOut[cchBuffer - 1] = W('\0'); + } + + hr = CLDB_S_TRUNCATION; + goto ErrExit; + } + if (pcchBuffer) + *pcchBuffer = iSize; + +ErrExit: + return hr; +} // CMiniMdRW::Impl_GetStringW + +//***************************************************************************** +// Get a column value from a row. Signed types are sign-extended to the full +// ULONG; unsigned types are 0-extended. +//***************************************************************************** +ULONG CMiniMdRW::GetCol( // Column data. + ULONG ixTbl, // Index of the table. + ULONG ixCol, // Index of the column. + void *pvRecord) // Record with the data. +{ + BYTE *pRecord; // The row. + BYTE *pData; // The item in the row. + ULONG val; // The return value. + // Valid Table, Column, Row? + _ASSERTE(ixTbl < m_TblCount); + _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + + // Column size, offset + CMiniColDef *pColDef = &m_TableDefs[ixTbl].m_pColDefs[ixCol]; + + pRecord = reinterpret_cast<BYTE*>(pvRecord); + pData = pRecord + pColDef->m_oColumn; + + switch (pColDef->m_cbColumn) + { + case 1: + val = *pData; + break; + case 2: + if (pColDef->m_Type == iSHORT) + val = static_cast<LONG>((INT16)GET_UNALIGNED_VAL16(pData)); + else + val = GET_UNALIGNED_VAL16(pData); + break; + case 4: + val = GET_UNALIGNED_VAL32(pData); + break; + default: + _ASSERTE(!"Unexpected column size"); + return 0; + } + + return val; +} // CMiniMdRW::GetCol + +//***************************************************************************** +// General token column fetcher. +//***************************************************************************** +mdToken CMiniMdRW::GetToken( + ULONG ixTbl, // Index of the table. + ULONG ixCol, // Index of the column. + void *pvRecord) // Record with the data. +{ + ULONG tkn; // Token from the table. + + // Valid Table, Column, Row? + _ASSERTE(ixTbl < m_TblCount); + _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + + // Column description. + CMiniColDef *pColDef = &m_TableDefs[ixTbl].m_pColDefs[ixCol]; + + // Is the column just a RID? + if (pColDef->m_Type <= iRidMax) + { + tkn = GetCol(ixTbl, ixCol, pvRecord); //pColDef, pvRecord, RidFromToken(tk)); + tkn = TokenFromRid(tkn, GetTokenForTable(pColDef->m_Type)); + } + else // Is it a coded token? + if (pColDef->m_Type <= iCodedTokenMax) + { + ULONG indexCodedToken = pColDef->m_Type - iCodedToken; + if (indexCodedToken < COUNTOF(g_CodedTokens)) + { + const CCodedTokenDef *pCdTkn = &g_CodedTokens[indexCodedToken]; + tkn = decodeToken(GetCol(ixTbl, ixCol, pvRecord), pCdTkn->m_pTokens, pCdTkn->m_cTokens); + } + else + { + _ASSERTE(!"GetToken called on unexpected coded token type"); + tkn = 0; + } + } + else // It is an error. + { + _ASSERTE(!"GetToken called on unexpected column type"); + tkn = 0; + } + + return tkn; +} // CMiniMdRW::GetToken + +//***************************************************************************** +// Put a column value into a row. The value is passed as a ULONG; 1, 2, or 4 +// bytes are stored into the column. No table is specified, and the coldef +// is passed directly. This allows putting data into other buffers, such as +// the temporary table used for saving. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PutCol( // S_OK or E_UNEXPECTED. + CMiniColDef ColDef, // The col def. + void *pvRecord, // The row. + ULONG uVal) // Value to put. +{ + HRESULT hr = S_OK; + BYTE *pRecord; // The row. + BYTE *pData; // The item in the row. + + pRecord = reinterpret_cast<BYTE*>(pvRecord); + pData = pRecord + ColDef.m_oColumn; + + switch (ColDef.m_cbColumn) + { + case 1: + // Don't store a value that would overflow. + if (uVal > UCHAR_MAX) + return E_INVALIDARG; + *pData = static_cast<BYTE>(uVal); + break; + case 2: + if (uVal > USHRT_MAX) + return E_INVALIDARG; + SET_UNALIGNED_VAL16(pData, uVal); + break; + case 4: + SET_UNALIGNED_VAL32(pData, uVal); + break; + default: + _ASSERTE(!"Unexpected column size"); + return E_UNEXPECTED; + } + + return hr; +} // CMiniMdRW::PutCol + +//***************************************************************************** +// Put a column value into a row. The value is passed as a ULONG; 1, 2, or 4 +// bytes are stored into the column. +//***************************************************************************** + +//***************************************************************************** +// Add a string to the string pool, and store the offset in the cell. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PutString( // S_OK or E_UNEXPECTED. + ULONG ixTbl, // The table. + ULONG ixCol, // The column. + void *pvRecord, // The row. + LPCSTR szString) // Value to put. +{ + _ASSERTE(szString != NULL); + + HRESULT hr = S_OK; + UINT32 nStringIndex = 0; + + // Valid Table, Column, Row? + _ASSERTE(ixTbl < m_TblCount); + _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + + // Column description. + _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING); + + // <TODO>@FUTURE: Set iOffset to 0 for empty string. Work around the bug in + // StringPool that does not handle empty strings correctly.</TODO> + if (szString[0] == 0) + { // It's empty string + nStringIndex = 0; + } + else + { // It's non-empty string + IfFailGo(m_StringHeap.AddString( + szString, + &nStringIndex)); + } + + hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nStringIndex); + + if (m_maxIx != UINT32_MAX) + { + IfFailGo(m_StringHeap.GetAlignedSize(&nStringIndex)); + } + if (nStringIndex > m_maxIx) + { + m_maxIx = nStringIndex; + if (m_maxIx > m_limIx && m_eGrow == eg_ok) + { + // OutputDebugStringA("Growing tables due to String overflow.\n"); + m_eGrow = eg_grow, m_maxRid = m_maxIx = UINT32_MAX; + } + } + +ErrExit: + return hr; +} // CMiniMdRW::PutString + +//***************************************************************************** +// Add a string to the string pool, and store the offset in the cell. +// Returns: S_OK or E_UNEXPECTED. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PutStringW( + ULONG ixTbl, // The table. + ULONG ixCol, // The column. + void *pvRecord, // The row. + LPCWSTR wszString) // Value to put. +{ + _ASSERTE(wszString != NULL); + + HRESULT hr = S_OK; + UINT32 nStringIndex = 0; // The new string. + + // Valid Table, Column, Row? + _ASSERTE(ixTbl < m_TblCount); + _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + + // Column description. + _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iSTRING); + + // Special case for empty string for StringPool + if (wszString[0] == 0) + { // It's empty string + // TODO: Is it OK that index 0 contains empty blob (00) and not empty string (00 01)? + nStringIndex = 0; + } + else + { // It's non-empty string + IfFailGo(m_StringHeap.AddStringW( + wszString, + &nStringIndex)); + } + + hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nStringIndex); + + if (m_maxIx != UINT32_MAX) + { + IfFailGo(m_StringHeap.GetAlignedSize(&nStringIndex)); + } + if (nStringIndex > m_maxIx) + { + m_maxIx = nStringIndex; + if (m_maxIx > m_limIx && m_eGrow == eg_ok) + { + // OutputDebugStringA("Growing tables due to String overflow.\n"); + m_eGrow = eg_grow, m_maxRid = m_maxIx = UINT32_MAX; + } + } + +ErrExit: + return hr; +} // CMiniMdRW::PutStringW + +//***************************************************************************** +// Add a guid to the guid pool, and store the index in the cell. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PutGuid( // S_OK or E_UNEXPECTED. + ULONG ixTbl, // The table. + ULONG ixCol, // The column. + void *pvRecord, // The row. + REFGUID guid) // Value to put. +{ + HRESULT hr = S_OK; + UINT32 nIndex; + UINT32 cbSize = 0; + + // Valid Table, Column, Row? + _ASSERTE(ixTbl < m_TblCount); + _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + + // Column description. + _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iGUID); + + IfFailGo(AddGuid(guid, &nIndex)); + + hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nIndex); + + if (m_maxIx != UINT32_MAX) + { + cbSize = m_GuidHeap.GetSize(); + } + if (cbSize > m_maxIx) + { + m_maxIx = cbSize; + if (m_maxIx > m_limIx && m_eGrow == eg_ok) + { + // OutputDebugStringA("Growing tables due to GUID overflow.\n"); + m_eGrow = eg_grow, m_maxRid = m_maxIx = UINT32_MAX; + } + } + +ErrExit: + return hr; +} // CMiniMdRW::PutGuid + +//***************************************************************************** +// Normally, an MVID is randomly generated for every metadata. +// ChangeMvid() can be used to explicitly set it. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ChangeMvid( // S_OK or E_UNEXPECTED. + REFGUID newMvid) +{ + HRESULT hr = S_OK; + + ModuleRec *pModuleRec; + IfFailRet(GetModuleRecord(1, &pModuleRec)); + UINT32 nGuidIndex = GetCol(TBL_Module, ModuleRec::COL_Mvid, pModuleRec); + + GUID UNALIGNED *pMvid; + IfFailRet(m_GuidHeap.GetGuid( + nGuidIndex, + &pMvid)); + + // Replace the GUID with new MVID. + *pMvid = newMvid; + // This was missing (probably because we don't test on platform with different bitness): + //SwapGuid(pMvid); + + return hr; +} // CMiniMdRW::ChangeMvid + +//***************************************************************************** +// Put a token into a cell. If the column is a coded token, perform the +// encoding first. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PutToken( // S_OK or E_UNEXPECTED. + ULONG ixTbl, // The table. + ULONG ixCol, // The column. + void *pvRecord, // The row. + mdToken tk) // Value to put. +{ + HRESULT hr = S_OK; + ULONG cdTkn; // The new coded token. + + // Valid Table, Column, Row? + _ASSERTE(ixTbl < m_TblCount); + _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + + // Column description. + CMiniColDef ColDef = m_TableDefs[ixTbl].m_pColDefs[ixCol]; + + // Is the column just a RID? + if (ColDef.m_Type <= iRidMax) + hr = PutCol(ColDef, pvRecord, RidFromToken(tk)); + else // Is it a coded token? + if (ColDef.m_Type <= iCodedTokenMax) + { + ULONG indexCodedToken = ColDef.m_Type - iCodedToken; + if (indexCodedToken < COUNTOF(g_CodedTokens)) + { + const CCodedTokenDef *pCdTkn = &g_CodedTokens[indexCodedToken]; + cdTkn = encodeToken(RidFromToken(tk), TypeFromToken(tk), pCdTkn->m_pTokens, pCdTkn->m_cTokens); + hr = PutCol(ColDef, pvRecord, cdTkn); + } + else + { + _ASSERTE(!"PutToken called on unexpected coded token type"); + hr = E_FAIL; + } + } + else // It is an error. + { + _ASSERTE(!"PutToken called on unexpected column type"); + } + + return hr; +} // CMiniMdRW::PutToken + +//***************************************************************************** +// Add a blob to the blob pool, and store the offset in the cell. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::PutBlob( + ULONG ixTbl, // Table with the row. + ULONG ixCol, // Column to set. + void *pvRecord, // The row. + const void *pvData, // Blob data. + ULONG cbData) // Size of the blob data. +{ + HRESULT hr = S_OK; + UINT32 nBlobIndex; + + // Valid Table, Column, Row? + _ASSERTE(ixTbl < m_TblCount); + _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + + // Column description. + _ASSERTE(m_TableDefs[ixTbl].m_pColDefs[ixCol].m_Type == iBLOB); + + IfFailGo(m_BlobHeap.AddBlob( + MetaData::DataBlob((BYTE *)pvData, cbData), + &nBlobIndex)); + + hr = PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pvRecord, nBlobIndex); + + if (m_maxIx != UINT32_MAX) + { + IfFailGo(m_BlobHeap.GetAlignedSize(&nBlobIndex)); + } + if (nBlobIndex > m_maxIx) + { + m_maxIx = nBlobIndex; + if (m_maxIx > m_limIx && m_eGrow == eg_ok) + { + // OutputDebugStringA("Growing tables due to Blob overflow.\n"); + m_eGrow = eg_grow, m_maxRid = m_maxIx = UINT32_MAX; + } + } + +ErrExit: + return hr; +} // CMiniMdRW::PutBlob + +//***************************************************************************** +// Given a table with a pointer to another table, add a row in the second table +// at the end of the range of rows belonging to some parent. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddChildRowIndirectForParent( + ULONG tblParent, // Parent table. + ULONG colParent, // Column in parent table. + ULONG tblChild, // Child table, pointed to by parent cell. + RID ridParent, // Rid of parent row. + void **ppRow) +{ + HRESULT hr; + ULONG ixInsert; // Index of new row. + ULONG i; // Loop control. + void *pRow; // A parent row. + ULONG ixChild; // Some child record RID. + + // If the row in the parent table is the last row, just append. + if (ridParent == GetCountRecs(tblParent)) + { + RID nRowIndex_Ignore; + return AddRecord(tblChild, ppRow, &nRowIndex_Ignore); + } + + // Determine the index at which to insert a row. + IfFailRet(getRow(tblParent, ridParent+1, &pRow)); + ixInsert = GetCol(tblParent, colParent, pRow); + + // Insert the row. + IfFailRet(m_Tables[tblChild].InsertRecord(ixInsert, reinterpret_cast<BYTE **>(ppRow))); + // Count the inserted record. + ++m_Schema.m_cRecs[tblChild]; + + if (m_Schema.m_cRecs[tblChild] > m_maxRid) + { + m_maxRid = m_Schema.m_cRecs[tblChild]; + if (m_maxRid > m_limRid && m_eGrow == eg_ok) + m_eGrow = eg_grow, m_maxIx = m_maxRid = UINT32_MAX; + } + + // Adjust the rest of the rows in the table. + for (i=GetCountRecs(tblParent); i>ridParent; --i) + { + IfFailRet(getRow(tblParent, i, &pRow)); + ixChild = GetCol(tblParent, colParent, pRow); + ++ixChild; + IfFailRet(PutCol(tblParent, colParent, pRow, ixChild)); + } + + return S_OK; +} // CMiniMdRW::AddChildRowIndirectForParent + +//***************************************************************************** +// Given a Parent and a Child, this routine figures if there needs to be an +// indirect table and creates it if needed. Else it just update the pointers +// in the entries contained in the parent table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddChildRowDirectForParent( + ULONG tblParent, // Parent table. + ULONG colParent, // Column in parent table. + ULONG tblChild, // Child table, pointed to by parent cell. + RID ridParent) // Rid of parent row. +{ + HRESULT hr = S_OK; + void *pRow; // A row in the parent table. + RID ixChild; // Rid of a child record. + + if (m_Schema.m_cRecs[tblChild-1] != 0) + { + // If there already exists an indirect table, just return. + hr = S_FALSE; + goto ErrExit; + } + + // If the parent record has subsequent parent records with children, + // we will now need to build a pointer table. + // + // The canonical form of a child pointer in a parent record is to point to + // the start of the child list. A record with no children will point + // to the same location as its subsequent record (that is, if A and B *could* + // have a child record, but only B *does*, both A and B will point to the + // same place. If the last record in the parent table has no child records, + // it will point one past the end of the child table. This is patterned + // after the STL's inclusive-BEGIN and exclusive-END. + // This has the unfortunate side effect that if a child record is added to + // a parent not at the end of its table, *all* of the subsequent parent records + // will have to be updated to point to the new "1 past end of child table" + // location. + // Therefore, as an optimization, we will also recognize a special marker, + // END_OF_TABLE (currently 0), to mean "past eot". + // + // If the child pointer of the record getting the new child is END_OF_TABLE, + // then there is no subsequent child pointer. We need to fix up this parent + // record, and any previous parent records with END_OF_TABLE to point to the + // new child record. + // If the child pointer of this parent record is not END_OF_TABLE, but the + // child pointer of the next parent record is, then there is nothing at + // all that needs to be done. + // If the child pointer of the next parent record is not END_OF_TABLE, then + // we will have to build a pointer table. + + // Get the parent record, and see if its child pointer is END_OF_TABLE. If so, + // fix the parent, and all previous END_OF_TABLE valued parent records. + IfFailGo(getRow(tblParent, ridParent, &pRow)); + ixChild = GetCol(tblParent, colParent, pRow); + if (ixChild == END_OF_TABLE) + { + IfFailGo(ConvertMarkerToEndOfTable(tblParent, colParent, m_Schema.m_cRecs[tblChild], ridParent)); + goto ErrExit; + } + + // The parent did not have END_OF_TABLE for its child pointer. If it was the last + // record in the table, there is nothing more to do. + if (ridParent == m_Schema.m_cRecs[tblParent]) + goto ErrExit; + + // The parent didn't have END_OF_TABLE, and there are more rows in parent table. + // If the next parent record's child pointer is END_OF_TABLE, then all of the + // remaining records are OK. + IfFailGo(getRow(tblParent, ridParent+1, &pRow)); + ixChild = GetCol(tblParent, colParent, pRow); + if (ixChild == END_OF_TABLE) + goto ErrExit; + + // The next record was not END_OF_TABLE, so some adjustment will be required. + // If it points to the actual END of the table, there are no more child records + // and the child pointers can be adjusted to the new END of the table. + if (ixChild == m_Schema.m_cRecs[tblChild]) + { + for (ULONG i=m_Schema.m_cRecs[tblParent]; i>ridParent; --i) + { + IfFailGo(getRow(tblParent, i, &pRow)); + IfFailGo(PutCol(tblParent, colParent, pRow, ixChild+1)); + } + goto ErrExit; + } + + // The next record contained a pointer to some actual child data. That means that + // this is an out-of-order insertion. We must create an indirect table. + // Convert any END_OF_TABLE to actual END of table value. Note that a record has + // just been added to the child table, and not yet to the parent table, so the END + // should currently point to the last valid record (instead of the usual first invalid + // rid). + IfFailGo(ConvertMarkerToEndOfTable(tblParent, colParent, m_Schema.m_cRecs[tblChild], m_Schema.m_cRecs[tblParent])); + // Create the indirect table. + IfFailGo(CreateIndirectTable(tblChild)); + hr = S_FALSE; + +ErrExit: + return hr; +} // CMiniMdRW::AddChildRowDirectForParent + +//***************************************************************************** +// Starting with some location, convert special END_OF_TABLE values into +// actual end of table values (count of records + 1). +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::ConvertMarkerToEndOfTable( + ULONG tblParent, // Parent table to convert. + ULONG colParent, // Column in parent table. + ULONG ixEnd, // Value to store to child pointer. + RID ridParent) // Rid of parent row to start with (work down). +{ + HRESULT hr; + void *pRow; // A row in the parent table. + RID ixChild; // Rid of a child record. + + for (; ridParent > 0; --ridParent) + { + IfFailGo(getRow(tblParent, ridParent, &pRow)); + ixChild = GetCol(tblParent, colParent, pRow); + // Finished when rows no longer have special value. + if (ixChild != END_OF_TABLE) + break; + IfFailGo(PutCol(tblParent, colParent, pRow, ixEnd)); + } + // Success. + hr = S_OK; + +ErrExit: + return hr; +} // CMiniMdRW::ConvertMarkerToEndOfTable + +//***************************************************************************** +// Given a Table ID this routine creates the corresponding pointer table with +// the entries in the given Table ID less one. It doesn't create the last +// entry by default, since its the last entry that caused the Indirect table to +// be required in most cases and will need to inserted at the appropriate location +// with AddChildRowIndirectForParent() function. So, be VERY CAREFUL when using this function! +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CreateIndirectTable( + ULONG ixTbl, // Given Table. + BOOL bOneLess /* = true */) // if true, create one entry less. +{ + void *pRecord; + ULONG cRecords; + HRESULT hr = S_OK; + + if (m_OptionValue.m_ErrorIfEmitOutOfOrder) + { + //<TODO> Can we use some bit fields and reduce the code size here?? + //</TODO> + if (ixTbl == TBL_Field && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDFieldOutOfOrder ) ) + { + _ASSERTE(!"Out of order emit of field token!"); + return CLDB_E_RECORD_OUTOFORDER; + } + else if (ixTbl == TBL_Method && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDMethodOutOfOrder ) ) + { + _ASSERTE(!"Out of order emit of method token!"); + return CLDB_E_RECORD_OUTOFORDER; + } + else if (ixTbl == TBL_Param && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDParamOutOfOrder ) ) + { + _ASSERTE(!"Out of order emit of param token!"); + return CLDB_E_RECORD_OUTOFORDER; + } + else if (ixTbl == TBL_Property && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDPropertyOutOfOrder ) ) + { + _ASSERTE(!"Out of order emit of property token!"); + return CLDB_E_RECORD_OUTOFORDER; + } + else if (ixTbl == TBL_Event && ( m_OptionValue.m_ErrorIfEmitOutOfOrder & MDEventOutOfOrder ) ) + { + _ASSERTE(!"Out of order emit of event token!"); + return CLDB_E_RECORD_OUTOFORDER; + } + } + + _ASSERTE(! HasIndirectTable(ixTbl)); + + cRecords = GetCountRecs(ixTbl); + if (bOneLess) + cRecords--; + + // Create one less than the number of records in the given table. + for (ULONG i = 1; i <= cRecords ; i++) + { + RID nRowIndex_Ignore; + IfFailGo(AddRecord(g_PtrTableIxs[ixTbl].m_ixtbl, &pRecord, &nRowIndex_Ignore)); + IfFailGo(PutCol(g_PtrTableIxs[ixTbl].m_ixtbl, g_PtrTableIxs[ixTbl].m_ixcol, pRecord, i)); + } +ErrExit: + return hr; +} // CMiniMdRW::CreateIndirectTable + +//--------------------------------------------------------------------------------------- +// +// The new paramter may not have been emitted in sequence order. So +// check the current parameter and move it up in the indirect table until +// we find the right home. +// +__checkReturn +HRESULT +CMiniMdRW::FixParamSequence( + RID md) // Rid of method with new parameter. +{ + HRESULT hr; + MethodRec * pMethod; + IfFailRet(GetMethodRecord(md, &pMethod)); + RID ixStart = getParamListOfMethod(pMethod); + RID ixEnd; + IfFailRet(getEndParamListOfMethod(md, &ixEnd)); + int iSlots = 0; + + // Param table should not be empty at this point. + _ASSERTE(ixEnd > ixStart); + + // Get a pointer to the new parameter. + RID ridNew; + ParamPtrRec * pNewParamPtr = NULL; + if (HasIndirectTable(TBL_Param)) + { + IfFailRet(GetParamPtrRecord(--ixEnd, &pNewParamPtr)); + ridNew = GetCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pNewParamPtr); + } + else + { + ridNew = --ixEnd; + } + + ParamRec * pNewParam; + IfFailRet(GetParamRecord(ridNew, &pNewParam)); + + // Walk the list forward looking for the insert point. + for (; ixStart < ixEnd; --ixEnd) + { + // Get the current parameter record. + RID ridOld; + if (HasIndirectTable(TBL_Param)) + { + ParamPtrRec * pParamPtr; + IfFailRet(GetParamPtrRecord(ixEnd - 1, &pParamPtr)); + ridOld = GetCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pParamPtr); + } + else + { + ridOld = ixEnd - 1; + } + + ParamRec * pParamRec; + IfFailRet(GetParamRecord(ridOld, &pParamRec)); + + // If the new record belongs before this existing record, slide + // all of the old stuff down. + if (pNewParam->GetSequence() < pParamRec->GetSequence()) + { + ++iSlots; + } + else + { + break; + } + } + + // If the item is out of order, move everything down one slot and + // copy the new parameter into the new location. Because the heap can be + // split, this must be done carefully. + //<TODO>@Future: one could write a more complicated but faster routine that + // copies blocks within heaps.</TODO> + if (iSlots) + { + RID endRid; + // Create an indirect table if there isn't one already. This is because + // we can't change tokens that have been handed out, in this case the + // param tokens. + if (!HasIndirectTable(TBL_Param)) + { + IfFailRet(CreateIndirectTable(TBL_Param, false)); + IfFailRet(getEndParamListOfMethod(md, &endRid)); + IfFailRet(GetParamPtrRecord(endRid - 1, &pNewParamPtr)); + } + int cbCopy = m_TableDefs[TBL_ParamPtr].m_cbRec; + void * pbBackup = _alloca(cbCopy); + memcpy(pbBackup, pNewParamPtr, cbCopy); + + IfFailRet(getEndParamListOfMethod(md, &endRid)); + for (ixEnd = endRid - 1; iSlots; iSlots--, --ixEnd) + { + ParamPtrRec * pTo; + IfFailRet(GetParamPtrRecord(ixEnd, &pTo)); + ParamPtrRec * pFrom; + IfFailRet(GetParamPtrRecord(ixEnd - 1, &pFrom)); + memcpy(pTo, pFrom, cbCopy); + } + + ParamPtrRec * pTo; + IfFailRet(GetParamPtrRecord(ixEnd, &pTo)); + memcpy(pTo, pbBackup, cbCopy); + } + return S_OK; +} // CMiniMdRW::FixParamSequence + +//--------------------------------------------------------------------------------------- +// +// Given a MethodDef and its parent TypeDef, add the MethodDef to the parent, +// adjusting the MethodPtr table if it exists or if it needs to be created. +// +__checkReturn +HRESULT +CMiniMdRW::AddMethodToTypeDef( + RID td, // The TypeDef to which to add the Method. + RID md) // MethodDef to add to TypeDef. +{ + HRESULT hr; + void * pPtr; + + // Add direct if possible. + IfFailGo(AddChildRowDirectForParent(TBL_TypeDef, TypeDefRec::COL_MethodList, TBL_Method, td)); + + // If couldn't add direct... + if (hr == S_FALSE) + { // Add indirect. + IfFailGo(AddChildRowIndirectForParent(TBL_TypeDef, TypeDefRec::COL_MethodList, TBL_MethodPtr, td, &pPtr)); + hr = PutCol(TBL_MethodPtr, MethodPtrRec::COL_Method, pPtr, md); + + // Add the <md, td> to the method parent lookup table. + IfFailGo(AddMethodToLookUpTable(TokenFromRid(md, mdtMethodDef), td) ); + } +ErrExit: + return hr; +} // CMiniMdRW::AddMethodToTypeDef + +//***************************************************************************** +// Given a FieldDef and its parent TypeDef, add the FieldDef to the parent, +// adjusting the FieldPtr table if it exists or if it needs to be created. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddFieldToTypeDef( + RID td, // The TypeDef to which to add the Field. + RID md) // FieldDef to add to TypeDef. +{ + HRESULT hr; + void *pPtr; + + // Add direct if possible. + IfFailGo(AddChildRowDirectForParent(TBL_TypeDef, TypeDefRec::COL_FieldList, TBL_Field, td)); + + // If couldn't add direct... + if (hr == S_FALSE) + { // Add indirect. + IfFailGo(AddChildRowIndirectForParent(TBL_TypeDef, TypeDefRec::COL_FieldList, TBL_FieldPtr, td, &pPtr)); + hr = PutCol(TBL_FieldPtr, FieldPtrRec::COL_Field, pPtr, md); + + // Add the <md, td> to the field parent lookup table. + IfFailGo(AddFieldToLookUpTable(TokenFromRid(md, mdtFieldDef), td)); + } +ErrExit: + return hr; +} // CMiniMdRW::AddFieldToTypeDef + +//***************************************************************************** +// Given a Param and its parent Method, add the Param to the parent, +// adjusting the ParamPtr table if there is an indirect table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddParamToMethod( + RID md, // The MethodDef to which to add the Param. + RID pd) // Param to add to MethodDef. +{ + HRESULT hr; + void *pPtr; + + IfFailGo(AddChildRowDirectForParent(TBL_Method, MethodRec::COL_ParamList, TBL_Param, md)); + if (hr == S_FALSE) + { + IfFailGo(AddChildRowIndirectForParent(TBL_Method, MethodRec::COL_ParamList, TBL_ParamPtr, md, &pPtr)); + IfFailGo(PutCol(TBL_ParamPtr, ParamPtrRec::COL_Param, pPtr, pd)); + + // Add the <pd, md> to the field parent lookup table. + IfFailGo(AddParamToLookUpTable(TokenFromRid(pd, mdtParamDef), md)); + } + IfFailGo(FixParamSequence(md)); + +ErrExit: + return hr; +} // CMiniMdRW::AddParamToMethod + +//***************************************************************************** +// Given a Property and its parent PropertyMap, add the Property to the parent, +// adjusting the PropertyPtr table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddPropertyToPropertyMap( + RID pmd, // The PropertyMap to which to add the Property. + RID pd) // Property to add to PropertyMap. +{ + HRESULT hr; + void *pPtr; + + IfFailGo(AddChildRowDirectForParent(TBL_PropertyMap, PropertyMapRec::COL_PropertyList, + TBL_Property, pmd)); + if (hr == S_FALSE) + { + IfFailGo(AddChildRowIndirectForParent(TBL_PropertyMap, PropertyMapRec::COL_PropertyList, + TBL_PropertyPtr, pmd, &pPtr)); + hr = PutCol(TBL_PropertyPtr, PropertyPtrRec::COL_Property, pPtr, pd); + } + + +ErrExit: + return hr; +} // CMiniMdRW::AddPropertyToPropertyMap + +//***************************************************************************** +// Given a Event and its parent EventMap, add the Event to the parent, +// adjusting the EventPtr table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddEventToEventMap( + ULONG emd, // The EventMap to which to add the Event. + RID ed) // Event to add to EventMap. +{ + HRESULT hr; + void *pPtr; + + IfFailGo(AddChildRowDirectForParent(TBL_EventMap, EventMapRec::COL_EventList, + TBL_Event, emd)); + if (hr == S_FALSE) + { + IfFailGo(AddChildRowIndirectForParent(TBL_EventMap, EventMapRec::COL_EventList, + TBL_EventPtr, emd, &pPtr)); + hr = PutCol(TBL_EventPtr, EventPtrRec::COL_Event, pPtr, ed); + } +ErrExit: + return hr; +} // CMiniMdRW::AddEventToEventMap + +//***************************************************************************** +// Find helper for a constant. This will trigger constant table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindConstantHelper( // return index to the constant table + mdToken tkParent, // Parent token. + RID *pFoundRid) +{ + _ASSERTE(TypeFromToken(tkParent) != 0); + + // If sorted, use the faster lookup + if (IsSorted(TBL_Constant)) + { + return FindConstantFor(RidFromToken(tkParent), TypeFromToken(tkParent), pFoundRid); + } + return GenericFindWithHash(TBL_Constant, ConstantRec::COL_Parent, tkParent, pFoundRid); +} // CMiniMdRW::FindConstantHelper + +//***************************************************************************** +// Find helper for a FieldMarshal. This will trigger FieldMarshal table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindFieldMarshalHelper( // return index to the field marshal table + mdToken tkParent, // Parent token. Can be a FieldDef or ParamDef. + RID *pFoundRid) +{ + _ASSERTE(TypeFromToken(tkParent) != 0); + + // If sorted, use the faster lookup + if (IsSorted(TBL_FieldMarshal)) + { + return FindFieldMarshalFor(RidFromToken(tkParent), TypeFromToken(tkParent), pFoundRid); + } + return GenericFindWithHash(TBL_FieldMarshal, FieldMarshalRec::COL_Parent, tkParent, pFoundRid); +} // CMiniMdRW::FindFieldMarshalHelper + + +//***************************************************************************** +// Find helper for a method semantics. +// This will look up methodsemantics based on its status! +// Can return out of memory error because of the enumerator. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindMethodSemanticsHelper( + mdToken tkAssociate, // Event or property token + HENUMInternal *phEnum) // fill in the enum +{ + ULONG ridStart, ridEnd; + ULONG index; + MethodSemanticsRec *pMethodSemantics; + HRESULT hr = NOERROR; + CLookUpHash *pHashTable = m_pLookUpHashs[TBL_MethodSemantics]; + + _ASSERTE(TypeFromToken(tkAssociate) != 0); + + if (IsSorted(TBL_MethodSemantics)) + { + IfFailGo(getAssociatesForToken(tkAssociate, &ridEnd, &ridStart)); + HENUMInternal::InitSimpleEnum(0, ridStart, ridEnd, phEnum); + } + else if (pHashTable) + { + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + HENUMInternal::InitDynamicArrayEnum(phEnum); + iHash = HashToken(tkAssociate); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailGo(GetMethodSemanticsRecord(p->tok, &pMethodSemantics)); + if (getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, p->tok) ); + } + } + } + else + { + // linear search + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = 1; index <= getCountMethodSemantics(); index++) + { + IfFailGo(GetMethodSemanticsRecord(index, &pMethodSemantics)); + if (getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, index) ); + } + } + } +ErrExit: + return hr; +} // CMiniMdRW::FindMethodSemanticsHelper + + +//***************************************************************************** +// Find helper for a method semantics given a associate and semantics. +// This will look up methodsemantics based on its status! +// Return CLDB_E_RECORD_NOTFOUND if cannot find the matching one +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindAssociateHelper( + mdToken tkAssociate, // Event or property token + DWORD dwSemantics, // [IN] given a associate semantics(setter, getter, testdefault, reset) + RID *pRid) // [OUT] return matching row index here +{ + ULONG ridStart, ridEnd; + ULONG index; + MethodSemanticsRec *pMethodSemantics; + HRESULT hr = NOERROR; + CLookUpHash *pHashTable = m_pLookUpHashs[TBL_MethodSemantics]; + + _ASSERTE(TypeFromToken(tkAssociate) != 0); + + if (pHashTable) + { + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + iHash = HashToken(tkAssociate); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailGo(GetMethodSemanticsRecord(p->tok, &pMethodSemantics)); + if (pMethodSemantics->GetSemantic() == dwSemantics && getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate) + { + *pRid = p->tok; + goto ErrExit; + } + } + } + else + { + if (IsSorted(TBL_MethodSemantics)) + { + IfFailGo(getAssociatesForToken(tkAssociate, &ridEnd, &ridStart)); + } + else + { + ridStart = 1; + ridEnd = getCountMethodSemantics() + 1; + } + + for (index = ridStart; index < ridEnd ; index++) + { + IfFailGo(GetMethodSemanticsRecord(index, &pMethodSemantics)); + if (pMethodSemantics->GetSemantic() == dwSemantics && getAssociationOfMethodSemantics(pMethodSemantics) == tkAssociate) + { + *pRid = index; + goto ErrExit; + } + } + } + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; +} // CMiniMdRW::FindAssociateHelper + + +//***************************************************************************** +// Find helper for a MethodImpl. +// This will trigger MethodImpl table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindMethodImplHelper( + mdTypeDef td, // TypeDef token for the Class. + HENUMInternal *phEnum) // fill in the enum +{ + ULONG ridStart, ridEnd; + ULONG index; + MethodImplRec *pMethodImpl; + HRESULT hr = NOERROR; + CLookUpHash *pHashTable = m_pLookUpHashs[TBL_MethodImpl]; + + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + if (IsSorted(TBL_MethodImpl)) + { + IfFailGo(getMethodImplsForClass(RidFromToken(td), &ridEnd, &ridStart)); + HENUMInternal::InitSimpleEnum(0, ridStart, ridEnd, phEnum); + } + else if (pHashTable) + { + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + HENUMInternal::InitDynamicArrayEnum(phEnum); + iHash = HashToken(td); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailGo(GetMethodImplRecord(p->tok, &pMethodImpl)); + if (getClassOfMethodImpl(pMethodImpl) == td) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, p->tok) ); + } + } + } + else + { + // linear search + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = 1; index <= getCountMethodImpls(); index++) + { + IfFailGo(GetMethodImplRecord(index, &pMethodImpl)); + if (getClassOfMethodImpl(pMethodImpl) == td) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, index) ); + } + } + } +ErrExit: + return hr; +} // CMiniMdRW::FindMethodImplHelper + + +//***************************************************************************** +// Find helper for a GenericParam. +// This will trigger GenericParam table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindGenericParamHelper( + mdToken tkOwner, // Token for the GenericParams' owner. + HENUMInternal *phEnum) // fill in the enum +{ + HRESULT hr = NOERROR; + ULONG ridStart, ridEnd; // Start, end of range of tokens. + ULONG index; // A loop counter. + GenericParamRec *pGenericParam; + CLookUpHash *pHashTable = m_pLookUpHashs[TBL_GenericParam]; + + if (IsSorted(TBL_GenericParam)) + { + mdToken tk; + tk = encodeToken(RidFromToken(tkOwner), TypeFromToken(tkOwner), mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)); + IfFailGo(SearchTableForMultipleRows(TBL_GenericParam, + _COLDEF(GenericParam,Owner), + tk, + &ridEnd, + &ridStart)); + HENUMInternal::InitSimpleEnum(mdtGenericParam, ridStart, ridEnd, phEnum); + } + else if (pHashTable) + { + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + HENUMInternal::InitDynamicArrayEnum(phEnum); + iHash = HashToken(tkOwner); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailGo(GetGenericParamRecord(p->tok, &pGenericParam)); + if (getOwnerOfGenericParam(pGenericParam) == tkOwner) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(p->tok, mdtGenericParam)) ); + } + } + } + else + { + // linear search + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = 1; index <= getCountGenericParams(); index++) + { + IfFailGo(GetGenericParamRecord(index, &pGenericParam)); + if (getOwnerOfGenericParam(pGenericParam) == tkOwner) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(index, mdtGenericParam)) ); + } + } + } +ErrExit: + return hr; +} // CMiniMdRW::FindGenericParamHelper + + +//***************************************************************************** +// Find helper for a GenericParamConstraint. +// This will trigger GenericParamConstraint table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindGenericParamConstraintHelper( + mdGenericParam tkParam, // Token for the GenericParam + HENUMInternal *phEnum) // fill in the enum +{ + HRESULT hr = NOERROR; + ULONG ridStart, ridEnd; // Start, end of range of tokens. + ULONG index; // A loop counter. + GenericParamConstraintRec *pConstraint; + CLookUpHash *pHashTable = m_pLookUpHashs[TBL_GenericParamConstraint]; + RID ridParam = RidFromToken(tkParam); + _ASSERTE(TypeFromToken(tkParam) == mdtGenericParam); + + // Extract the rid part of the token for comparison below. Be sure + // that the column is a RID column, so that getGPCFGP() returns a RID. + _ASSERTE(IsRidType(m_TableDefs[TBL_GenericParamConstraint].m_pColDefs[GenericParamConstraintRec::COL_Owner].m_Type)); + + if (IsSorted(TBL_GenericParamConstraint)) + { + IfFailGo(getGenericParamConstraintsForGenericParam(ridParam, &ridEnd, &ridStart)); + HENUMInternal::InitSimpleEnum(mdtGenericParamConstraint, ridStart, ridEnd, phEnum); + } + else if (pHashTable) + { + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + HENUMInternal::InitDynamicArrayEnum(phEnum); + iHash = HashToken(tkParam); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailGo(GetGenericParamConstraintRecord(p->tok, &pConstraint)); + if (getOwnerOfGenericParamConstraint(pConstraint) == tkParam) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(p->tok, mdtGenericParamConstraint)) ); + } + } + } + else + { + // linear search + HENUMInternal::InitDynamicArrayEnum(phEnum); + for (index = 1; index <= getCountGenericParamConstraints(); index++) + { + IfFailGo(GetGenericParamConstraintRecord(index, &pConstraint)); + if (getOwnerOfGenericParamConstraint(pConstraint) == tkParam) + { + IfFailGo( HENUMInternal::AddElementToEnum(phEnum, TokenFromRid(index, mdtGenericParamConstraint)) ); + } + } + } +ErrExit: + return hr; +} // CMiniMdRW::FindGenericParamConstraintHelper + + +//***************************************************************************** +// Find helper for a ClassLayout. This will trigger ClassLayout table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindClassLayoutHelper( // return index to the ClassLayout table + mdTypeDef tkParent, // Parent token. + RID *pFoundRid) +{ + _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef); + + // If sorted, use the faster lookup + if (IsSorted(TBL_ClassLayout)) + { + return FindClassLayoutFor(RidFromToken(tkParent), pFoundRid); + } + return GenericFindWithHash(TBL_ClassLayout, ClassLayoutRec::COL_Parent, tkParent, pFoundRid); +} // CMiniMdRW::FindClassLayoutHelper + +//***************************************************************************** +// Find helper for a FieldLayout. This will trigger FieldLayout table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindFieldLayoutHelper( // return index to the FieldLayout table + mdFieldDef tkField, // Field RID. + RID *pFoundRid) +{ + _ASSERTE(TypeFromToken(tkField) == mdtFieldDef); + + // If sorted, use the faster lookup + if (IsSorted(TBL_FieldLayout)) + { + return FindFieldLayoutFor(RidFromToken(tkField), pFoundRid); + } + return GenericFindWithHash(TBL_FieldLayout, FieldLayoutRec::COL_Field, tkField, pFoundRid); +} // CMiniMdRW::FindFieldLayoutHelper + +//***************************************************************************** +// Find helper for a ImplMap. This will trigger ImplMap table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindImplMapHelper( // return index to the ImplMap table + mdToken tk, // Member forwarded token. + RID *pFoundRid) +{ + _ASSERTE(TypeFromToken(tk) != 0); + + // If sorted, use the faster lookup + if (IsSorted(TBL_ImplMap)) + { + return FindImplMapFor(RidFromToken(tk), TypeFromToken(tk), pFoundRid); + } + return GenericFindWithHash(TBL_ImplMap, ImplMapRec::COL_MemberForwarded, tk, pFoundRid); +} // CMiniMdRW::FindImplMapHelper + + +//***************************************************************************** +// Find helper for a FieldRVA. This will trigger FieldRVA table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindFieldRVAHelper( // return index to the FieldRVA table + mdFieldDef tkField, // Field token. + RID *pFoundRid) +{ + _ASSERTE(TypeFromToken(tkField) == mdtFieldDef); + + // If sorted, use the faster lookup + if (IsSorted(TBL_FieldRVA)) + { + return FindFieldRVAFor(RidFromToken(tkField), pFoundRid); + } + return GenericFindWithHash(TBL_FieldRVA, FieldRVARec::COL_Field, tkField, pFoundRid); +} // CMiniMdRW::FindFieldRVAHelper + +//***************************************************************************** +// Find helper for a NestedClass. This will trigger NestedClass table to be sorted if it is not. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindNestedClassHelper( // return index to the NestedClass table + mdTypeDef tkClass, // NestedClass RID. + RID *pFoundRid) +{ + // If sorted, use the faster lookup + if (IsSorted(TBL_NestedClass)) + { + return FindNestedClassFor(RidFromToken(tkClass), pFoundRid); + } + return GenericFindWithHash(TBL_NestedClass, NestedClassRec::COL_NestedClass, tkClass, pFoundRid); +} // CMiniMdRW::FindNestedClassHelper + + +//************************************************************************* +// generic find helper with hash table +//************************************************************************* +__checkReturn +HRESULT +CMiniMdRW::GenericFindWithHash( // Return code. + ULONG ixTbl, // Table with hash + ULONG ixCol, // col that we hash. + mdToken tkTarget, // token to be find in the hash + RID *pFoundRid) +{ + HRESULT hr; + ULONG index; + mdToken tkHash; + BYTE * pRec; + + // Partial check -- only one rid for table 0, so if type is 0, rid should be 1. + _ASSERTE(TypeFromToken(tkTarget) != 0 || RidFromToken(tkTarget) == 1); + + if (m_pLookUpHashs[ixTbl] == NULL) + { + // Just ignore the returned error - the hash is either created or not + (void)GenericBuildHashTable(ixTbl, ixCol); + } + + CLookUpHash * pHashTable = m_pLookUpHashs[ixTbl]; + if (pHashTable != NULL) + { + TOKENHASHENTRY *p; + ULONG iHash; + int pos; + + // Hash the data. + iHash = HashToken(tkTarget); + + // Go through every entry in the hash chain looking for ours. + for (p = pHashTable->FindFirst(iHash, pos); + p; + p = pHashTable->FindNext(pos)) + { + IfFailRet(m_Tables[ixTbl].GetRecord(p->tok, &pRec)); + + // get the column value that we will hash + tkHash = GetToken(ixTbl, ixCol, pRec); + if (tkHash == tkTarget) + { + // found the match + *pFoundRid = p->tok; + return S_OK; + } + } + } + else + { + // linear search + for (index = 1; index <= GetCountRecs(ixTbl); index++) + { + IfFailRet(m_Tables[ixTbl].GetRecord(index, &pRec)); + tkHash = GetToken(ixTbl, ixCol, pRec); + if (tkHash == tkTarget) + { + // found the match + *pFoundRid = index; + return S_OK; + } + } + } + *pFoundRid = 0; + return S_OK; +} // CMiniMdRW::GenericFindWithHash + +//************************************************************************* +// Build a hash table for the specified table if the size exceed the thresholds. +//************************************************************************* +__checkReturn +HRESULT +CMiniMdRW::GenericBuildHashTable( + ULONG ixTbl, // Table with hash. + ULONG ixCol) // Column that we hash. +{ + HRESULT hr = S_OK; + BYTE *pRec; + mdToken tkHash; + ULONG iHash; + TOKENHASHENTRY *pEntry; + + // If the hash table hasn't been built it, see if it should get faulted in. + if (m_pLookUpHashs[ixTbl] == NULL) + { + ULONG ridEnd = GetCountRecs(ixTbl); + + //<TODO>@FUTURE: we need to init the size of the hash table corresponding to the current + // size of table in E&C's case. + //</TODO> + // Avoid prefast warning with "if (ridEnd + 1 > INDEX_ROW_COUNT_THRESHOLD)" + if (ridEnd > INDEX_ROW_COUNT_THRESHOLD - 1) + { + // Create a new hash. + NewHolder<CLookUpHash> pHashTable = new (nothrow) CLookUpHash; + IfNullGo(pHashTable); + IfFailGo(pHashTable->NewInit( + g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)])); + + // Scan every entry already in the table, add it to the hash. + for (ULONG index = 1; index <= ridEnd; index++) + { + IfFailGo(m_Tables[ixTbl].GetRecord(index, &pRec)); + + // get the column value that we will hash + tkHash = GetToken(ixTbl, ixCol, pRec); + + // hash the value + iHash = HashToken(tkHash); + + pEntry = pHashTable->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = index; + + } + + if (InterlockedCompareExchangeT<CLookUpHash *>( + &m_pLookUpHashs[ixTbl], + pHashTable, + NULL) == NULL) + { // We won the initializaion race + pHashTable.SuppressRelease(); + } + } + } +ErrExit: + return hr; +} // CMiniMdRW::GenericBuildHashTable + +//************************************************************************* +// Add a rid from a table into a hash. We will hash on the ixCol of the ixTbl. +//************************************************************************* +__checkReturn +HRESULT +CMiniMdRW::GenericAddToHash( + ULONG ixTbl, // Table with hash + ULONG ixCol, // column that we hash by calling HashToken. + RID rid) // Token of new guy into the ixTbl. +{ + HRESULT hr = S_OK; + CLookUpHash *pHashTable = m_pLookUpHashs[ixTbl]; + void *pRec; + mdToken tkHash; + ULONG iHash; + TOKENHASHENTRY *pEntry; + + // If the hash table hasn't been built it, see if it should get faulted in. + if (pHashTable == NULL) + { + IfFailGo(GenericBuildHashTable(ixTbl, ixCol)); + } + else + { + // Adding into hash table has to be protected by write-lock + INDEBUG(Debug_CheckIsLockedForWrite();) + + IfFailGo(m_Tables[ixTbl].GetRecord(rid, reinterpret_cast<BYTE **>(&pRec))); + + tkHash = GetToken(ixTbl, ixCol, pRec); + iHash = HashToken(tkHash); + pEntry = pHashTable->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = rid; + } + +ErrExit: + return hr; +} // CMiniMdRW::GenericAddToHash + + +//***************************************************************************** +// look up a table by a col given col value is ulVal. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::LookUpTableByCol( + ULONG ulVal, // Value for which to search. + VirtualSort *pVSTable, // A VirtualSort on the table, if any. + RID *pRidStart, // Put RID of first match here. + RID *pRidEnd) // [OPTIONAL] Put RID of end match here. +{ + HRESULT hr = NOERROR; + ULONG ixTbl; + ULONG ixCol; + + _ASSERTE(pVSTable != NULL); + ixTbl = pVSTable->m_ixTbl; + ixCol = pVSTable->m_ixCol; + if (IsSorted(ixTbl)) + { + // Table itself is sorted so we don't need to build a virtual sort table. + // Binary search on the table directly. + // + IfFailGo(SearchTableForMultipleRows( + ixTbl, + m_TableDefs[ixTbl].m_pColDefs[ixCol], + ulVal, + pRidEnd, + pRidStart)); + } + else + { + if (!pVSTable->m_isMapValid) + { + INDEBUG(Debug_CheckIsLockedForWrite();) + + int iCount; + + // build the parallel VirtualSort table + if (pVSTable->m_pMap == NULL) + { + // the first time that we build the VS table. We need to allocate the TOKENMAP + pVSTable->m_pMap = new (nothrow) TOKENMAP; + IfNullGo(pVSTable->m_pMap); + } + + // ensure the look up table is big enough + iCount = pVSTable->m_pMap->Count(); + if (pVSTable->m_pMap->AllocateBlock(m_Schema.m_cRecs[ixTbl] + 1 - iCount) == 0) + { + IfFailGo(E_OUTOFMEMORY); + } + + // now build the table + // Element 0 of m_pMap will never be used, its just being initialized anyway. + for (ULONG i = 0; i <= m_Schema.m_cRecs[ixTbl]; i++) + { + *(pVSTable->m_pMap->Get(i)) = i; + } + // sort the table + IfFailGo(pVSTable->Sort()); + } + // binary search on the LookUp + { + void *pRow; // Row from a table. + ULONG val; // Value from a row. + CMiniColDef *pCol; + int lo,hi,mid=0; // binary search indices. + RID ridEnd, ridBegin; + + pCol = m_TableDefs[ixTbl].m_pColDefs; + + // Start with entire table. + lo = 1; + hi = GetCountRecs( ixTbl ); + // While there are rows in the range... + while ( lo <= hi ) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + IfFailGo(getRow( + ixTbl, + (UINT32)*(pVSTable->m_pMap->Get(mid)), + &pRow)); + val = getIX( pRow, pCol[ixCol] ); + + // If equal to the target, done. + if ( val == ulVal ) + break; + // If middle item is too small, search the top half. + if ( val < ulVal ) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + if ( lo > hi ) + { + // Didn't find anything that matched. + *pRidStart = 0; + if (pRidEnd) *pRidEnd = 0; + goto ErrExit; + } + + + // Now mid is pointing to one of the several records that match the search. + // Find the beginning and find the end. + ridBegin = mid; + + // End will be at least one larger than found record. + ridEnd = ridBegin + 1; + + // Search back to start of group. + for (;;) + { + if (ridBegin <= 1) + { + break; + } + IfFailGo(getRow( + ixTbl, + (UINT32)*(pVSTable->m_pMap->Get(ridBegin-1)), + &pRow)); + if (getIX(pRow, pCol[ixCol]) != ulVal) + { + break; + } + --ridBegin; + } + + // If desired, search forward to end of group. + if (pRidEnd != NULL) + { + for (;;) + { + if (ridEnd > GetCountRecs(ixTbl)) + { + break; + } + IfFailGo(getRow( + ixTbl, + (UINT32)*(pVSTable->m_pMap->Get(ridEnd)), + &pRow)); + if (getIX(pRow, pCol[ixCol]) != ulVal) + { + break; + } + ++ridEnd; + } + *pRidEnd = ridEnd; + } + *pRidStart = ridBegin; + } + } + + // fall through +ErrExit: + return hr; +} // CMiniMdRW::LookUpTableByCol + +__checkReturn +HRESULT +CMiniMdRW::Impl_SearchTableRW( + ULONG ixTbl, // Table to search. + ULONG ixCol, // Column to search. + ULONG ulTarget, // Value to search for. + RID *pFoundRid) +{ + HRESULT hr = S_OK; + RID iRid; // The resulting RID. + RID iRidEnd; // Unused. + + // Look up. + hr = LookUpTableByCol(ulTarget, m_pVS[ixTbl], &iRid, &iRidEnd); + if (FAILED(hr)) + { + iRid = 0; + } + else // Convert to real RID. + { + iRid = GetRidFromVirtualSort(ixTbl, iRid); + } + + *pFoundRid = iRid; + return S_OK; +} // CMiniMdRW::Impl_SearchTableRW + +//***************************************************************************** +// Search a table for the row containing the given key value. +// EG. Constant table has pointer back to Param or Field. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::vSearchTable( // RID of matching row, or 0. + ULONG ixTbl, // Table to search. + CMiniColDef sColumn, // Sorted key column, containing search value. + ULONG ulTarget, // Target for search. + RID *pRid) +{ + HRESULT hr; + void *pRow; // Row from a table. + ULONG val; // Value from a row. + + int lo,mid,hi; // binary search indices. + + // Binary search requires sorted table. + // @todo GENERICS: why is IsSorted not true for mdtGenericParam? + // _ASSERTE(IsSorted(ixTbl)); + + // Start with entire table. + lo = 1; + hi = GetCountRecs(ixTbl); + // While there are rows in the range... + while (lo <= hi) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + IfFailRet(getRow(ixTbl, mid, &pRow)); + val = getIX(pRow, sColumn); + // If equal to the target, done. + if (val == ulTarget) + { + *pRid = mid; + return S_OK; + } + // If middle item is too small, search the top half. + if (val < ulTarget || val == END_OF_TABLE) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + // Didn't find anything that matched. + + // @todo GENERICS: Work around for refEmit feature. Remove once table is sorted. + if (ixTbl == TBL_GenericParam && !IsSorted(ixTbl)) + { + for (int i = 1; i <= (int)GetCountRecs(ixTbl); i ++) + { + IfFailRet(getRow(ixTbl, i, &pRow)); + if (getIX(pRow, sColumn) == ulTarget) + { + *pRid = i; + return S_OK; + } + } + } + + *pRid = 0; + return S_OK; +} // CMiniMdRW::vSearchTable + +//***************************************************************************** +// Search a table for the highest-RID row containing a value that is less than +// or equal to the target value. EG. TypeDef points to first Field, but if +// a TypeDef has no fields, it points to first field of next TypeDef. +// This is complicated by the possible presence of columns containing +// END_OF_TABLE values, which are not necessarily in greater than +// other values. However, this invalid-rid value will occur only at the +// end of the table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::vSearchTableNotGreater( // RID of matching row, or 0. + ULONG ixTbl, // Table to search. + CMiniColDef sColumn, // the column def containing search value + ULONG ulTarget, // target for search + RID *pRid) +{ + HRESULT hr; + void *pRow; // Row from a table. + ULONG cRecs; // Rows in the table. + ULONG val = 0; // Value from a table. + ULONG lo,mid=0,hi; // binary search indices. + + cRecs = GetCountRecs(ixTbl); + + // Start with entire table. + lo = 1; + hi = cRecs; + // If no recs, return. + if (lo > hi) + { + *pRid = 0; + return S_OK; + } + // While there are rows in the range... + while (lo <= hi) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + IfFailRet(getRow(ixTbl, mid, &pRow)); + val = getIX(pRow, sColumn); + // If equal to the target, done searching. + if (val == ulTarget) + break; + // If middle item is too small, search the top half. + if (val < ulTarget && val != END_OF_TABLE) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + // May or may not have found anything that matched. Mid will be close, but may + // be to high or too low. It should point to the highest acceptable + // record. + + // If the value is greater than the target, back up just until the value is + // less than or equal to the target. SHOULD only be one step. + if (val > ulTarget || val == END_OF_TABLE) + { + while (val > ulTarget || val == END_OF_TABLE) + { + _ASSERTE(mid > 1); + // If no recs match, return. + if (mid == 1) + { + *pRid = 0; + return S_OK; + } + --mid; + IfFailRet(getRow(ixTbl, mid, &pRow)); + val = getIX(pRow, sColumn); + } + } + else + { + // Value is less than or equal to the target. As long as the next + // record is also acceptable, move forward. + while (mid < cRecs) + { + // There is another record. Get its value. + IfFailRet(getRow(ixTbl, mid+1, &pRow)); + val = getIX(pRow, sColumn); + // If that record is too high, stop. + if (val > ulTarget || val == END_OF_TABLE) + break; + mid++; + } + } + + // Return the value that's just less than the target. + *pRid = mid; + return S_OK; +} // CMiniMdRW::vSearchTableNotGreater + +//--------------------------------------------------------------------------------------- +// +// Create MemberRef hash table. +// +__checkReturn +HRESULT +CMiniMdRW::CreateMemberRefHash() +{ + HRESULT hr = S_OK; + + if (m_pMemberRefHash == NULL) + { + ULONG ridEnd = getCountMemberRefs(); + if (ridEnd + 1 > INDEX_ROW_COUNT_THRESHOLD) + { + // Create a new hash. + NewHolder<CMemberRefHash> pMemberRefHash = new (nothrow) CMemberRefHash(); + IfNullGo(pMemberRefHash); + IfFailGo(pMemberRefHash->NewInit( + g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)])); + + // Scan every entry already in the table, add it to the hash. + for (ULONG index = 1; index <= ridEnd; index++) + { + MemberRefRec * pMemberRef; + IfFailGo(GetMemberRefRecord(index, &pMemberRef)); + + LPCSTR szMemberRefName; + IfFailGo(getNameOfMemberRef(pMemberRef, &szMemberRefName)); + ULONG iHash = HashMemberRef( + getClassOfMemberRef(pMemberRef), + szMemberRefName); + + TOKENHASHENTRY * pEntry = pMemberRefHash->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = TokenFromRid(index, mdtMemberRef); + } + + if (InterlockedCompareExchangeT<CMemberRefHash *>(&m_pMemberRefHash, pMemberRefHash, NULL) == NULL) + { // We won the initialization race + pMemberRefHash.SuppressRelease(); + } + } + } + +ErrExit: + return hr; +} // CMiniMdRW::CreateMemberRefHash + +//--------------------------------------------------------------------------------------- +// +// Add a new MemberRef to the hash table. +// +__checkReturn +HRESULT +CMiniMdRW::AddMemberRefToHash( + mdMemberRef mr) // Token of new guy. +{ + HRESULT hr = S_OK; + + // If the hash exists, we will add to it - requires write-lock + INDEBUG(Debug_CheckIsLockedForWrite();) + + // If the hash table hasn't been built it, see if it should get faulted in. + if (m_pMemberRefHash == NULL) + { + IfFailGo(CreateMemberRefHash()); + } + else + { + MemberRefRec * pMemberRef; + IfFailGo(GetMemberRefRecord(RidFromToken(mr), &pMemberRef)); + + LPCSTR szMemberRefName; + IfFailGo(getNameOfMemberRef(pMemberRef, &szMemberRefName)); + ULONG iHash = HashMemberRef( + getClassOfMemberRef(pMemberRef), + szMemberRefName); + + TOKENHASHENTRY * pEntry = m_pMemberRefHash->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = TokenFromRid(RidFromToken(mr), mdtMemberRef); + } + +ErrExit: + return hr; +} // CMiniMdRW::AddMemberRefToHash + +//--------------------------------------------------------------------------------------- +// +// If the hash is built, search for the item. Ignore token *ptkMemberRef. +// +CMiniMdRW::HashSearchResult +CMiniMdRW::FindMemberRefFromHash( + mdToken tkParent, // Parent token. + LPCUTF8 szName, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob, // Size of signature. + mdMemberRef * ptkMemberRef) // IN: Ignored token. OUT: Return if found. +{ + // If the table is there, look for the item in the chain of items. + if (m_pMemberRefHash != NULL) + { + TOKENHASHENTRY * p; + ULONG iHash; + int pos; + + // Hash the data. + iHash = HashMemberRef(tkParent, szName); + + // Go through every entry in the hash chain looking for ours. + for (p = m_pMemberRefHash->FindFirst(iHash, pos); + p != NULL; + p = m_pMemberRefHash->FindNext(pos)) + { + if ((CompareMemberRefs(p->tok, tkParent, szName, pvSigBlob, cbSigBlob) == S_OK) + && (*ptkMemberRef != p->tok)) + { + *ptkMemberRef = p->tok; + return Found; + } + } + + return NotFound; + } + else + { + return NoTable; + } +} // CMiniMdRW::FindMemberRefFromHash + +//***************************************************************************** +// Check a given mr token to see if this one is a match. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CompareMemberRefs( // S_OK match, S_FALSE no match. + mdMemberRef mr, // Token to check. + mdToken tkPar, // Parent token. + LPCUTF8 szNameUtf8, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob) // Size of signature. +{ + HRESULT hr; + MemberRefRec *pMemberRef; + LPCUTF8 szNameUtf8Tmp; + PCCOR_SIGNATURE pvSigBlobTmp; + ULONG cbSigBlobTmp; + + IfFailRet(GetMemberRefRecord(RidFromToken(mr), &pMemberRef)); + if (!IsNilToken(tkPar)) + { + // If caller specifies the tkPar and tkPar doesn't match, + // try the next memberref. + // + if (tkPar != getClassOfMemberRef(pMemberRef)) + return S_FALSE; + } + + IfFailRet(getNameOfMemberRef(pMemberRef, &szNameUtf8Tmp)); + if (strcmp(szNameUtf8Tmp, szNameUtf8) == 0) + { + if (pvSigBlob == NULL) + { + return S_OK; + } + + // Name matched. Now check the signature if caller supplies signature + // + if ((cbSigBlob != 0) && (pvSigBlob != NULL)) + { + IfFailRet(getSignatureOfMemberRef(pMemberRef, &pvSigBlobTmp, &cbSigBlobTmp)); + if ((cbSigBlobTmp == cbSigBlob) && + (memcmp(pvSigBlob, pvSigBlobTmp, cbSigBlob) == 0)) + { + return S_OK; + } + } + } + return S_FALSE; +} // CMiniMdRW::CompareMemberRefs + + +//***************************************************************************** +// Add a new memberdef to the hash table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddMemberDefToHash( + mdToken tkMember, // Token of new guy. It can be MethodDef or FieldDef + mdToken tkParent) // Parent token. +{ + HRESULT hr = S_OK; + ULONG iHash; + MEMBERDEFHASHENTRY * pEntry; + + // If the hash exists, we will add to it - requires write-lock + INDEBUG(Debug_CheckIsLockedForWrite();) + + // If the hash table hasn't been built it, see if it should get faulted in. + if (m_pMemberDefHash == NULL) + { + IfFailGo(CreateMemberDefHash()); + } + else + { + LPCSTR szName; + if (TypeFromToken(tkMember) == mdtMethodDef) + { + MethodRec * pMethodRecord; + IfFailGo(GetMethodRecord(RidFromToken(tkMember), &pMethodRecord)); + IfFailGo(getNameOfMethod(pMethodRecord, &szName)); + } + else + { + _ASSERTE(TypeFromToken(tkMember) == mdtFieldDef); + FieldRec * pFieldRecord; + IfFailGo(GetFieldRecord(RidFromToken(tkMember), &pFieldRecord)); + IfFailGo(getNameOfField(pFieldRecord, &szName)); + } + + iHash = HashMemberDef(tkParent, szName); + + pEntry = m_pMemberDefHash->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = tkMember; + pEntry->tkParent = tkParent; + } + +ErrExit: + return hr; +} // CMiniMdRW::AddMemberDefToHash + + +//***************************************************************************** +// Create MemberDef Hash +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CreateMemberDefHash() +{ + HRESULT hr = S_OK; + ULONG iHash; + MEMBERDEFHASHENTRY * pEntry; + + // If the hash table hasn't been built it, see if it should get faulted in. + if (m_pMemberDefHash == NULL) + { + ULONG ridMethod = getCountMethods(); + ULONG ridField = getCountFields(); + ULONG iType; + ULONG ridStart; + ULONG ridEnd; + TypeDefRec * pRec; + MethodRec * pMethod; + FieldRec * pField; + + if ((ridMethod + ridField + 1) > INDEX_ROW_COUNT_THRESHOLD) + { + // Create a new hash. + NewHolder<CMemberDefHash> pMemberDefHash = new (nothrow) CMemberDefHash(); + IfNullGo(pMemberDefHash); + IfFailGo(pMemberDefHash->NewInit( + g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)])); + + for (iType = 1; iType <= getCountTypeDefs(); iType++) + { + IfFailGo(GetTypeDefRecord(iType, &pRec)); + ridStart = getMethodListOfTypeDef(pRec); + IfFailGo(getEndMethodListOfTypeDef(iType, &ridEnd)); + + // add all of the methods of this typedef into hash table + for (; ridStart < ridEnd; ridStart++) + { + RID methodRid; + IfFailGo(GetMethodRid(ridStart, &methodRid)); + IfFailGo(GetMethodRecord(methodRid, &pMethod)); + LPCSTR szMethodName; + IfFailGo(getNameOfMethod(pMethod, &szMethodName)); + iHash = HashMemberDef(TokenFromRid(iType, mdtTypeDef), szMethodName); + + pEntry = pMemberDefHash->Add(iHash); + if (pEntry == NULL) + IfFailGo(OutOfMemory()); + pEntry->tok = TokenFromRid(methodRid, mdtMethodDef); + pEntry->tkParent = TokenFromRid(iType, mdtTypeDef); + } + + // add all of the fields of this typedef into hash table + ridStart = getFieldListOfTypeDef(pRec); + IfFailGo(getEndFieldListOfTypeDef(iType, &ridEnd)); + + // Scan every entry already in the Method table, add it to the hash. + for (; ridStart < ridEnd; ridStart++) + { + RID fieldRid; + IfFailGo(GetFieldRid(ridStart, &fieldRid)); + IfFailGo(GetFieldRecord(fieldRid, &pField)); + LPCSTR szFieldName; + IfFailGo(getNameOfField(pField, &szFieldName)); + iHash = HashMemberDef(TokenFromRid(iType, mdtTypeDef), szFieldName); + + pEntry = pMemberDefHash->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = TokenFromRid(fieldRid, mdtFieldDef); + pEntry->tkParent = TokenFromRid(iType, mdtTypeDef); + } + } + + if (InterlockedCompareExchangeT<CMemberDefHash *>(&m_pMemberDefHash, pMemberDefHash, NULL) == NULL) + { // We won the initialization race + pMemberDefHash.SuppressRelease(); + } + } + } +ErrExit: + return hr; +} // CMiniMdRW::CreateMemberDefHash + +//--------------------------------------------------------------------------------------- +// +// If the hash is built, search for the item. Ignore token *ptkMember. +// +CMiniMdRW::HashSearchResult +CMiniMdRW::FindMemberDefFromHash( + mdToken tkParent, // Parent token. + LPCUTF8 szName, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob, // Size of signature. + mdToken * ptkMember) // IN: Ignored token. OUT: Return if found. It can be MethodDef or FieldDef +{ + // check to see if we need to create hash table + if (m_pMemberDefHash == NULL) + { + // Ignore the failure - the hash won't be created in the worst case + (void)CreateMemberDefHash(); + } + + // If the table is there, look for the item in the chain of items. + if (m_pMemberDefHash != NULL) + { + MEMBERDEFHASHENTRY * pEntry; + ULONG iHash; + int pos; + + // Hash the data. + iHash = HashMemberDef(tkParent, szName); + + // Go through every entry in the hash chain looking for ours. + for (pEntry = m_pMemberDefHash->FindFirst(iHash, pos); + pEntry != NULL; + pEntry = m_pMemberDefHash->FindNext(pos)) + { + if ((CompareMemberDefs(pEntry->tok, pEntry->tkParent, tkParent, szName, pvSigBlob, cbSigBlob) == S_OK) + && (pEntry->tok != *ptkMember)) + { + *ptkMember = pEntry->tok; + return Found; + } + } + + return NotFound; + } + else + { + return NoTable; + } +} // CMiniMdRW::FindMemberDefFromHash + + +//***************************************************************************** +// Check a given memberDef token to see if this one is a match. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CompareMemberDefs( // S_OK match, S_FALSE no match. + mdToken tkMember, // Token to check. It can be MethodDef or FieldDef + mdToken tkParent, // Parent token recorded in the hash entry + mdToken tkPar, // Parent token. + LPCUTF8 szNameUtf8, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob) // Size of signature. +{ + HRESULT hr; + MethodRec *pMethod; + FieldRec *pField; + LPCUTF8 szNameUtf8Tmp; + PCCOR_SIGNATURE pvSigBlobTmp; + ULONG cbSigBlobTmp; + bool bPrivateScope; + + if (TypeFromToken(tkMember) == mdtMethodDef) + { + IfFailGo(GetMethodRecord(RidFromToken(tkMember), &pMethod)); + IfFailGo(getNameOfMethod(pMethod, &szNameUtf8Tmp)); + IfFailGo(getSignatureOfMethod(pMethod, &pvSigBlobTmp, &cbSigBlobTmp)); + bPrivateScope = IsMdPrivateScope(getFlagsOfMethod(pMethod)); + } + else + { + _ASSERTE(TypeFromToken(tkMember) == mdtFieldDef); + IfFailGo(GetFieldRecord(RidFromToken(tkMember), &pField)); + IfFailGo(getNameOfField(pField, &szNameUtf8Tmp)); + IfFailGo(getSignatureOfField(pField, &pvSigBlobTmp, &cbSigBlobTmp)); + bPrivateScope = IsFdPrivateScope(getFlagsOfField(pField)); + } + if (bPrivateScope || (tkPar != tkParent)) + { + return S_FALSE; + } + + if (strcmp(szNameUtf8Tmp, szNameUtf8) == 0) + { + if (pvSigBlob == NULL) + { + return S_OK; + } + + // Name matched. Now check the signature if caller supplies signature + // + if ((cbSigBlob != 0) && (pvSigBlob != NULL)) + { + if ((cbSigBlobTmp == cbSigBlob) && + (memcmp(pvSigBlob, pvSigBlobTmp, cbSigBlob) == 0)) + { + return S_OK; + } + } + } + hr = S_FALSE; +ErrExit: + return hr; +} // CMiniMdRW::CompareMemberDefs + +//***************************************************************************** +// Add a new NamedItem to the hash table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddNamedItemToHash( + ULONG ixTbl, // Table with the new item. + mdToken tk, // Token of new guy. + LPCUTF8 szName, // Name of item. + mdToken tkParent) // Token of parent, if any. +{ + HRESULT hr = S_OK; + BYTE *pNamedItem; // A named item record. + LPCUTF8 szItem; // Name of the item. + mdToken tkPar = 0; // Parent token of the item. + ULONG iHash; // A named item's hash value. + TOKENHASHENTRY *pEntry; // New hash entry. + + // If the hash table hasn't been built it, see if it should get faulted in. + if (m_pNamedItemHash == NULL) + { + ULONG ridEnd = GetCountRecs(ixTbl); + // Range check avoiding prefast warning with: "if (ridEnd + 1 > INDEX_ROW_COUNT_THRESHOLD)" + if (ridEnd > (INDEX_ROW_COUNT_THRESHOLD - 1)) + { + // This assert causes Dev11 #65887, turn it on when the bug is fixed + //INDEBUG(Debug_CheckIsLockedForWrite();) + + // OutputDebugStringA("Creating TypeRef hash\n"); + // Create a new hash. + m_pNamedItemHash = new (nothrow) CMetaDataHashBase; + IfNullGo(m_pNamedItemHash); + IfFailGo(m_pNamedItemHash->NewInit( + g_HashSize[GetMetaDataSizeIndex(&m_OptionValue)])); + + // Scan every entry already in the table, add it to the hash. + for (ULONG index = 1; index <= ridEnd; index++) + { + IfFailGo(m_Tables[ixTbl].GetRecord(index, &pNamedItem)); + IfFailGo(getString(GetCol(ixTbl, g_TblIndex[ixTbl].m_iName, pNamedItem), &szItem)); + if (g_TblIndex[ixTbl].m_iParent != (ULONG) -1) + tkPar = GetToken(ixTbl, g_TblIndex[ixTbl].m_iParent, pNamedItem); + + iHash = HashNamedItem(tkPar, szItem); + + pEntry = m_pNamedItemHash->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = TokenFromRid(index, g_TblIndex[ixTbl].m_Token); + } + } + } + else + { + tk = RidFromToken(tk); + IfFailGo(m_Tables[ixTbl].GetRecord(tk, &pNamedItem)); + IfFailGo(getString(GetCol(ixTbl, g_TblIndex[ixTbl].m_iName, pNamedItem), &szItem)); + if (g_TblIndex[ixTbl].m_iParent != (ULONG)-1) + tkPar = GetToken(ixTbl, g_TblIndex[ixTbl].m_iParent, pNamedItem); + + iHash = HashNamedItem(tkPar, szItem); + + pEntry = m_pNamedItemHash->Add(iHash); + IfNullGo(pEntry); + pEntry->tok = TokenFromRid(tk, g_TblIndex[ixTbl].m_Token); + } + +ErrExit: + return hr; +} // CMiniMdRW::AddNamedItemToHash + +//***************************************************************************** +// If the hash is built, search for the item. +//***************************************************************************** +CMiniMdRW::HashSearchResult +CMiniMdRW::FindNamedItemFromHash( + ULONG ixTbl, // Table with the item. + LPCUTF8 szName, // Name of item. + mdToken tkParent, // Token of parent, if any. + mdToken * ptk) // Return if found. +{ + // If the table is there, look for the item in the chain of items. + if (m_pNamedItemHash != NULL) + { + TOKENHASHENTRY *p; // Hash entry from chain. + ULONG iHash; // Item's hash value. + int pos; // Position in hash chain. + mdToken type; // Type of the item being sought. + + type = g_TblIndex[ixTbl].m_Token; + + // Hash the data. + iHash = HashNamedItem(tkParent, szName); + + // Go through every entry in the hash chain looking for ours. + for (p = m_pNamedItemHash->FindFirst(iHash, pos); + p != NULL; + p = m_pNamedItemHash->FindNext(pos)) + { // Check that the item is from the right table. + if (TypeFromToken(p->tok) != (ULONG)type) + { + //<TODO>@FUTURE: if using the named item hash for multiple tables, remove + // this check. Until then, debugging aid.</TODO> + _ASSERTE(!"Table mismatch in hash chain"); + continue; + } + // Item is in the right table, do the deeper check. + if (CompareNamedItems(ixTbl, p->tok, szName, tkParent) == S_OK) + { + *ptk = p->tok; + return Found; + } + } + + return NotFound; + } + else + { + return NoTable; + } +} // CMiniMdRW::FindNamedItemFromHash + +//***************************************************************************** +// Check a given mr token to see if this one is a match. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::CompareNamedItems( // S_OK match, S_FALSE no match. + ULONG ixTbl, // Table with the item. + mdToken tk, // Token to check. + LPCUTF8 szName, // Name of item. + mdToken tkParent) // Token of parent, if any. +{ + HRESULT hr; + BYTE *pNamedItem; // Item to check. + LPCUTF8 szNameUtf8Tmp; // Name of item to check. + + // Get the record. + IfFailRet(m_Tables[ixTbl].GetRecord(RidFromToken(tk), &pNamedItem)); + + // Name is cheaper to get than coded token parent, and fails pretty quickly. + IfFailRet(getString(GetCol(ixTbl, g_TblIndex[ixTbl].m_iName, pNamedItem), &szNameUtf8Tmp)); + if (strcmp(szNameUtf8Tmp, szName) != 0) + return S_FALSE; + + // Name matched, try parent, if any. + if (g_TblIndex[ixTbl].m_iParent != (ULONG)-1) + { + mdToken tkPar = GetToken(ixTbl, g_TblIndex[ixTbl].m_iParent, pNamedItem); + if (tkPar != tkParent) + return S_FALSE; + } + + // Made it to here, so everything matched. + return S_OK; +} // CMiniMdRW::CompareNamedItems + +//***************************************************************************** +// Add <md, td> entry to the MethodDef map look up table +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddMethodToLookUpTable( + mdMethodDef md, + mdTypeDef td) +{ + HRESULT hr = NOERROR; + mdToken *ptk; + _ASSERTE((TypeFromToken(md) == mdtMethodDef) && HasIndirectTable(TBL_Method)); + + if (m_pMethodMap != NULL) + { + // Only add to the lookup table if it has been built already by demand. + // + // The first entry in the map is a dummy entry. + // The i'th index entry of the map is the td for methoddef of i. + // We do expect the methoddef tokens are all added when the map exist. + // + _ASSERTE(RidFromToken(md) == (ULONG)m_pMethodMap->Count()); + INDEBUG(Debug_CheckIsLockedForWrite();) + ptk = m_pMethodMap->Append(); + IfNullGo(ptk); + *ptk = td; + } +ErrExit: + return hr; +} // CMiniMdRW::AddMethodToLookUpTable + +//***************************************************************************** +// Add <fd, td> entry to the FieldDef map look up table +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddFieldToLookUpTable( + mdFieldDef fd, + mdTypeDef td) +{ + HRESULT hr = NOERROR; + mdToken *ptk; + _ASSERTE((TypeFromToken(fd) == mdtFieldDef) && HasIndirectTable(TBL_Field)); + if (m_pFieldMap != NULL) + { + // Only add to the lookup table if it has been built already by demand. + // + // The first entry in the map is a dummy entry. + // The i'th index entry of the map is the td for fielddef of i. + // We do expect the fielddef tokens are all added when the map exist. + // + _ASSERTE(RidFromToken(fd) == (ULONG)m_pFieldMap->Count()); + ptk = m_pFieldMap->Append(); + IfNullGo(ptk); + *ptk = td; + } + +ErrExit: + return hr; +} // CMiniMdRW::AddFieldToLookUpTable + +//***************************************************************************** +// Add <pr, td> entry to the Property map look up table +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddPropertyToLookUpTable( + mdProperty pr, + mdTypeDef td) +{ + HRESULT hr = NOERROR; + mdToken *ptk; + _ASSERTE((TypeFromToken(pr) == mdtProperty) && HasIndirectTable(TBL_Property)); + + if (m_pPropertyMap != NULL) + { + // Only add to the lookup table if it has been built already by demand. + // + // The first entry in the map is a dummy entry. + // The i'th index entry of the map is the td for property of i. + // We do expect the property tokens are all added when the map exist. + // + _ASSERTE(RidFromToken(pr) == (ULONG)m_pPropertyMap->Count()); + ptk = m_pPropertyMap->Append(); + IfNullGo(ptk); + *ptk = td; + } +ErrExit: + return hr; +} // CMiniMdRW::AddPropertyToLookUpTable + +//***************************************************************************** +// Add <ev, td> entry to the Event map look up table +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddEventToLookUpTable( + mdEvent ev, + mdTypeDef td) +{ + HRESULT hr = NOERROR; + mdToken *ptk; + _ASSERTE((TypeFromToken(ev) == mdtEvent) && HasIndirectTable(TBL_Event)); + + if (m_pEventMap != NULL) + { + // Only add to the lookup table if it has been built already by demand. + // + // now add to the EventMap table + _ASSERTE(RidFromToken(ev) == (ULONG)m_pEventMap->Count()); + ptk = m_pEventMap->Append(); + IfNullGo(ptk); + *ptk = td; + } +ErrExit: + return hr; +} // CMiniMdRW::AddEventToLookUpTable + +//***************************************************************************** +// Add <pd, md> entry to the Param map look up table +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::AddParamToLookUpTable( + mdParamDef pd, + mdMethodDef md) +{ + HRESULT hr = NOERROR; + mdToken *ptk; + _ASSERTE((TypeFromToken(pd) == mdtParamDef) && HasIndirectTable(TBL_Param)); + + if (m_pParamMap != NULL) + { + // Only add to the lookup table if it has been built already by demand. + // + // now add to the EventMap table + _ASSERTE(RidFromToken(pd) == (ULONG)m_pParamMap->Count()); + ptk = m_pParamMap->Append(); + IfNullGo(ptk); + *ptk = md; + } +ErrExit: + return hr; +} // CMiniMdRW::AddParamToLookUpTable + +//***************************************************************************** +// Find parent for a method token. This will use the lookup table if there is an +// intermediate table. Or it will use FindMethodOfParent helper +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindParentOfMethodHelper( + mdMethodDef md, // [IN] the methoddef token + mdTypeDef *ptd) // [OUT] the parent token +{ + HRESULT hr = NOERROR; + if (HasIndirectTable(TBL_Method)) + { + if (m_pMethodMap == NULL) + { + ULONG indexTd; + ULONG indexMd; + ULONG ridStart; + ULONG ridEnd; + TypeDefRec * pTypeDefRec; + MethodPtrRec * pMethodPtrRec; + + // build the MethodMap table + NewHolder<TOKENMAP> pMethodMap = new (nothrow) TOKENMAP; + IfNullGo(pMethodMap); + ULONG nAllocateSize; + if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Method], 1, nAllocateSize)) + { + IfFailGo(COR_E_OVERFLOW); + } + if (pMethodMap->AllocateBlock(nAllocateSize) == 0) + IfFailGo(E_OUTOFMEMORY); + for (indexTd = 1; indexTd <= m_Schema.m_cRecs[TBL_TypeDef]; indexTd++) + { + IfFailGo(GetTypeDefRecord(indexTd, &pTypeDefRec)); + ridStart = getMethodListOfTypeDef(pTypeDefRec); + IfFailGo(getEndMethodListOfTypeDef(indexTd, &ridEnd)); + + for (indexMd = ridStart; indexMd < ridEnd; indexMd++) + { + IfFailGo(GetMethodPtrRecord(indexMd, &pMethodPtrRec)); + PREFIX_ASSUME(pMethodMap->Get(getMethodOfMethodPtr(pMethodPtrRec)) != NULL); + *(pMethodMap->Get(getMethodOfMethodPtr(pMethodPtrRec))) = indexTd; + } + } + if (InterlockedCompareExchangeT<TOKENMAP *>( + &m_pMethodMap, + pMethodMap, + NULL) == NULL) + { // We won the initializaion race + pMethodMap.SuppressRelease(); + } + } + *ptd = *(m_pMethodMap->Get(RidFromToken(md))); + } + else + { + IfFailGo(FindParentOfMethod(RidFromToken(md), (RID *)ptd)); + } + RidToToken(*ptd, mdtTypeDef); +ErrExit: + return hr; +} // CMiniMdRW::FindParentOfMethodHelper + +//***************************************************************************** +// Find parent for a field token. This will use the lookup table if there is an +// intermediate table. Or it will use FindFieldOfParent helper +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindParentOfFieldHelper( + mdFieldDef fd, // [IN] fielddef token + mdTypeDef *ptd) // [OUT] parent token +{ + HRESULT hr = NOERROR; + if (HasIndirectTable(TBL_Field)) + { + if (m_pFieldMap == NULL) + { + ULONG indexTd; + ULONG indexFd; + ULONG ridStart, ridEnd; + TypeDefRec *pTypeDefRec; + FieldPtrRec *pFieldPtrRec; + + // build the FieldMap table + NewHolder<TOKENMAP> pFieldMap = new (nothrow) TOKENMAP; + IfNullGo(pFieldMap); + ULONG nAllocateSize; + if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Field], 1, nAllocateSize)) + { + IfFailGo(COR_E_OVERFLOW); + } + if (pFieldMap->AllocateBlock(nAllocateSize) == 0) + IfFailGo(E_OUTOFMEMORY); + for (indexTd = 1; indexTd<= m_Schema.m_cRecs[TBL_TypeDef]; indexTd++) + { + IfFailGo(GetTypeDefRecord(indexTd, &pTypeDefRec)); + ridStart = getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(getEndFieldListOfTypeDef(indexTd, &ridEnd)); + + for (indexFd = ridStart; indexFd < ridEnd; indexFd++) + { + IfFailGo(GetFieldPtrRecord(indexFd, &pFieldPtrRec)); + PREFIX_ASSUME(pFieldMap->Get(getFieldOfFieldPtr(pFieldPtrRec)) != NULL); + *(pFieldMap->Get(getFieldOfFieldPtr(pFieldPtrRec))) = indexTd; + } + } + if (InterlockedCompareExchangeT<TOKENMAP *>( + &m_pFieldMap, + pFieldMap, + NULL) == NULL) + { // We won the initializaion race + pFieldMap.SuppressRelease(); + } + } + *ptd = *(m_pFieldMap->Get(RidFromToken(fd))); + } + else + { + IfFailGo(FindParentOfField(RidFromToken(fd), (RID *)ptd)); + } + RidToToken(*ptd, mdtTypeDef); +ErrExit: + return hr; +} // CMiniMdRW::FindParentOfFieldHelper + +//***************************************************************************** +// Find parent for a property token. This will use the lookup table if there is an +// intermediate table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindParentOfPropertyHelper( + mdProperty pr, + mdTypeDef *ptd) +{ + HRESULT hr = NOERROR; + if (HasIndirectTable(TBL_Property)) + { + if (m_pPropertyMap == NULL) + { + ULONG indexMap; + ULONG indexPr; + ULONG ridStart, ridEnd; + PropertyMapRec *pPropertyMapRec; + PropertyPtrRec *pPropertyPtrRec; + + // build the PropertyMap table + NewHolder<TOKENMAP> pPropertyMap = new (nothrow) TOKENMAP; + IfNullGo(pPropertyMap); + ULONG nAllocateSize; + if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Property], 1, nAllocateSize)) + { + IfFailGo(COR_E_OVERFLOW); + } + if (pPropertyMap->AllocateBlock(nAllocateSize) == 0) + IfFailGo( E_OUTOFMEMORY ); + for (indexMap = 1; indexMap<= m_Schema.m_cRecs[TBL_PropertyMap]; indexMap++) + { + IfFailGo(GetPropertyMapRecord(indexMap, &pPropertyMapRec)); + ridStart = getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(getEndPropertyListOfPropertyMap(indexMap, &ridEnd)); + + for (indexPr = ridStart; indexPr < ridEnd; indexPr++) + { + IfFailGo(GetPropertyPtrRecord(indexPr, &pPropertyPtrRec)); + mdToken *tok = pPropertyMap->Get(getPropertyOfPropertyPtr(pPropertyPtrRec)); + PREFIX_ASSUME(tok != NULL); + *tok = getParentOfPropertyMap(pPropertyMapRec); + } + } + if (InterlockedCompareExchangeT<TOKENMAP *>( + &m_pPropertyMap, + pPropertyMap, + NULL) == NULL) + { // We won the initializaion race + pPropertyMap.SuppressRelease(); + } + } + *ptd = *(m_pPropertyMap->Get(RidFromToken(pr))); + } + else + { + RID ridPropertyMap; + PropertyMapRec *pRec; + + IfFailGo(FindPropertyMapParentOfProperty(RidFromToken(pr), &ridPropertyMap)); + IfFailGo(GetPropertyMapRecord(ridPropertyMap, &pRec)); + *ptd = getParentOfPropertyMap(pRec); + } + RidToToken(*ptd, mdtTypeDef); +ErrExit: + return hr; +} // CMiniMdRW::FindParentOfPropertyHelper + +//***************************************************************************** +// Find parent for an Event token. This will use the lookup table if there is an +// intermediate table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindParentOfEventHelper( + mdEvent ev, + mdTypeDef *ptd) +{ + HRESULT hr = NOERROR; + if (HasIndirectTable(TBL_Event)) + { + if (m_pEventMap == NULL) + { + ULONG indexMap; + ULONG indexEv; + ULONG ridStart, ridEnd; + EventMapRec *pEventMapRec; + EventPtrRec *pEventPtrRec; + + // build the EventMap table + NewHolder<TOKENMAP> pEventMap = new (nothrow) TOKENMAP; + IfNullGo(pEventMap); + ULONG nAllocateSize; + if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Event], 1, nAllocateSize)) + { + IfFailGo(COR_E_OVERFLOW); + } + if (pEventMap->AllocateBlock(nAllocateSize) == 0) + IfFailGo(E_OUTOFMEMORY); + for (indexMap = 1; indexMap<= m_Schema.m_cRecs[TBL_EventMap]; indexMap++) + { + IfFailGo(GetEventMapRecord(indexMap, &pEventMapRec)); + ridStart = getEventListOfEventMap(pEventMapRec); + IfFailGo(getEndEventListOfEventMap(indexMap, &ridEnd)); + + for (indexEv = ridStart; indexEv < ridEnd; indexEv++) + { + IfFailGo(GetEventPtrRecord(indexEv, &pEventPtrRec)); + mdToken* tok = pEventMap->Get(getEventOfEventPtr(pEventPtrRec)); + PREFIX_ASSUME(tok != NULL); + *tok = getParentOfEventMap(pEventMapRec); + } + } + if (InterlockedCompareExchangeT<TOKENMAP *>( + &m_pEventMap, + pEventMap, + NULL) == NULL) + { // We won the initializaion race + pEventMap.SuppressRelease(); + } + } + *ptd = *(m_pEventMap->Get(RidFromToken(ev))); + } + else + { + RID ridEventMap; + EventMapRec *pRec; + + IfFailGo(FindEventMapParentOfEvent(RidFromToken(ev), &ridEventMap)); + IfFailGo(GetEventMapRecord(ridEventMap, &pRec)); + *ptd = getParentOfEventMap(pRec); + } + RidToToken(*ptd, mdtTypeDef); +ErrExit: + return hr; +} // CMiniMdRW::FindParentOfEventHelper + +//***************************************************************************** +// Find parent for a ParamDef token. This will use the lookup table if there is an +// intermediate table. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::FindParentOfParamHelper( + mdParamDef pd, + mdMethodDef *pmd) +{ + HRESULT hr = NOERROR; + if (HasIndirectTable(TBL_Param)) + { + if (m_pParamMap == NULL) + { + ULONG indexMd; + ULONG indexPd; + ULONG ridStart, ridEnd; + MethodRec *pMethodRec; + ParamPtrRec *pParamPtrRec; + + // build the ParamMap table + NewHolder<TOKENMAP> pParamMap = new (nothrow) TOKENMAP; + IfNullGo(pParamMap); + ULONG nAllocateSize; + if (!ClrSafeInt<ULONG>::addition(m_Schema.m_cRecs[TBL_Param], 1, nAllocateSize)) + { + IfFailGo(COR_E_OVERFLOW); + } + if (pParamMap->AllocateBlock(nAllocateSize) == 0) + IfFailGo(E_OUTOFMEMORY); + for (indexMd = 1; indexMd<= m_Schema.m_cRecs[TBL_Method]; indexMd++) + { + IfFailGo(GetMethodRecord(indexMd, &pMethodRec)); + ridStart = getParamListOfMethod(pMethodRec); + IfFailGo(getEndParamListOfMethod(indexMd, &ridEnd)); + + for (indexPd = ridStart; indexPd < ridEnd; indexPd++) + { + IfFailGo(GetParamPtrRecord(indexPd, &pParamPtrRec)); + PREFIX_ASSUME(pParamMap->Get(getParamOfParamPtr(pParamPtrRec)) != NULL); + *(pParamMap->Get(getParamOfParamPtr(pParamPtrRec))) = indexMd; + } + } + if (InterlockedCompareExchangeT<TOKENMAP *>( + &m_pParamMap, + pParamMap, + NULL) == NULL) + { // We won the initializaion race + pParamMap.SuppressRelease(); + } + } + *pmd = *(m_pParamMap->Get(RidFromToken(pd))); + } + else + { + IfFailGo(FindParentOfParam(RidFromToken(pd), (RID *)pmd)); + } + RidToToken(*pmd, mdtMethodDef); +ErrExit: + return hr; +} // CMiniMdRW::FindParentOfParamHelper + + +//****************************************************************************** +// Add an entry in the ENC Log table. +//****************************************************************************** +__checkReturn +HRESULT +CMiniMdRW::UpdateENCLogHelper( + mdToken tk, // Token to be added to the ENCLog table. + CMiniMdRW::eDeltaFuncs funccode) // Specifies the optional function code.. +{ + ENCLogRec *pRecord; + RID iRecord; + HRESULT hr = S_OK; + + // @todo - MD can't handle anything other than functions right now + /* if (TypeFromToken(tk) != mdtMethodDef) + { + _ASSERTE(!"Trying to do something that we can't do"); + return S_OK; + } + */ + IfFailGo(AddENCLogRecord(&pRecord, &iRecord)); + pRecord->SetToken(tk); + pRecord->SetFuncCode(funccode); + +ErrExit: + return hr; +} // CMiniMdRW::UpdateENCLogHelper + +__checkReturn +HRESULT +CMiniMdRW::UpdateENCLogHelper2( + ULONG ixTbl, // Table being updated. + ULONG iRid, // Record within table. + CMiniMdRW::eDeltaFuncs funccode) // Specifies the optional function code.. +{ + ENCLogRec *pRecord; + RID iRecord; + HRESULT hr = S_OK; + + IfFailGo(AddENCLogRecord(&pRecord, &iRecord)); + pRecord->SetToken(RecIdFromRid(iRid, ixTbl)); + pRecord->SetFuncCode(funccode); + +ErrExit: + return hr; +} // CMiniMdRW::UpdateENCLogHelper2 + +__checkReturn +HRESULT +CMiniMdRW::ResetENCLog() +{ +#ifdef FEATURE_METADATA_EMIT + HRESULT hr = S_OK; + ModuleRec * pMod; + + // Get the module record. + IfFailGo(GetModuleRecord(1, &pMod)); + + + // Reset the pool deltas + m_StringHeap.StartNewEnCSession(); + m_BlobHeap.StartNewEnCSession(); + m_UserStringHeap.StartNewEnCSession(); + + // Clear the ENCLog + m_Tables[TBL_ENCLog].Delete(); + m_Schema.m_cRecs[TBL_ENCLog] = 0; + +ErrExit: + return hr; +#else //!FEATURE_METADATA_EMIT + return S_OK; +#endif //!FEATURE_METADATA_EMIT +} // CMiniMdRW::ResetENCLog + +// ---------------------------------------------------------------------------- +// Workaround for compiler performance issue VSW 584653 for 2.0 RTM. +// Get the table's VirtualSort validity state. +bool +CMiniMdRW::IsTableVirtualSorted(ULONG ixTbl) +{ + _ASSERTE(ixTbl < m_TblCount); + + if (m_pVS[ixTbl] == NULL) + { + return false; + } + return m_pVS[ixTbl]->m_isMapValid; +} // CMiniMdRW::IsTableVirtualSorted + +// ---------------------------------------------------------------------------- +// Workaround for compiler performance issue VSW 584653 for 2.0 RTM. +// +// Validate table's VirtualSort after adding one record into the table. +// Returns new VirtualSort validity state in *pfIsTableVirtualSortValid. +// Assumptions: +// Table's VirtualSort was valid before adding the record to the table. +// The caller must ensure validity of VirtualSort by calling to +// IsTableVirtualSorted or by using the returned state from previous +// call to this method. +__checkReturn +HRESULT +CMiniMdRW::ValidateVirtualSortAfterAddRecord( + ULONG ixTbl, + bool *pfIsTableVirtualSortValid) +{ + _ASSERTE(ixTbl < m_TblCount); + + HRESULT hr; + VirtualSort *pVS = m_pVS[ixTbl]; + + // VirtualSort was valid (had to exist) + _ASSERTE(pVS != NULL); + // Adding record invalidated VirtualSort + _ASSERTE(!pVS->m_isMapValid); + // Only 1 record was added into table (VirtualSort has 1 bogus element) + _ASSERTE(m_Schema.m_cRecs[ixTbl] == (ULONG)pVS->m_pMap->Count()); + + // Append 1 element into VirtualSort + mdToken *pAddedVSToken = pVS->m_pMap->Append(); + if (pAddedVSToken == NULL) + { // There's not enough memory + // Do not handle OOM now, just leave the VirtualSort invalidated, the + // next allocation will take care of OOM or the VirtualSort will be + // resorted when needed (as it was before this performance workaround) + *pfIsTableVirtualSortValid = false; + return S_OK; + } + + // Initialize added element + int iLastElementIndex = pVS->m_pMap->Count() - 1; + *pAddedVSToken = iLastElementIndex; + // Check if the added element extends the VirtualSort (keeps sorting) + if (iLastElementIndex > 2) + { + int nCompareResult; + IfFailRet(pVS->Compare( + iLastElementIndex - 1, + iLastElementIndex, + &nCompareResult)); + if (nCompareResult < 0) + { // VirtualSort was extended - the added element is bigger than + // previously last element in VirtualSort + + // Validate VirtualSort as it is still sorted and covers all elements + // of the MetaData table + pVS->m_isMapValid = true; + *pfIsTableVirtualSortValid = true; + return S_OK; + } + } + // The added element doesn't extend VirtualSort - it is not sorted + + // Keep the VirtualSort invalidated, therefore next binary search will + // force its recreation and resorting (as it did before this performance + // workaround) + *pfIsTableVirtualSortValid = false; + return S_OK; +} // CMiniMdRW::ValidateVirtualSortAfterAddRecord + +#ifdef _DEBUG + +// ---------------------------------------------------------------------------- +void +CMiniMdRW::Debug_CheckIsLockedForWrite() +{ + // If this assert fires, then we are trying to modify MetaData that is not locked for write + _ASSERTE((dbg_m_pLock == NULL) || dbg_m_pLock->Debug_IsLockedForWrite()); +} + +#endif //_DEBUG + +//***************************************************************************** +// +// Sort the whole RID table +// +//***************************************************************************** +__checkReturn +HRESULT +VirtualSort::Sort() +{ + m_isMapValid = true; + // Note that m_pMap stores an additional bogus element at count 0. This is + // just so we can align the index in m_pMap with the Rids which are 1 based. + return SortRange(1, m_pMap->Count() - 1); +} // VirtualSort::Sort + +//***************************************************************************** +// +// Sort the range from iLeft to iRight +// +//***************************************************************************** +__checkReturn +HRESULT +VirtualSort::SortRange( + int iLeft, + int iRight) +{ + HRESULT hr; + int iLast; + + for (;;) + { + // if less than two elements you're done. + if (iLeft >= iRight) + { + return S_OK; + } + + // The mid-element is the pivot, move it to the left. + Swap(iLeft, (iLeft+iRight)/2); + iLast = iLeft; + + // move everything that is smaller than the pivot to the left. + for (int i = iLeft+1; i <= iRight; i++) + { + int nCompareResult; + IfFailRet(Compare(i, iLeft, &nCompareResult)); + if (nCompareResult < 0) + { + Swap(i, ++iLast); + } + } + + // Put the pivot to the point where it is in between smaller and larger elements. + Swap(iLeft, iLast); + + // Sort each partition. + int iLeftLast = iLast - 1; + int iRightFirst = iLast + 1; + if (iLeftLast - iLeft < iRight - iRightFirst) + { // Left partition is smaller, sort it recursively + IfFailRet(SortRange(iLeft, iLeftLast)); + // Tail call to sort the right (bigger) partition + iLeft = iRightFirst; + //iRight = iRight; + continue; + } + else + { // Right partition is smaller, sort it recursively + IfFailRet(SortRange(iRightFirst, iRight)); + // Tail call to sort the left (bigger) partition + //iLeft = iLeft; + iRight = iLeftLast; + continue; + } + } +} // VirtualSort::SortRange + +//***************************************************************************** +// +// Compare two RID base on the m_ixTbl's m_ixCol +// +//***************************************************************************** +__checkReturn +HRESULT +VirtualSort::Compare( + RID iLeft, // First item to compare. + RID iRight, // Second item to compare. + int *pnResult) // -1, 0, or 1 +{ + HRESULT hr; + RID ridLeft = *(m_pMap->Get(iLeft)); + RID ridRight = *(m_pMap->Get(iRight)); + void *pRow; // Row from a table. + ULONG valRight, valLeft; // Value from a row. + + IfFailRet(m_pMiniMd->getRow(m_ixTbl, ridLeft, &pRow)); + valLeft = m_pMiniMd->getIX(pRow, m_pMiniMd->m_TableDefs[m_ixTbl].m_pColDefs[m_ixCol]); + IfFailRet(m_pMiniMd->getRow(m_ixTbl, ridRight, &pRow)); + valRight = m_pMiniMd->getIX(pRow, m_pMiniMd->m_TableDefs[m_ixTbl].m_pColDefs[m_ixCol]); + + if (valLeft < valRight) + { + *pnResult = -1; + return S_OK; + } + if (valLeft > valRight) + { + *pnResult = 1; + return S_OK; + } + // Values are equal -- preserve existing ordering. + if (ridLeft < ridRight) + { + *pnResult = -1; + return S_OK; + } + if (ridLeft > ridRight) + { + *pnResult = 1; + return S_OK; + } + // Comparing an item to itself? + _ASSERTE(!"Comparing an item to itself in sort"); + + *pnResult = 0; + return S_OK; +} // VirtualSort::Compare + +//***************************************************************************** +// +// Initialization function +// +//***************************************************************************** +void VirtualSort::Init( // + ULONG ixTbl, // Table index. + ULONG ixCol, // Column index. + CMiniMdRW *pMiniMd) // MiniMD with data. +{ + m_pMap = NULL; + m_isMapValid = false; + m_ixTbl = ixTbl; + m_ixCol = ixCol; + m_pMiniMd = pMiniMd; +} // VirtualSort::Init + + +//***************************************************************************** +// +// Uninitialization function +// +//***************************************************************************** +void VirtualSort::Uninit() +{ + if ( m_pMap ) + delete m_pMap; + m_pMap = NULL; + m_isMapValid = false; +} // VirtualSort::Uninit + + +//***************************************************************************** +// +// Mark a token +// +//***************************************************************************** +HRESULT FilterTable::MarkToken( + mdToken tk, // token to be marked as to keep + DWORD bitToMark) // bit flag to set in the keep table +{ + HRESULT hr = NOERROR; + RID rid = RidFromToken(tk); + + if ( (Count() == 0) || ((RID)(Count() -1)) < rid ) + { + // grow table + IfFailGo( AllocateBlock( rid + 1 - Count() ) ); + } + +#ifdef _DEBUG + if ( (*Get(rid)) & bitToMark ) + { + // global TypeDef could be marked more than once so don't assert if token is mdtTypeDef + if (TypeFromToken(tk) != mdtTypeDef) + _ASSERTE(!"Token has been Marked"); + } +#endif //_DEBUG + + // set the keep bit + *Get(rid) = (*Get(rid)) | bitToMark; +ErrExit: + return hr; +} // FilterTable::MarkToken + + +//***************************************************************************** +// +// Unmark a token +// +//***************************************************************************** +HRESULT FilterTable::UnmarkToken( + mdToken tk, // token to be unmarked as deleted. + DWORD bitToMark) // bit flag to unset in the keep table +{ + RID rid = RidFromToken(tk); + + if ( (Count() == 0) || ((RID)(Count() -1)) < rid ) + { + // unmarking should not have grown table. It currently only support dropping the transient CAs. + _ASSERTE(!"BAD state!"); + } + +#ifdef _DEBUG + if ( (*Get(rid)) & bitToMark ) + { + // global TypeDef could be marked more than once so don't assert if token is mdtTypeDef + if (TypeFromToken(tk) != mdtTypeDef) + _ASSERTE(!"Token has been Marked"); + } +#endif //_DEBUG + + // unset the keep bit + *Get(rid) = (*Get(rid)) & ~bitToMark; + return NOERROR; +} // FilterTable::MarkToken + + +//***************************************************************************** +// +// Mark an UserString token +// +//***************************************************************************** +HRESULT FilterTable::MarkUserString( + mdString str) +{ + int high, low, mid; + + low = 0; + high = m_daUserStringMarker->Count() - 1; + while (low <= high) + { + mid = (high + low) / 2; + if ((m_daUserStringMarker->Get(mid))->m_tkString > (DWORD) str) + { + high = mid - 1; + } + else if ((m_daUserStringMarker->Get(mid))->m_tkString < (DWORD) str) + { + low = mid + 1; + } + else + { + (m_daUserStringMarker->Get(mid))->m_fMarked = true; + return NOERROR; + } + } + _ASSERTE(!"Bad Token!"); + return NOERROR; +} // FilterTable::MarkUserString + +//***************************************************************************** +// +// Mark a UserString token that was added since our last MarkAll/UnMarkAll +// +//***************************************************************************** +HRESULT FilterTable::MarkNewUserString(mdString str) +{ + FilterUserStringEntry *pItem = m_daUserStringMarker->Append(); + + if (pItem == NULL) + return E_OUTOFMEMORY; + + pItem->m_tkString = str; + pItem->m_fMarked = true; + + return S_OK; +} // FilterTable::MarkNewUserString + +//***************************************************************************** +// +// Unmarking from 1 to ulSize for all tokens. +// +//***************************************************************************** +HRESULT FilterTable::UnmarkAll( + CMiniMdRW *pMiniMd, + ULONG ulSize) +{ + HRESULT hr; + + S_UINT32 nAllocateSize = S_UINT32(ulSize) + S_UINT32(1); + if (nAllocateSize.IsOverflow()) + { + IfFailGo(COR_E_OVERFLOW); + } + if (!AllocateBlock(nAllocateSize.Value())) + { + IfFailGo(E_OUTOFMEMORY); + } + memset(Get(0), 0, nAllocateSize.Value() * sizeof(DWORD)); + + // unmark all of the user string + m_daUserStringMarker = new (nothrow) CDynArray<FilterUserStringEntry>(); + IfNullGo(m_daUserStringMarker); + + for (UINT32 nIndex = 0; ;) + { + MetaData::DataBlob userString; + UINT32 nNextIndex; + hr = pMiniMd->GetUserStringAndNextIndex( + nIndex, + &userString, + &nNextIndex); + IfFailGo(hr); + if (hr == S_FALSE) + { // We reached the last user string + hr = S_OK; + break; + } + _ASSERTE(hr == S_OK); + + // Skip empty strings + if (userString.IsEmpty()) + { + nIndex = nNextIndex; + continue; + } + FilterUserStringEntry *pItem = m_daUserStringMarker->Append(); + pItem->m_tkString = TokenFromRid(nIndex, mdtString); + pItem->m_fMarked = false; + + // Process next user string in the heap + nIndex = nNextIndex; + } + +ErrExit: + return hr; +} // FilterTable::UnmarkAll + + + +//***************************************************************************** +// +// Marking from 1 to ulSize for all tokens. +// +//***************************************************************************** +HRESULT FilterTable::MarkAll( + CMiniMdRW *pMiniMd, + ULONG ulSize) +{ + HRESULT hr = S_OK; + + S_UINT32 nAllocateSize = S_UINT32(ulSize) + S_UINT32(1); + if (nAllocateSize.IsOverflow()) + { + IfFailGo(COR_E_OVERFLOW); + } + if (!AllocateBlock(nAllocateSize.Value())) + { + IfFailGo(E_OUTOFMEMORY); + } + memset(Get(0), 0xFFFFFFFF, nAllocateSize.Value() * sizeof(DWORD)); + + // mark all of the user string + m_daUserStringMarker = new (nothrow) CDynArray<FilterUserStringEntry>(); + IfNullGo(m_daUserStringMarker); + + for (UINT32 nIndex = 0; ;) + { + MetaData::DataBlob userString; + UINT32 nNextIndex; + hr = pMiniMd->GetUserStringAndNextIndex( + nIndex, + &userString, + &nNextIndex); + IfFailGo(hr); + if (hr == S_FALSE) + { // We reached the last user string + hr = S_OK; + break; + } + _ASSERTE(hr == S_OK); + + // Skip empty strings + if (userString.IsEmpty()) + { + nIndex = nNextIndex; + continue; + } + FilterUserStringEntry *pItem = m_daUserStringMarker->Append(); + pItem->m_tkString = TokenFromRid(nIndex, mdtString); + pItem->m_fMarked = true; + + // Process next user string in the heap + nIndex = nNextIndex; + } + +ErrExit: + return hr; +} // FilterTable::MarkAll + +//***************************************************************************** +// +// return true if a token is marked. Otherwise return false. +// +//***************************************************************************** +bool FilterTable::IsTokenMarked( + mdToken tk, // Token to inquiry + DWORD bitMarked) // bit flag to check in the deletion table +{ + RID rid = RidFromToken(tk); + + //<TODO>@FUTURE: inconsistency!!! + // If caller unmarked everything while the module has 2 typedef and 10 methodef. + // We will have 11 rows in the FilterTable. Then user add the 3 typedef, it is + // considered unmarked unless we mark it when we do DefineTypeDef. However, if user + // add another MethodDef, it will be considered marked unless we unmarked..... + // Maybe the solution is not to support DefineXXXX if you use the filter interface??</TODO> + + if ( (Count() == 0) || ((RID)(Count() - 1)) < rid ) + { + // If UnmarkAll has never been called or tk is added after UnmarkAll, + // tk is considered marked. + // + return true; + } + return ( (*Get(rid)) & bitMarked ? true : false); +} // FilterTable::IsTokenMarked + + +//***************************************************************************** +// +// return true if a token is marked. Otherwise return false. +// +//***************************************************************************** +bool FilterTable::IsTokenMarked( + mdToken tk) // Token to inquiry +{ + + switch ( TypeFromToken(tk) ) + { + case mdtTypeRef: + return IsTypeRefMarked(tk); + case mdtTypeDef: + return IsTypeDefMarked(tk); + case mdtFieldDef: + return IsFieldMarked(tk); + case mdtMethodDef: + return IsMethodMarked(tk); + case mdtParamDef: + return IsParamMarked(tk); + case mdtMemberRef: + return IsMemberRefMarked(tk); + case mdtCustomAttribute: + return IsCustomAttributeMarked(tk); + case mdtPermission: + return IsDeclSecurityMarked(tk); + case mdtSignature: + return IsSignatureMarked(tk); + case mdtEvent: + return IsEventMarked(tk); + case mdtProperty: + return IsPropertyMarked(tk); + case mdtModuleRef: + return IsModuleRefMarked(tk); + case mdtTypeSpec: + return IsTypeSpecMarked(tk); + case mdtInterfaceImpl: + return IsInterfaceImplMarked(tk); + case mdtMethodSpec: + return IsMethodSpecMarked(tk); + case mdtString: + return IsUserStringMarked(tk); + default: + _ASSERTE(!"Bad token type!"); + break; + } + return false; +} // FilterTable::IsTokenMarked + +//***************************************************************************** +// +// return true if an UserString is marked. +// +//***************************************************************************** +bool FilterTable::IsUserStringMarked(mdString str) +{ + int low, mid, high, count; + + // if m_daUserStringMarker is not created, UnmarkAll has never been called + if (m_daUserStringMarker == NULL) + return true; + + low = 0; + count = m_daUserStringMarker->Count(); + + if (count == 0) + { + // No strings are marked. + return false; + } + + high = m_daUserStringMarker->Count() - 1; + + while (low <= high) + { + mid = (high + low) / 2; + if ((m_daUserStringMarker->Get(mid))->m_tkString > (DWORD) str) + { + high = mid - 1; + } + else if ((m_daUserStringMarker->Get(mid))->m_tkString < (DWORD) str) + { + low = mid + 1; + } + else + { + return (m_daUserStringMarker->Get(mid))->m_fMarked; + } + } + _ASSERTE(!"Bad Token!"); + return false; +} // FilterTable::IsUserStringMarked + + + +//***************************************************************************** +// +// destructor +// +//***************************************************************************** +FilterTable::~FilterTable() +{ + if (m_daUserStringMarker) + delete m_daUserStringMarker; + Clear(); +} // FilterTable::~FilterTable + diff --git a/src/coreclr/md/enc/pdbheap.cpp b/src/coreclr/md/enc/pdbheap.cpp new file mode 100644 index 00000000000..962472ac65a --- /dev/null +++ b/src/coreclr/md/enc/pdbheap.cpp @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "stdafx.h" +#include "pdbheap.h" + +PdbHeap::PdbHeap() : m_data(NULL), m_size(0) +{ +} + +PdbHeap::~PdbHeap() +{ + if (m_data != NULL) + { + delete[] m_data; + m_data = NULL; + } +} + +__checkReturn +HRESULT PdbHeap::SetData(PORT_PDB_STREAM* data) +{ + m_size = sizeof(data->id) + + sizeof(data->entryPoint) + + sizeof(data->referencedTypeSystemTables) + + (sizeof(ULONG) * data->typeSystemTableRowsSize); + m_data = new BYTE[m_size]; + + ULONG offset = 0; + if (memcpy_s(m_data + offset, m_size, &data->id, sizeof(data->id))) + return E_FAIL; + offset += sizeof(data->id); + + if (memcpy_s(m_data + offset, m_size, &data->entryPoint, sizeof(data->entryPoint))) + return E_FAIL; + offset += sizeof(data->entryPoint); + + if (memcpy_s(m_data + offset, m_size, &data->referencedTypeSystemTables, sizeof(data->referencedTypeSystemTables))) + return E_FAIL; + offset += sizeof(data->referencedTypeSystemTables); + + if (memcpy_s(m_data + offset, m_size, data->typeSystemTableRows, sizeof(ULONG) * data->typeSystemTableRowsSize)) + return E_FAIL; + offset += sizeof(ULONG) * data->typeSystemTableRowsSize; + + _ASSERTE(offset == m_size); + + return S_OK; +} + +__checkReturn +HRESULT PdbHeap::SaveToStream(IStream* stream) +{ + HRESULT hr = S_OK; + if (!IsEmpty()) + { + ULONG written = 0; + hr = stream->Write(m_data, m_size, &written); + _ASSERTE(m_size == written); + } + return hr; +} + +BOOL PdbHeap::IsEmpty() +{ + return m_size == 0; +} + +ULONG PdbHeap::GetSize() +{ + return m_size; +} diff --git a/src/coreclr/md/enc/peparse.cpp b/src/coreclr/md/enc/peparse.cpp new file mode 100644 index 00000000000..b5bff4ed164 --- /dev/null +++ b/src/coreclr/md/enc/peparse.cpp @@ -0,0 +1,147 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "stdafx.h" + +#include <windows.h> +#include <corhdr.h> +#include "corerror.h" +#include "pedecoder.h" + + +static const char g_szCORMETA[] = ".cormeta"; + + +HRESULT CLiteWeightStgdbRW::FindImageMetaData(PVOID pImage, DWORD dwFileLength, BOOL bMappedImage, PVOID *ppMetaData, ULONG *pcbMetaData) +{ +#ifndef DACCESS_COMPILE + PEDecoder pe; + + // We need to use different PEDecoder initialization based on the type of data we give it. + // We use the one with a 'bool' as the second argument when dealing with a mapped file, + // and we use the one that takes a COUNT_T as the second argument when dealing with a + // flat file. + if (bMappedImage) + { + if (FAILED(pe.Init(pImage, false)) || + !pe.CheckNTHeaders()) + { + return COR_E_BADIMAGEFORMAT; + } + } + else + { + pe.Init(pImage, (COUNT_T)dwFileLength); + } + + // Minimally validate image + if (!pe.CheckCorHeader()) + return COR_E_BADIMAGEFORMAT; + + + COUNT_T size = 0; + + *ppMetaData = (void *)pe.GetMetadata(&size); + + // Couldn't find any IL metadata in this image + if (*ppMetaData == NULL) + return CLDB_E_NO_DATA; + + if (pcbMetaData != NULL) + *pcbMetaData = size; + + return S_OK; +#else + DacNotImpl(); + return E_NOTIMPL; +#endif +} // CLiteWeightStgdbRW::FindImageMetaData + +// +// Note: Remove once defined in winnt.h +// +typedef struct ANON_OBJECT_HEADER2 { + WORD Sig1; // Must be IMAGE_FILE_MACHINE_UNKNOWN + WORD Sig2; // Must be 0xffff + WORD Version; // >= 2 (implies the CLSID field, Flags and metadata info are present) + WORD Machine; + DWORD TimeDateStamp; + CLSID ClassID; // Used to invoke CoCreateInstance + DWORD SizeOfData; // Size of data that follows the header + DWORD Flags; + DWORD MetaDataSize; // Size of CLR metadata + DWORD MetaDataOffset; // Offset of CLR metadata +} ANON_OBJECT_HEADER2; + +#define ANON_OBJECT_HAS_CORMETA 0x00000001 +#define ANON_OBJECT_IS_PUREMSIL 0x00000002 + +HRESULT CLiteWeightStgdbRW::FindObjMetaData(PVOID pImage, DWORD dwFileLength, PVOID *ppMetaData, ULONG *pcbMetaData) +{ + DWORD dwSize = 0; + DWORD dwOffset = 0; + + ANON_OBJECT_HEADER2 *pAnonImageHdr = (ANON_OBJECT_HEADER2 *) pImage; // Anonymous object header + + // Check to see if this is a LTCG object + if (dwFileLength >= sizeof(ANON_OBJECT_HEADER2) && + pAnonImageHdr->Sig1 == VAL16(IMAGE_FILE_MACHINE_UNKNOWN) && + pAnonImageHdr->Sig2 == VAL16(IMPORT_OBJECT_HDR_SIG2)) + { + // Version 1 anonymous objects don't have metadata info + if (VAL16(pAnonImageHdr->Version) < 2) + goto BadFormat; + + // Anonymous objects contain the metadata info in the header + dwOffset = VAL32(pAnonImageHdr->MetaDataOffset); + dwSize = VAL32(pAnonImageHdr->MetaDataSize); + } + else + { + // Check to see if we have enough data + if (dwFileLength < sizeof(IMAGE_FILE_HEADER)) + goto BadFormat; + + IMAGE_FILE_HEADER *pImageHdr = (IMAGE_FILE_HEADER *) pImage; // Header for the .obj file. + + // Walk each section looking for .cormeta. + DWORD nSections = VAL16(pImageHdr->NumberOfSections); + + // Check to see if we have enough data + S_UINT32 nSectionsSize = S_UINT32(sizeof(IMAGE_FILE_HEADER)) + S_UINT32(nSections) * S_UINT32(sizeof(IMAGE_SECTION_HEADER)); + if (nSectionsSize.IsOverflow() || (dwFileLength < nSectionsSize.Value())) + goto BadFormat; + + IMAGE_SECTION_HEADER *pSectionHdr = (IMAGE_SECTION_HEADER *)(pImageHdr + 1); // Section header. + + for (DWORD i=0; i<nSections; i++, pSectionHdr++) + { + // Simple comparison to section name. + if (memcmp((const char *) pSectionHdr->Name, g_szCORMETA, sizeof(pSectionHdr->Name)) == 0) + { + dwOffset = VAL32(pSectionHdr->PointerToRawData); + dwSize = VAL32(pSectionHdr->SizeOfRawData); + break; + } + } + } + + if (dwOffset == 0 || dwSize == 0) + goto BadFormat; + + // Check that raw data in the section is actually within the file. + { + S_UINT32 dwEndOffset = S_UINT32(dwOffset) + S_UINT32(dwSize); + if ((dwOffset >= dwFileLength) || dwEndOffset.IsOverflow() || (dwEndOffset.Value() > dwFileLength)) + goto BadFormat; + } + + *ppMetaData = (PVOID) ((ULONG_PTR) pImage + dwOffset); + *pcbMetaData = dwSize; + return (S_OK); + +BadFormat: + *ppMetaData = NULL; + *pcbMetaData = 0; + return (COR_E_BADIMAGEFORMAT); +} diff --git a/src/coreclr/md/enc/rwutil.cpp b/src/coreclr/md/enc/rwutil.cpp new file mode 100644 index 00000000000..e7aed761c25 --- /dev/null +++ b/src/coreclr/md/enc/rwutil.cpp @@ -0,0 +1,1290 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RWUtil.cpp +// + +// +// contains utility code to MD directory +// +//***************************************************************************** +#include "stdafx.h" +#include "metadata.h" +#include "rwutil.h" +#include "utsem.h" +#include "../inc/mdlog.h" + +//***************************************************************************** +// Helper methods +//***************************************************************************** +void +Unicode2UTF( + LPCWSTR wszSrc, // The string to convert. + __out_ecount(cbDst) + LPUTF8 szDst, // Buffer for the output UTF8 string. + int cbDst) // Size of the buffer for UTF8 string. +{ + int cchSrc = (int)wcslen(wszSrc); + int cchRet; + + cchRet = WszWideCharToMultiByte( + CP_UTF8, + 0, + wszSrc, + cchSrc + 1, + szDst, + cbDst, + NULL, + NULL); + + if (cchRet == 0) + { + _ASSERTE_MSG(FALSE, "Converting unicode string to UTF8 string failed!"); + szDst[0] = '\0'; + } +} // Unicode2UTF + + +HRESULT HENUMInternal::CreateSimpleEnum( + DWORD tkKind, // kind of token that we are iterating + ULONG ridStart, // starting rid + ULONG ridEnd, // end rid + HENUMInternal **ppEnum) // return the created HENUMInternal +{ + HENUMInternal *pEnum; + HRESULT hr = NOERROR; + + // Don't create an empty enum. + if (ridStart >= ridEnd) + { + *ppEnum = 0; + goto ErrExit; + } + + pEnum = new (nothrow) HENUMInternal; + + // check for out of memory error + if (pEnum == NULL) + IfFailGo( E_OUTOFMEMORY ); + + HENUMInternal::ZeroEnum(pEnum); + pEnum->m_tkKind = tkKind; + pEnum->m_EnumType = MDSimpleEnum; + pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart; + pEnum->u.m_ulEnd = ridEnd; + pEnum->m_ulCount = ridEnd - ridStart; + + *ppEnum = pEnum; +ErrExit: + return hr; + +} // CreateSimpleEnum + + +//***************************************************************************** +// Helper function to destroy Enumerator +//***************************************************************************** +void HENUMInternal::DestroyEnum( + HENUMInternal *pmdEnum) +{ + if (pmdEnum == NULL) + return; + + if (pmdEnum->m_EnumType == MDDynamicArrayEnum) + { + TOKENLIST *pdalist; + pdalist = (TOKENLIST *) &(pmdEnum->m_cursor); + + // clear the embedded dynamic array before we delete the enum + pdalist->Clear(); + } + delete pmdEnum; +} // DestroyEnum + + +//***************************************************************************** +// Helper function to destroy Enumerator if the enumerator is empty +//***************************************************************************** +void HENUMInternal::DestroyEnumIfEmpty( + HENUMInternal **ppEnum) // reset the enumerator pointer to NULL if empty +{ + + if (*ppEnum == NULL) + return; + + if ((*ppEnum)->m_ulCount == 0) + { + HENUMInternal::DestroyEnum(*ppEnum); + *ppEnum = NULL; + } +} // DestroyEnumIfEmpty + + +void HENUMInternal::ClearEnum( + HENUMInternal *pmdEnum) +{ + if (pmdEnum == NULL) + return; + + if (pmdEnum->m_EnumType == MDDynamicArrayEnum) + { + TOKENLIST *pdalist; + pdalist = (TOKENLIST *) &(pmdEnum->m_cursor); + + // clear the embedded dynamic array before we delete the enum + pdalist->Clear(); + } +} // ClearEnum + + +//***************************************************************************** +// Helper function to iterate the enum +//***************************************************************************** +bool HENUMInternal::EnumNext( + HENUMInternal *phEnum, // [IN] the enumerator to retrieve information + mdToken *ptk) // [OUT] token to scope the search +{ + _ASSERTE(phEnum && ptk); + + if (phEnum->u.m_ulCur >= phEnum->u.m_ulEnd) + return false; + + if ( phEnum->m_EnumType == MDSimpleEnum ) + { + *ptk = phEnum->u.m_ulCur | phEnum->m_tkKind; + phEnum->u.m_ulCur++; + } + else + { + TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor); + + _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum ); + *ptk = *( pdalist->Get(phEnum->u.m_ulCur++) ); + } + return true; +} // EnumNext + +//***************************************************************************** +// Number of items in the enumerator. +//***************************************************************************** +HRESULT HENUMInternal::GetCount( + HENUMInternal *phEnum, // [IN] the enumerator to retrieve information + ULONG *pCount) // ]OUT] the index of the desired item +{ + // Check for empty enum. + if (phEnum == 0) + return S_FALSE; + + *pCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart; + return S_OK; +} + +//***************************************************************************** +// Get a specific element. +//***************************************************************************** +HRESULT HENUMInternal::GetElement( + HENUMInternal *phEnum, // [IN] the enumerator to retrieve information + ULONG ix, // ]IN] the index of the desired item + mdToken *ptk) // [OUT] token to fill +{ + // Check for empty enum. + if (phEnum == 0) + return S_FALSE; + + if (ix > (phEnum->u.m_ulEnd - phEnum->u.m_ulStart)) + return S_FALSE; + + if ( phEnum->m_EnumType == MDSimpleEnum ) + { + *ptk = (phEnum->u.m_ulStart + ix) | phEnum->m_tkKind; + } + else + { + TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor); + + _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum ); + *ptk = *( pdalist->Get(ix) ); + } + + return S_OK; +} + +//***************************************************************************** +// Helper function to fill output token buffers given an enumerator +//***************************************************************************** +HRESULT HENUMInternal::EnumWithCount( + HENUMInternal *pEnum, // enumerator + ULONG cMax, // max tokens that caller wants + mdToken rTokens[], // output buffer to fill the tokens + ULONG *pcTokens) // number of tokens fill to the buffer upon return +{ + ULONG cTokens; + HRESULT hr = NOERROR; + + // Check for empty enum. + if (pEnum == 0) + { + if (pcTokens) + *pcTokens = 0; + return S_FALSE; + } + + // we can only fill the minimun of what caller asked for or what we have left + cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax); + + if (pEnum->m_EnumType == MDSimpleEnum) + { + + // now fill the output + for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++) + { + rTokens[i] = TokenFromRid(pEnum->u.m_ulCur, pEnum->m_tkKind); + } + + } + else + { + // cannot be any other kind! + _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum ); + + // get the embedded dynamic array + TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor); + + for (ULONG i = 0; i < cTokens; i ++, pEnum->u.m_ulCur++) + { + rTokens[i] = *( pdalist->Get(pEnum->u.m_ulCur) ); + } + } + + if (pcTokens) + *pcTokens = cTokens; + + if (cTokens == 0) + hr = S_FALSE; + return hr; +} // EnumWithCount + + +//***************************************************************************** +// Helper function to fill output token buffers given an enumerator +// This is a variation that takes two output arrays. The tokens in the +// enumerator are interleaved, one for each array. This is currently used by +// EnumMethodImpl which needs to return two arrays. +//***************************************************************************** +HRESULT HENUMInternal::EnumWithCount( + HENUMInternal *pEnum, // enumerator + ULONG cMax, // max tokens that caller wants + mdToken rTokens1[], // first output buffer to fill the tokens + mdToken rTokens2[], // second output buffer to fill the tokens + ULONG *pcTokens) // number of tokens fill to each buffer upon return +{ + ULONG cTokens; + HRESULT hr = NOERROR; + + // cannot be any other kind! + _ASSERTE( pEnum->m_EnumType == MDDynamicArrayEnum ); + + // Check for empty enum. + if (pEnum == 0) + { + if (pcTokens) + *pcTokens = 0; + return S_FALSE; + } + + // Number of tokens must always be a multiple of 2. + _ASSERTE(! ((pEnum->u.m_ulEnd - pEnum->u.m_ulCur) % 2) ); + + // we can only fill the minimun of what caller asked for or what we have left + cTokens = min ( (pEnum->u.m_ulEnd - pEnum->u.m_ulCur), cMax * 2); + + // get the embedded dynamic array + TOKENLIST *pdalist = (TOKENLIST *)&(pEnum->m_cursor); + + for (ULONG i = 0; i < (cTokens / 2); i++) + { + rTokens1[i] = *( pdalist->Get(pEnum->u.m_ulCur++) ); + rTokens2[i] = *( pdalist->Get(pEnum->u.m_ulCur++) ); + } + + if (pcTokens) + *pcTokens = cTokens / 2; + + if (cTokens == 0) + hr = S_FALSE; + return hr; +} // EnumWithCount + + +//***************************************************************************** +// Helper function to create HENUMInternal +//***************************************************************************** +HRESULT HENUMInternal::CreateDynamicArrayEnum( + DWORD tkKind, // kind of token that we are iterating + HENUMInternal **ppEnum) // return the created HENUMInternal +{ + HENUMInternal *pEnum; + HRESULT hr = NOERROR; + TOKENLIST *pdalist; + + pEnum = new (nothrow) HENUMInternal; + + // check for out of memory error + if (pEnum == NULL) + IfFailGo( E_OUTOFMEMORY ); + + HENUMInternal::ZeroEnum(pEnum); + pEnum->m_tkKind = tkKind; + pEnum->m_EnumType = MDDynamicArrayEnum; + + // run the constructor in place + pdalist = (TOKENLIST *) &(pEnum->m_cursor); + ::new (pdalist) TOKENLIST; + + *ppEnum = pEnum; +ErrExit: + return hr; + +} // _CreateDynamicArrayEnum + + + +//***************************************************************************** +// Helper function to init HENUMInternal +//***************************************************************************** +void HENUMInternal::InitDynamicArrayEnum( + HENUMInternal *pEnum) // HENUMInternal to be initialized +{ + TOKENLIST *pdalist; + + HENUMInternal::ZeroEnum(pEnum); + pEnum->m_EnumType = MDDynamicArrayEnum; + pEnum->m_tkKind = (DWORD) -1; + + // run the constructor in place + pdalist = (TOKENLIST *) &(pEnum->m_cursor); + ::new (pdalist) TOKENLIST; +} // CreateDynamicArrayEnum + + +//***************************************************************************** +// Helper function to init HENUMInternal +//***************************************************************************** +void HENUMInternal::InitSimpleEnum( + DWORD tkKind, // kind of token that we are iterating + ULONG ridStart, // starting rid + ULONG ridEnd, // end rid + HENUMInternal *pEnum) // HENUMInternal to be initialized +{ + pEnum->m_EnumType = MDSimpleEnum; + pEnum->m_tkKind = tkKind; + pEnum->u.m_ulStart = pEnum->u.m_ulCur = ridStart; + pEnum->u.m_ulEnd = ridEnd; + pEnum->m_ulCount = ridEnd - ridStart; + +} // InitSimpleEnum + + + + +//***************************************************************************** +// Helper function to init HENUMInternal +//***************************************************************************** +HRESULT HENUMInternal::AddElementToEnum( + HENUMInternal *pEnum, // return the created HENUMInternal + mdToken tk) // token value to be stored +{ + HRESULT hr = NOERROR; + TOKENLIST *pdalist; + mdToken *ptk; + + pdalist = (TOKENLIST *) &(pEnum->m_cursor); + + { + // TODO: Revisit this violation. + CONTRACT_VIOLATION(ThrowsViolation); + ptk = ((mdToken *)pdalist->Append()); + } + if (ptk == NULL) + IfFailGo( E_OUTOFMEMORY ); + *ptk = tk; + + // increase the count + pEnum->m_ulCount++; + pEnum->u.m_ulEnd++; +ErrExit: + return hr; + +} // _AddElementToEnum + + + + + +//***************************************************************************** +// find a token in the tokenmap. +//***************************************************************************** +MDTOKENMAP::~MDTOKENMAP() +{ + if (m_pMap) + m_pMap->Release(); +} // MDTOKENMAP::~MDTOKENMAP() + +HRESULT MDTOKENMAP::Init( + IUnknown *pImport) // The import that this map is for. +{ + HRESULT hr; // A result. + IMetaDataTables *pITables=0; // Table information. + ULONG cRows; // Count of rows in a table. + ULONG cTotal; // Running total of rows in db. + TOKENREC *pRec; // A TOKENREC record. + mdToken tkTable; // Token kind for a table. + + hr = pImport->QueryInterface(IID_IMetaDataTables, (void**)&pITables); + if (hr == S_OK) + { + // Determine the size of each table. + cTotal = 0; + for (ULONG ixTbl=0; ixTbl<TBL_COUNT; ++ixTbl) + { + // Where does this table's data start. + m_TableOffset[ixTbl] = cTotal; + // See if this table has tokens. + tkTable = CMiniMdRW::GetTokenForTable(ixTbl); + if (tkTable == (ULONG) -1) + { + // It doesn't have tokens, so we won't see any tokens for the table. + } + else + { // It has tokens, so we may see a token for every row. + IfFailGo(pITables->GetTableInfo(ixTbl, 0, &cRows, 0,0,0)); + // Safe: cTotal += cRows + if (!ClrSafeInt<ULONG>::addition(cTotal, cRows, cTotal)) + { + IfFailGo(COR_E_OVERFLOW); + } + } + } + m_TableOffset[TBL_COUNT] = cTotal; + m_iCountIndexed = cTotal; + // Attempt to allocate space for all of the possible remaps. + if (!AllocateBlock(cTotal)) + IfFailGo(E_OUTOFMEMORY); + // Note that no sorts are needed. + m_sortKind = Indexed; + // Initialize entries to "not found". + for (ULONG i=0; i<cTotal; ++i) + { + pRec = Get(i); + pRec->SetEmpty(); + } + } +#if defined(_DEBUG) + if (SUCCEEDED(pImport->QueryInterface(IID_IMetaDataImport, (void**)&m_pImport))) + { + // Ok, here's a pretty nasty workaround. We're going to make a big assumption here + // that we're owned by the pImport, and so we don't need to keep a refcount + // on the pImport object. + // + // If we did, we'd create a circular reference and neither this object nor + // the RegMeta would be freed. + m_pImport->Release(); + + } + + + +#endif + +ErrExit: + if (pITables) + pITables->Release(); + return hr; +} // HRESULT MDTOKENMAP::Init() + +HRESULT MDTOKENMAP::EmptyMap() +{ + int nCount = Count(); + for (int i=0; i<nCount; ++i) + { + Get(i)->SetEmpty(); + } + + return S_OK; +}// HRESULT MDTOKENMAP::Clear() + + +//***************************************************************************** +// find a token in the tokenmap. +//***************************************************************************** +bool MDTOKENMAP::Find( + mdToken tkFind, // [IN] the token value to find + TOKENREC **ppRec) // [OUT] point to the record found in the dynamic array +{ + int lo,mid,hi; // binary search indices. + TOKENREC *pRec = NULL; + + if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString) + { + // Get the entry. + ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind); + if(ixTbl == (ULONG) -1) + return false; + ULONG iRid = RidFromToken(tkFind); + if((m_TableOffset[ixTbl] + iRid) > m_TableOffset[ixTbl+1]) + return false; + pRec = Get(m_TableOffset[ixTbl] + iRid - 1); + // See if it has been set. + if (pRec->IsEmpty()) + return false; + // Verify that it is what we think it is. + _ASSERTE(pRec->m_tkFrom == tkFind); + *ppRec = pRec; + return true; + } + else + { // Shouldn't be any unsorted records, and table must be sorted in proper ordering. + _ASSERTE( m_iCountTotal == m_iCountSorted && + (m_sortKind == SortByFromToken || m_sortKind == Indexed) ); + _ASSERTE( (m_iCountIndexed + m_iCountTotal) == (ULONG)Count() ); + + // Start with entire table. + lo = m_iCountIndexed; + hi = Count() - 1; + + // While there are rows in the range... + while (lo <= hi) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + + pRec = Get(mid); + + // If equal to the target, done. + if (tkFind == pRec->m_tkFrom) + { + *ppRec = Get(mid); + return true; + } + + // If middle item is too small, search the top half. + if (pRec->m_tkFrom < tkFind) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + } + + // Didn't find anything that matched. + return false; +} // bool MDTOKENMAP::Find() + + + +//***************************************************************************** +// remap the token +//***************************************************************************** +HRESULT MDTOKENMAP::Remap( + mdToken tkFrom, + mdToken *ptkTo) +{ + HRESULT hr = NOERROR; + TOKENREC *pRec; + + // Remap nil to same thing (helps because System.Object has no base class.) + if (IsNilToken(tkFrom)) + { + *ptkTo = tkFrom; + return hr; + } + + if ( Find(tkFrom, &pRec) ) + { + *ptkTo = pRec->m_tkTo; + } + else + { + _ASSERTE( !" Bad lookup map!"); + hr = META_E_BADMETADATA; + } + return hr; +} // HRESULT MDTOKENMAP::Remap() + + + +//***************************************************************************** +// find a token in the tokenmap. +//***************************************************************************** +HRESULT MDTOKENMAP::InsertNotFound( + mdToken tkFind, + bool fDuplicate, + mdToken tkTo, + TOKENREC **ppRec) +{ + HRESULT hr = NOERROR; + int lo, mid, hi; // binary search indices. + TOKENREC *pRec; + + // If possible, validate the input. + _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind)); + + if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString) + { + // Get the entry. + ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind); + _ASSERTE(ixTbl != (ULONG) -1); + ULONG iRid = RidFromToken(tkFind); + _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]); + pRec = Get(m_TableOffset[ixTbl] + iRid - 1); + // See if it has been set. + if (!pRec->IsEmpty()) + { // Verify that it is what we think it is. + _ASSERTE(pRec->m_tkFrom == tkFind); + } + // Store the data. + pRec->m_tkFrom = tkFind; + pRec->m_isDuplicate = fDuplicate; + pRec->m_tkTo = tkTo; + pRec->m_isFoundInImport = false; + // Return the result. + *ppRec = pRec; + } + else + { // Shouldn't be any unsorted records, and table must be sorted in proper ordering. + _ASSERTE( m_iCountTotal == m_iCountSorted && + (m_sortKind == SortByFromToken || m_sortKind == Indexed) ); + + if ((Count() - m_iCountIndexed) > 0) + { + // Start with entire table. + lo = m_iCountIndexed; + hi = Count() - 1; + + // While there are rows in the range... + while (lo < hi) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + + pRec = Get(mid); + + // If equal to the target, done. + if (tkFind == pRec->m_tkFrom) + { + *ppRec = Get(mid); + goto ErrExit; + } + + // If middle item is too small, search the top half. + if (pRec->m_tkFrom < tkFind) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + _ASSERTE(hi <= lo); + pRec = Get(lo); + + if (tkFind == pRec->m_tkFrom) + { + if (tkTo == pRec->m_tkTo && fDuplicate == pRec->m_isDuplicate) + { + *ppRec = pRec; + } + else + { + _ASSERTE(!"inconsistent token has been added to the table!"); + IfFailGo( E_FAIL ); + } + } + + if (tkFind < pRec->m_tkFrom) + { + // insert before lo; + pRec = Insert(lo); + } + else + { + // insert after lo + pRec = Insert(lo + 1); + } + } + else + { + // table is empty + pRec = Insert(m_iCountIndexed); + } + + + // If pRec == NULL, return E_OUTOFMEMORY + IfNullGo(pRec); + + m_iCountTotal++; + m_iCountSorted++; + + *ppRec = pRec; + + // initialize the record + pRec->m_tkFrom = tkFind; + pRec->m_isDuplicate = fDuplicate; + pRec->m_tkTo = tkTo; + pRec->m_isFoundInImport = false; + } + +ErrExit: + return hr; +} // HRESULT MDTOKENMAP::InsertNotFound() + + +//***************************************************************************** +// find a "to" token in the tokenmap. Now that we are doing the ref to def optimization, +// we might have several from tokens map to the same to token. We need to return a range of index +// instead.... +//***************************************************************************** +bool MDTOKENMAP::FindWithToToken( + mdToken tkFind, // [IN] the token value to find + int *piPosition) // [OUT] return the first from-token that has the matching to-token +{ + int lo, mid, hi; // binary search indices. + TOKENREC *pRec; + TOKENREC *pRec2; + + // This makes sure that no insertions take place between calls to FindWithToToken. + // We want to avoid repeated sorting of the table. + _ASSERTE(m_sortKind != SortByToToken || m_iCountTotal == m_iCountSorted); + + // If the map is sorted with From tokens, change it to be sorted with To tokens. + if (m_sortKind != SortByToToken) + SortTokensByToToken(); + + // Start with entire table. + lo = 0; + hi = Count() - 1; + + // While there are rows in the range... + while (lo <= hi) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + + pRec = Get(mid); + + // If equal to the target, done. + if (tkFind == pRec->m_tkTo) + { + for (int i = mid-1; i >= 0; i--) + { + pRec2 = Get(i); + if (tkFind != pRec2->m_tkTo) + { + *piPosition = i + 1; + return true; + } + } + *piPosition = 0; + return true; + } + + // If middle item is too small, search the top half. + if (pRec->m_tkTo < tkFind) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + // Didn't find anything that matched. + return false; +} // bool MDTOKENMAP::FindWithToToken() + + + +//***************************************************************************** +// output a remapped token +//***************************************************************************** +mdToken MDTOKENMAP::SafeRemap( + mdToken tkFrom) // [IN] the token value to find +{ + TOKENREC *pRec; + + // If possible, validate the input. + _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFrom)); + + SortTokensByFromToken(); + + if ( Find(tkFrom, &pRec) ) + { + return pRec->m_tkTo; + } + + return tkFrom; +} // mdToken MDTOKENMAP::SafeRemap() + + +//***************************************************************************** +// Sorting +//***************************************************************************** +void MDTOKENMAP::SortTokensByToToken() +{ + // Only sort if there are unsorted records or the sort kind changed. + if (m_iCountSorted < m_iCountTotal || m_sortKind != SortByToToken) + { + // Sort the entire array. + m_iCountTotal = Count(); + m_iCountIndexed = 0; + SortRangeToToken(0, m_iCountTotal - 1); + m_iCountSorted = m_iCountTotal; + m_sortKind = SortByToToken; + } +} // void MDTOKENMAP::SortTokensByToToken() + +void MDTOKENMAP::SortRangeFromToken( + int iLeft, + int iRight) +{ + int iLast; + int i; // loop variable. + + // if less than two elements you're done. + if (iLeft >= iRight) + return; + + // The mid-element is the pivot, move it to the left. + Swap(iLeft, (iLeft+iRight)/2); + iLast = iLeft; + + // move everything that is smaller than the pivot to the left. + for(i = iLeft+1; i <= iRight; i++) + if (CompareFromToken(i, iLeft) < 0) + Swap(i, ++iLast); + + // Put the pivot to the point where it is in between smaller and larger elements. + Swap(iLeft, iLast); + + // Sort the each partition. + SortRangeFromToken(iLeft, iLast-1); + SortRangeFromToken(iLast+1, iRight); +} // void MDTOKENMAP::SortRangeFromToken() + + +//***************************************************************************** +// Sorting +//***************************************************************************** +void MDTOKENMAP::SortRangeToToken( + int iLeft, + int iRight) +{ + int iLast; + int i; // loop variable. + + // if less than two elements you're done. + if (iLeft >= iRight) + return; + + // The mid-element is the pivot, move it to the left. + Swap(iLeft, (iLeft+iRight)/2); + iLast = iLeft; + + // move everything that is smaller than the pivot to the left. + for(i = iLeft+1; i <= iRight; i++) + if (CompareToToken(i, iLeft) < 0) + Swap(i, ++iLast); + + // Put the pivot to the point where it is in between smaller and larger elements. + Swap(iLeft, iLast); + + // Sort the each partition. + SortRangeToToken(iLeft, iLast-1); + SortRangeToToken(iLast+1, iRight); +} // void MDTOKENMAP::SortRangeToToken() + + +//***************************************************************************** +// find a token in the tokenmap. +//***************************************************************************** +HRESULT MDTOKENMAP::AppendRecord( + mdToken tkFind, + bool fDuplicate, + mdToken tkTo, + TOKENREC **ppRec) +{ + HRESULT hr = NOERROR; + TOKENREC *pRec; + + // If possible, validate the input. + _ASSERTE(!m_pImport || m_pImport->IsValidToken(tkFind)); + + // If the map is indexed, and this is a table token, update-in-place. + if (m_sortKind == Indexed && TypeFromToken(tkFind) != mdtString) + { + // Get the entry. + ULONG ixTbl = CMiniMdRW::GetTableForToken(tkFind); + _ASSERTE(ixTbl != (ULONG) -1); + ULONG iRid = RidFromToken(tkFind); + _ASSERTE((m_TableOffset[ixTbl] + iRid) <= m_TableOffset[ixTbl+1]); + pRec = Get(m_TableOffset[ixTbl] + iRid - 1); + // See if it has been set. + if (!pRec->IsEmpty()) + { // Verify that it is what we think it is. + _ASSERTE(pRec->m_tkFrom == tkFind); + } + } + else + { + pRec = Append(); + IfNullGo(pRec); + + // number of entries increased but not the sorted entry + m_iCountTotal++; + } + + // Store the data. + pRec->m_tkFrom = tkFind; + pRec->m_isDuplicate = fDuplicate; + pRec->m_tkTo = tkTo; + pRec->m_isFoundInImport = false; + *ppRec = pRec; + +ErrExit: + return hr; +} // HRESULT MDTOKENMAP::AppendRecord() + + + +//********************************************************************************************************* +// +// CMapToken's constructor +// +//********************************************************************************************************* +CMapToken::CMapToken() +{ + m_cRef = 1; + m_pTKMap = NULL; + m_isSorted = true; +} // TokenManager::TokenManager() + + + +//********************************************************************************************************* +// +// CMapToken's destructor +// +//********************************************************************************************************* +CMapToken::~CMapToken() +{ + delete m_pTKMap; +} // CMapToken::~CMapToken() + + +ULONG CMapToken::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} // CMapToken::AddRef() + + + +ULONG CMapToken::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (!cRef) + delete this; + return (cRef); +} // CMapToken::Release() + + +HRESULT CMapToken::QueryInterface(REFIID riid, void **ppUnk) +{ + if (ppUnk == NULL) + return E_INVALIDARG; + + if (IsEqualIID(riid, IID_IMapToken)) + { + *ppUnk = (IMapToken *) this; + } + else if (IsEqualIID(riid, IID_IUnknown)) + { + *ppUnk = (IUnknown *) this; + } + else + { + *ppUnk = NULL; + return (E_NOINTERFACE); + } + + AddRef(); + return (S_OK); +} // CMapToken::QueryInterface + + + +//********************************************************************************************************* +// +// Track the token mapping +// +//********************************************************************************************************* +HRESULT CMapToken::Map( + mdToken tkFrom, + mdToken tkTo) +{ + HRESULT hr = NOERROR; + TOKENREC *pTkRec; + + if (m_pTKMap == NULL) + m_pTKMap = new (nothrow) MDTOKENMAP; + + IfNullGo( m_pTKMap ); + + IfFailGo( m_pTKMap->AppendRecord(tkFrom, false, tkTo, &pTkRec) ); + _ASSERTE( pTkRec ); + + m_isSorted = false; +ErrExit: + return hr; +} + + +//********************************************************************************************************* +// +// return what tkFrom is mapped to ptkTo. If there is no remap +// (ie the token from is filtered out by the filter mechanism, it will return false. +// +//********************************************************************************************************* +bool CMapToken::Find( + mdToken tkFrom, + TOKENREC **pRecTo) +{ + TOKENREC *pRec; + bool bRet; + if ( m_isSorted == false ) + { + // sort the map + m_pTKMap->SortTokensByFromToken(); + m_isSorted = true; + } + + bRet = m_pTKMap->Find(tkFrom, &pRec) ; + if (bRet) + { + _ASSERTE(pRecTo); + *pRecTo = pRec; + } + else + { + pRec = NULL; + } + return bRet; +} + + +//********************************************************************************************************* +// +// This function returns true if tkFrom is resolved to a def token. Otherwise, it returns +// false. +// +//********************************************************************************************************* +bool TokenRemapManager::ResolveRefToDef( + mdToken tkRef, // [IN] ref token + mdToken *ptkDef) // [OUT] def token that it resolves to. If it does not resolve to a def + // token, it will return the tkRef token here. +{ + mdToken tkTo; + + _ASSERTE(ptkDef); + + if (TypeFromToken(tkRef) == mdtTypeRef) + { + tkTo = m_TypeRefToTypeDefMap[RidFromToken(tkRef)]; + } + else + { + _ASSERTE( TypeFromToken(tkRef) == mdtMemberRef ); + tkTo = m_MemberRefToMemberDefMap[RidFromToken(tkRef)]; + } + if (RidFromToken(tkTo) == mdTokenNil) + { + *ptkDef = tkRef; + return false; + } + *ptkDef = tkTo; + return true; +} // ResolveRefToDef + + + +//********************************************************************************************************* +// +// Destructor +// +//********************************************************************************************************* +TokenRemapManager::~TokenRemapManager() +{ + m_TypeRefToTypeDefMap.Clear(); + m_MemberRefToMemberDefMap.Clear(); +} // ~TokenRemapManager + + +//********************************************************************************************************* +// +// Initialize the size of Ref to Def optimization table. We will grow the tables in this function. +// We also initialize the table entries to zero. +// +//********************************************************************************************************* +HRESULT TokenRemapManager::ClearAndEnsureCapacity( + ULONG cTypeRef, + ULONG cMemberRef) +{ + HRESULT hr = NOERROR; + if ( ((ULONG) (m_TypeRefToTypeDefMap.Count())) < (cTypeRef + 1) ) + { + if ( m_TypeRefToTypeDefMap.AllocateBlock(cTypeRef + 1 - m_TypeRefToTypeDefMap.Count() ) == 0 ) + IfFailGo( E_OUTOFMEMORY ); + } + memset( m_TypeRefToTypeDefMap.Get(0), 0, (cTypeRef + 1) * sizeof(mdToken) ); + + if ( ((ULONG) (m_MemberRefToMemberDefMap.Count())) < (cMemberRef + 1) ) + { + if ( m_MemberRefToMemberDefMap.AllocateBlock(cMemberRef + 1 - m_MemberRefToMemberDefMap.Count() ) == 0 ) + IfFailGo( E_OUTOFMEMORY ); + } + memset( m_MemberRefToMemberDefMap.Get(0), 0, (cMemberRef + 1) * sizeof(mdToken) ); + +ErrExit: + return hr; +} // HRESULT TokenRemapManager::ClearAndEnsureCapacity() + + + +//********************************************************************************************************* +// +// Constructor +// +//********************************************************************************************************* +CMDSemReadWrite::CMDSemReadWrite( + UTSemReadWrite * pSem) +{ + m_fLockedForRead = false; + m_fLockedForWrite = false; + m_pSem = pSem; +} // CMDSemReadWrite::CMDSemReadWrite + + + +//********************************************************************************************************* +// +// Destructor +// +//********************************************************************************************************* +CMDSemReadWrite::~CMDSemReadWrite() +{ + _ASSERTE(!m_fLockedForRead || !m_fLockedForWrite); + if (m_pSem == NULL) + { + return; + } + if (m_fLockedForRead) + { + LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::~CSemReadWrite \n")); + m_pSem->UnlockRead(); + } + if (m_fLockedForWrite) + { + LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::~CSemReadWrite \n")); + m_pSem->UnlockWrite(); + } +} // CMDSemReadWrite::~CMDSemReadWrite + +//********************************************************************************************************* +// +// Used to obtain the read lock +// +//********************************************************************************************************* +HRESULT CMDSemReadWrite::LockRead() +{ + HRESULT hr = S_OK; + + _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite); + + if (m_pSem == NULL) + { + INDEBUG(m_fLockedForRead = true); + return hr; + } + + LOG((LF_METADATA, LL_EVERYTHING, "LockRead called from CSemReadWrite::LockRead \n")); + IfFailRet(m_pSem->LockRead()); + m_fLockedForRead = true; + + return hr; +} // CMDSemReadWrite::LockRead + +//********************************************************************************************************* +// +// Used to obtain the read lock +// +//********************************************************************************************************* +HRESULT CMDSemReadWrite::LockWrite() +{ + HRESULT hr = S_OK; + + _ASSERTE(!m_fLockedForRead && !m_fLockedForWrite); + + if (m_pSem == NULL) + { + INDEBUG(m_fLockedForWrite = true); + return hr; + } + + LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::LockWrite \n")); + IfFailRet(m_pSem->LockWrite()); + m_fLockedForWrite = true; + + return hr; +} + +//********************************************************************************************************* +// +// Convert a read lock to a write lock +// +//********************************************************************************************************* +HRESULT CMDSemReadWrite::ConvertReadLockToWriteLock() +{ + _ASSERTE(!m_fLockedForWrite); + + HRESULT hr = S_OK; + + if (m_pSem == NULL) + { + INDEBUG(m_fLockedForRead = false); + INDEBUG(m_fLockedForWrite = true); + return hr; + } + + if (m_fLockedForRead) + { + LOG((LF_METADATA, LL_EVERYTHING, "UnlockRead called from CSemReadWrite::ConvertReadLockToWriteLock \n")); + m_pSem->UnlockRead(); + m_fLockedForRead = false; + } + LOG((LF_METADATA, LL_EVERYTHING, "LockWrite called from CSemReadWrite::ConvertReadLockToWriteLock\n")); + IfFailRet(m_pSem->LockWrite()); + m_fLockedForWrite = true; + + return hr; +} // CMDSemReadWrite::ConvertReadLockToWriteLock + + +//********************************************************************************************************* +// +// Unlocking for write +// +//********************************************************************************************************* +void CMDSemReadWrite::UnlockWrite() +{ + _ASSERTE(!m_fLockedForRead); + + if (m_pSem == NULL) + { + INDEBUG(m_fLockedForWrite = false); + return; + } + if (m_fLockedForWrite) + { + LOG((LF_METADATA, LL_EVERYTHING, "UnlockWrite called from CSemReadWrite::UnlockWrite \n")); + m_pSem->UnlockWrite(); + m_fLockedForWrite = false; + } +} // CMDSemReadWrite::UnlockWrite diff --git a/src/coreclr/md/enc/stdafx.h b/src/coreclr/md/enc/stdafx.h new file mode 100644 index 00000000000..16d3ca8cd1f --- /dev/null +++ b/src/coreclr/md/enc/stdafx.h @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// stdafx.h +// + +// +// Precompiled headers. +// +//***************************************************************************** +#ifndef __STDAFX_H_ +#define __STDAFX_H_ + +#include <crtwrap.h> +#include <winwrap.h> +#include <utilcode.h> + +#include <cor.h> +#include <corpriv.h> + +#include "../hotdata/hotheap.h" +#include <metamodelro.h> +#include <liteweightstgdb.h> + + +#include "mdcommon.h" + +#include "utsem.h" + +#endif // __STDAFX_H__ diff --git a/src/coreclr/md/enc/stgio.cpp b/src/coreclr/md/enc/stgio.cpp new file mode 100644 index 00000000000..887f478cf63 --- /dev/null +++ b/src/coreclr/md/enc/stgio.cpp @@ -0,0 +1,1401 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// StgIO.h +// + +// +// This module handles disk/memory i/o for a generic set of storage solutions, +// including: +// * File system handle (HFILE) +// * IStream +// * User supplied memory buffer (non-movable) +// +// The Read, Write, Seek, ... functions are all directed to the corresponding +// method for each type of file, allowing the consumer to use one set of api's. +// +// File system data can be paged fully into memory in two scenarios: +// read: Normal memory mapped file is created to manage paging. +// write: A custom paging system provides storage for pages as required. This +// data is invalidated when you call Rewrite on the file. +// +// Transactions and backups are handled in the existing file case only. The +// Rewrite function can make a backup of the current contents, and the Restore +// function can be used to recover the data into the current scope. The backup +// file is flushed to disk (which is slower but safer) after the copy. The +// Restore also flushed the recovered changes to disk. Worst case scenario you +// get a crash after calling Rewrite but before Restore, in which case you will +// have a foo.clb.txn file in the same directory as the source file, foo.clb in +// this example. +//<REVISIT_TODO> +// @FUTURE: issues, +// 1. For reading a .clb in an image, it would be great to memory map +// only the portion of the file with the .clb in it. +//</REVISIT_TODO> +//***************************************************************************** +#include "stdafx.h" // Standard headers. +#include "stgio.h" // Our definitions. +#include "corerror.h" +#include "posterror.h" +#include "pedecoder.h" +#include "pedecoder.inl" + +//********** Types. *********************************************************** +#define SMALL_ALLOC_MAP_SIZE (64 * 1024) // 64 kb is the minimum size of virtual + // memory you can allocate, so anything + // less is a waste of VM resources. + + +#define MIN_WRITE_CACHE_BYTES (16 * 1024) // 16 kb for a write back cache + + +//********** Locals. ********************************************************** +HRESULT MapFileError(DWORD error); +static void *AllocateMemory(int iSize); +static void FreeMemory(void *pbData); +inline HRESULT MapFileError(DWORD error) +{ + return (PostError(HRESULT_FROM_WIN32(error))); +} + +// Static to class. +int StgIO::m_iPageSize=0; // Size of an OS page. +int StgIO::m_iCacheSize=0; // Size for the write cache. + + + +//********** Code. ************************************************************ +StgIO::StgIO( + bool bAutoMap) : // Memory map for read on open? + m_bAutoMap(bAutoMap) +{ + CtorInit(); + + // If the system page size has not been queried, do so now. + if (m_iPageSize == 0) + { + SYSTEM_INFO sInfo; // Some O/S information. + + // Query the system page size. + GetSystemInfo(&sInfo); + m_iPageSize = sInfo.dwPageSize; + m_iCacheSize = ((MIN_WRITE_CACHE_BYTES - 1) & ~(m_iPageSize - 1)) + m_iPageSize; + } +} + + +void StgIO::CtorInit() +{ + m_bWriteThrough = false; + m_bRewrite = false; + m_bFreeMem = false; + m_pIStream = 0; + m_hFile = INVALID_HANDLE_VALUE; + m_hModule = NULL; + m_hMapping = 0; + m_pBaseData = 0; + m_pData = 0; + m_cbData = 0; + m_fFlags = 0; + m_iType = STGIO_NODATA; + m_cbOffset = 0; + m_rgBuff = 0; + m_cbBuff = 0; + m_rgPageMap = 0; + m_FileType = FILETYPE_UNKNOWN; + m_cRef = 1; + m_mtMappedType = MTYPE_NOMAPPING; +} + + + +StgIO::~StgIO() +{ + if (m_rgBuff) + { + FreeMemory(m_rgBuff); + m_rgBuff = 0; + } + + Close(); +} + + +//***************************************************************************** +// Open the base file on top of: (a) file, (b) memory buffer, or (c) stream. +// If create flag is specified, then this will create a new file with the +// name supplied. No data is read from an opened file. You must call +// MapFileToMem before doing direct pointer access to the contents. +//***************************************************************************** +HRESULT StgIO::Open( // Return code. + LPCWSTR szName, // Name of the storage. + int fFlags, // How to open the file. + const void *pbBuff, // Optional buffer for memory. + ULONG cbBuff, // Size of buffer. + IStream *pIStream, // Stream for input. + LPSECURITY_ATTRIBUTES pAttributes) // Security token. +{ + HRESULT hr; + + // If we were given the storage memory to begin with, then use it. + if (pbBuff && cbBuff) + { + _ASSERTE((fFlags & DBPROP_TMODEF_WRITE) == 0); + + // Save the memory address and size only. No handles. + m_pData = (void *) pbBuff; + m_cbData = cbBuff; + + // All access to data will be by memory provided. + if ((fFlags & DBPROP_TMODEF_SHAREDMEM) == DBPROP_TMODEF_SHAREDMEM) + { + // We're taking ownership of this memory + m_pBaseData = m_pData; + m_iType = STGIO_SHAREDMEM; + } + else + { + m_iType = STGIO_MEM; + } + goto ErrExit; + } + // Check for data backed by a stream pointer. + else if (pIStream) + { + // If this is for the non-create case, get the size of existing data. + if ((fFlags & DBPROP_TMODEF_CREATE) == 0) + { + LARGE_INTEGER iMove = { { 0, 0 } }; + ULARGE_INTEGER iSize; + + // Need the size of the data so we can map it into memory. + if (FAILED(hr = pIStream->Seek(iMove, STREAM_SEEK_END, &iSize))) + return (hr); + m_cbData = iSize.u.LowPart; + } + // Else there is nothing. + else + m_cbData = 0; + + // Save an addref'd copy of the stream. + m_pIStream = pIStream; + m_pIStream->AddRef(); + + // All access to data will be by memory provided. + m_iType = STGIO_STREAM; + goto ErrExit; + } + + // If not on memory, we need a file to do a create/open. + if (!szName || !*szName) + { + return (PostError(E_INVALIDARG)); + } + // Check for create of a new file. + else if (fFlags & DBPROP_TMODEF_CREATE) + { + //<REVISIT_TODO>@future: This could chose to open the file in write through + // mode, which would provide better Duribility (from ACID props), + // but would be much slower.</REVISIT_TODO> + + // Create the new file, overwriting only if caller allows it. + if ((m_hFile = WszCreateFile(szName, GENERIC_READ | GENERIC_WRITE, 0, 0, + (fFlags & DBPROP_TMODEF_FAILIFTHERE) ? CREATE_NEW : CREATE_ALWAYS, + 0, 0)) == INVALID_HANDLE_VALUE) + { + return (MapFileError(GetLastError())); + } + + // Data will come from the file. + m_iType = STGIO_HFILE; + } + // For open in read mode, need to open the file on disk. If opening a shared + // memory view, it has to be opened already, so no file open. + else if ((fFlags & DBPROP_TMODEF_WRITE) == 0) + { + // We have not opened the file nor loaded it as module + _ASSERTE(m_hFile == INVALID_HANDLE_VALUE); + _ASSERTE(m_hModule == NULL); + + // Open the file for read. Sharing is determined by caller, it can + // allow other readers or be exclusive. + DWORD dwFileSharingFlags = FILE_SHARE_DELETE; + if (!(fFlags & DBPROP_TMODEF_EXCLUSIVE)) + { + dwFileSharingFlags |= FILE_SHARE_READ; + +#if !defined(DACCESS_COMPILE) && !defined(TARGET_UNIX) + // PEDecoder is not defined in DAC + + // We prefer to use LoadLibrary if we can because it will share already loaded images (used for execution) + // which saves virtual memory. We only do this if our caller has indicated that this PE file is trusted + // and thus it is OK to do LoadLibrary (note that we still only load it as a resource, which mitigates + // most of the security risk anyway). + if ((fFlags & DBPROP_TMODEF_TRYLOADLIBRARY) != 0) + { + m_hModule = WszLoadLibraryEx(szName, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if (m_hModule != NULL) + { + m_iType = STGIO_HMODULE; + + m_mtMappedType = MTYPE_IMAGE; + + // LoadLibraryEx returns 2 lowest bits indicating how the module was loaded + m_pBaseData = m_pData = (void *)(((INT_PTR)m_hModule) & ~(INT_PTR)0x3); + + PEDecoder peDecoder; + if (SUCCEEDED(peDecoder.Init( + m_pBaseData, + false)) && // relocated + peDecoder.CheckNTHeaders()) + { + m_cbData = peDecoder.GetNTHeaders32()->OptionalHeader.SizeOfImage; + } + else + { + // PEDecoder failed on loaded library, let's backout all our changes to this object + // and fall back to file mapping + m_iType = STGIO_NODATA; + m_mtMappedType = MTYPE_NOMAPPING; + m_pBaseData = m_pData = NULL; + + FreeLibrary(m_hModule); + m_hModule = NULL; + } + } + } +#endif //!DACCESS_COMPILE && !TARGET_UNIX + } + + if (m_hModule == NULL) + { // We didn't get the loaded module (we either didn't want to or it failed) + HandleHolder hFile(WszCreateFile(szName, + GENERIC_READ, + dwFileSharingFlags, + 0, + OPEN_EXISTING, + 0, + 0)); + + if (hFile == INVALID_HANDLE_VALUE) + return (MapFileError(GetLastError())); + + // Get size of file. + m_cbData = ::SetFilePointer(hFile, 0, 0, FILE_END); + + // Can't read anything from an empty file. + if (m_cbData == 0) + return (PostError(CLDB_E_NO_DATA)); + + // Data will come from the file. + m_hFile = hFile.Extract(); + + m_iType = STGIO_HFILE; + } + } + +ErrExit: + + // If we will ever write, then we need the buffer cache. + if (fFlags & DBPROP_TMODEF_WRITE) + { + // Allocate a cache buffer for writing. + if ((m_rgBuff = (BYTE *) AllocateMemory(m_iCacheSize)) == NULL) + { + Close(); + return PostError(OutOfMemory()); + } + m_cbBuff = 0; + } + + // Save flags for later. + m_fFlags = fFlags; + if ((szName != NULL) && (*szName != 0)) + { + WCHAR rcExt[_MAX_PATH]; + SplitPath(szName, NULL, 0, NULL, 0, NULL, 0, rcExt, _MAX_PATH); + if (SString::_wcsicmp(rcExt, W(".obj")) == 0) + { + m_FileType = FILETYPE_NTOBJ; + } + else if (SString::_wcsicmp(rcExt, W(".tlb")) == 0) + { + m_FileType = FILETYPE_TLB; + } + } + + // For auto map case, map the view of the file as part of open. + if (m_bAutoMap && + (m_iType == STGIO_HFILE || m_iType == STGIO_STREAM) && + !(fFlags & DBPROP_TMODEF_CREATE)) + { + void * ptr; + ULONG cb; + + if (FAILED(hr = MapFileToMem(ptr, &cb, pAttributes))) + { + Close(); + return hr; + } + } + return S_OK; +} // StgIO::Open + + +//***************************************************************************** +// Shut down the file handles and allocated objects. +//***************************************************************************** +void StgIO::Close() +{ + switch (m_iType) + { + // Free any allocated memory. + case STGIO_SHAREDMEM: + if (m_pBaseData != NULL) + { + CoTaskMemFree(m_pBaseData); + m_pBaseData = NULL; + break; + } + + FALLTHROUGH; + + case STGIO_MEM: + case STGIO_HFILEMEM: + if (m_bFreeMem && m_pBaseData) + { + FreeMemory(m_pBaseData); + m_pBaseData = m_pData = 0; + } + // Intentional fall through to file case, if we kept handle open. + FALLTHROUGH; + + case STGIO_HFILE: + { + // Free the file handle. + if (m_hFile != INVALID_HANDLE_VALUE) + CloseHandle(m_hFile); + + // If we allocated space for in memory paging, then free it. + } + break; + + case STGIO_HMODULE: + { + if (m_hModule != NULL) + FreeLibrary(m_hModule); + m_hModule = NULL; + break; + } + + // Free the stream pointer. + case STGIO_STREAM: + { + if (m_pIStream != NULL) + m_pIStream->Release(); + } + break; + + // Weird to shut down what you didn't open, isn't it? Allow for + // error case where dtor shuts down as an afterthought. + case STGIO_NODATA: + default: + return; + } + + // Free any page map and base data. + FreePageMap(); + + // Reset state values so we don't get confused. + CtorInit(); +} + +//***************************************************************************** +// Called to read the data into allocated memory and release the backing store. +// Only available on read-only data. +//***************************************************************************** +HRESULT +StgIO::LoadFileToMemory() +{ + HRESULT hr; + void *pData; // Allocated buffer for file. + ULONG cbData; // Size of the data. + ULONG cbRead = 0; // Data actually read. + + // Make sure it is a read-only file. + if (m_fFlags & DBPROP_TMODEF_WRITE) + return E_INVALIDARG; + + // Try to allocate the buffer. + cbData = m_cbData; + pData = AllocateMemory(cbData); + IfNullGo(pData); + + // Try to read the file into the buffer. + IfFailGo(Read(pData, cbData, &cbRead)); + if (cbData != cbRead) + { + _ASSERTE_MSG(FALSE, "Read didn't succeed."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Done with the old data. + Close(); + + // Open with new data. + hr = Open(NULL /* szName */, STGIO_READ, pData, cbData, NULL /* IStream* */, NULL /* lpSecurityAttributes */); + _ASSERTE(SUCCEEDED(hr)); // should not be a failure code path with open on buffer. + + // Mark the new memory so that it will be freed later. + m_pBaseData = m_pData; + m_bFreeMem = true; + +ErrExit: + if (FAILED(hr) && pData) + FreeMemory(pData); + + return hr; +} // StgIO::LoadFileToMemory + + +//***************************************************************************** +// Read data from the storage source. This will handle all types of backing +// storage from mmf, streams, and file handles. No read ahead or MRU +// caching is done. +//***************************************************************************** +HRESULT StgIO::Read( // Return code. + void *pbBuff, // Write buffer here. + ULONG cbBuff, // How much to read. + ULONG *pcbRead) // How much read. +{ + ULONG cbCopy; // For boundary checks. + void *pbData; // Data buffer for mem read. + HRESULT hr = S_OK; + + // Validate arguments, don't call if you don't need to. + _ASSERTE(pbBuff != 0); + _ASSERTE(cbBuff > 0); + + // Get the data based on type. + switch (m_iType) + { + // For data on file, there are two possiblities: + // (1) We have an in memory backing store we should use, or + // (2) We just need to read from the file. + case STGIO_HFILE: + case STGIO_HMODULE: + { + _ASSERTE((m_hFile != INVALID_HANDLE_VALUE) || (m_hModule != NULL)); + + // Backing store does its own paging. + if (IsBackingStore() || IsMemoryMapped()) + { + // Force the data into memory. + if (FAILED(hr = GetPtrForMem(GetCurrentOffset(), cbBuff, pbData))) + goto ErrExit; + + // Copy it back for the user and save the size. + memcpy(pbBuff, pbData, cbBuff); + if (pcbRead) + *pcbRead = cbBuff; + } + // If there is no backing store, this is just a read operation. + else + { + _ASSERTE((m_iType == STGIO_HFILE) && (m_hFile != INVALID_HANDLE_VALUE)); + _ASSERTE(m_hModule == NULL); + + ULONG cbTemp = 0; + if (!pcbRead) + pcbRead = &cbTemp; + hr = ReadFromDisk(pbBuff, cbBuff, pcbRead); + m_cbOffset += *pcbRead; + } + } + break; + + // Data in a stream is always just read. + case STGIO_STREAM: + { + _ASSERTE((IStream *) m_pIStream); + if (!pcbRead) + pcbRead = &cbCopy; + *pcbRead = 0; + hr = m_pIStream->Read(pbBuff, cbBuff, pcbRead); + if (SUCCEEDED(hr)) + m_cbOffset += *pcbRead; + } + break; + + // Simply copy the data from our data. + case STGIO_MEM: + case STGIO_SHAREDMEM: + case STGIO_HFILEMEM: + { + _ASSERTE(m_pData && m_cbData); + + // Check for read past end of buffer and adjust. + if (GetCurrentOffset() + cbBuff > m_cbData) + cbCopy = m_cbData - GetCurrentOffset(); + else + cbCopy = cbBuff; + + // Copy the data into the callers buffer. + memcpy(pbBuff, (void *) ((DWORD_PTR)m_pData + GetCurrentOffset()), cbCopy); + if (pcbRead) + *pcbRead = cbCopy; + + // Save a logical offset. + m_cbOffset += cbCopy; + } + break; + + case STGIO_NODATA: + default: + _ASSERTE(0); + break; + } + +ErrExit: + return (hr); +} + + +//***************************************************************************** +// Write to disk. This function will cache up to a page of data in a buffer +// and peridocially flush it on overflow and explicit request. This makes it +// safe to do lots of small writes without too much performance overhead. +//***************************************************************************** +HRESULT StgIO::Write( // true/false. + const void *pbBuff, // Data to write. + ULONG cbWrite, // How much data to write. + ULONG *pcbWritten) // How much did get written. +{ + ULONG cbWriteIn=cbWrite; // Track amount written. + ULONG cbCopy; + HRESULT hr = S_OK; + + _ASSERTE(m_rgBuff != 0); + _ASSERTE(cbWrite); + + while (cbWrite) + { + // In the case where the buffer is already huge, write the whole thing + // and avoid the cache. + if (m_cbBuff == 0 && cbWrite >= (ULONG) m_iPageSize) + { + if (SUCCEEDED(hr = WriteToDisk(pbBuff, cbWrite, pcbWritten))) + m_cbOffset += cbWrite; + break; + } + // Otherwise cache as much as we can and flush. + else + { + // Determine how much data goes into the cache buffer. + cbCopy = m_iPageSize - m_cbBuff; + cbCopy = min(cbCopy, cbWrite); + + // Copy the data into the cache and adjust counts. + memcpy(&m_rgBuff[m_cbBuff], pbBuff, cbCopy); + pbBuff = (void *) ((DWORD_PTR)pbBuff + cbCopy); + m_cbBuff += cbCopy; + m_cbOffset += cbCopy; + cbWrite -= cbCopy; + + // If there is enough data, then flush it to disk and reset count. + if (m_cbBuff >= (ULONG) m_iPageSize) + { + if (FAILED(hr = FlushCache())) + break; + } + } + } + + // Return value for caller. + if (SUCCEEDED(hr) && pcbWritten) + *pcbWritten = cbWriteIn; + return (hr); +} + + +//***************************************************************************** +// Moves the file pointer to the new location. This handles the different +// types of storage systems. +//***************************************************************************** +HRESULT StgIO::Seek( // New offset. + int lVal, // How much to move. + ULONG fMoveType) // Direction, use Win32 FILE_xxxx. +{ + ULONG cbRtn = 0; + HRESULT hr = NOERROR; + + _ASSERTE(fMoveType >= FILE_BEGIN && fMoveType <= FILE_END); + + // Action taken depends on type of storage. + switch (m_iType) + { + case STGIO_HFILE: + { + // Use the file system's move. + _ASSERTE(m_hFile != INVALID_HANDLE_VALUE); + cbRtn = ::SetFilePointer(m_hFile, lVal, 0, fMoveType); + + // Save the location redundantly. + if (cbRtn != 0xffffffff) + { + // make sure that m_cbOffset will stay within range + if (cbRtn > m_cbData || cbRtn < 0) + { + IfFailGo(STG_E_INVALIDFUNCTION); + } + m_cbOffset = cbRtn; + } + } + break; + + case STGIO_STREAM: + { + LARGE_INTEGER iMove; + ULARGE_INTEGER iNewLoc; + + // Need a 64-bit int. + iMove.QuadPart = lVal; + + // The move types are named differently, but have same value. + if (FAILED(hr = m_pIStream->Seek(iMove, fMoveType, &iNewLoc))) + return (hr); + + // make sure that m_cbOffset will stay within range + if (iNewLoc.u.LowPart > m_cbData || iNewLoc.u.LowPart < 0) + IfFailGo(STG_E_INVALIDFUNCTION); + + // Save off only out location. + m_cbOffset = iNewLoc.u.LowPart; + } + break; + + case STGIO_MEM: + case STGIO_SHAREDMEM: + case STGIO_HFILEMEM: + case STGIO_HMODULE: + { + // We own the offset, so change our value. + switch (fMoveType) + { + case FILE_BEGIN: + + // make sure that m_cbOffset will stay within range + if ((ULONG) lVal > m_cbData || lVal < 0) + { + IfFailGo(STG_E_INVALIDFUNCTION); + } + m_cbOffset = lVal; + break; + + case FILE_CURRENT: + + // make sure that m_cbOffset will stay within range + if (m_cbOffset + lVal > m_cbData) + { + IfFailGo(STG_E_INVALIDFUNCTION); + } + m_cbOffset = m_cbOffset + lVal; + break; + + case FILE_END: + _ASSERTE(lVal < (LONG) m_cbData); + // make sure that m_cbOffset will stay within range + if (m_cbData + lVal > m_cbData) + { + IfFailGo(STG_E_INVALIDFUNCTION); + } + m_cbOffset = m_cbData + lVal; + break; + } + + cbRtn = m_cbOffset; + } + break; + + // Weird to seek with no data. + case STGIO_NODATA: + default: + _ASSERTE(0); + break; + } + +ErrExit: + return hr; +} + + +//***************************************************************************** +// Retrieves the current offset for the storage being used. This value is +// tracked based on Read, Write, and Seek operations. +//***************************************************************************** +ULONG StgIO::GetCurrentOffset() // Current offset. +{ + return (m_cbOffset); +} + + +//***************************************************************************** +// Map the file contents to a memory mapped file and return a pointer to the +// data. For read/write with a backing store, map the file using an internal +// paging system. +//***************************************************************************** +HRESULT StgIO::MapFileToMem( // Return code. + void *&ptr, // Return pointer to file data. + ULONG *pcbSize, // Return size of data. + LPSECURITY_ATTRIBUTES pAttributes) // Security token. +{ + char rcShared[MAXSHMEM]; // ANSI version of shared name. + HRESULT hr = S_OK; + + // Don't penalize for multiple calls. Also, allow calls for mem type so + // callers don't need to do so much checking. + if (IsBackingStore() || + IsMemoryMapped() || + (m_iType == STGIO_MEM) || + (m_iType == STGIO_SHAREDMEM) || + (m_iType == STGIO_HFILEMEM)) + { + ptr = m_pData; + if (pcbSize) + *pcbSize = m_cbData; + return (S_OK); + } + + //#CopySmallFiles + // Check the size of the data we want to map. If it is small enough, then + // simply allocate a chunk of memory from a finer grained heap. This saves + // virtual memory space, page table entries, and should reduce overall working set. + // Also, open for read/write needs a full backing store. + if ((m_cbData <= SMALL_ALLOC_MAP_SIZE) && (SMALL_ALLOC_MAP_SIZE > 0)) + { + DWORD cbRead = m_cbData; + _ASSERTE(m_pData == 0); + + // Just malloc a chunk of data to use. + m_pBaseData = m_pData = AllocateMemory(m_cbData); + if (!m_pData) + { + hr = OutOfMemory(); + goto ErrExit; + } + + // Read all of the file contents into this piece of memory. + IfFailGo( Seek(0, FILE_BEGIN) ); + if (FAILED(hr = Read(m_pData, cbRead, &cbRead))) + { + FreeMemory(m_pData); + m_pData = 0; + goto ErrExit; + } + _ASSERTE(cbRead == m_cbData); + + // If the file isn't being opened for exclusive mode, then free it. + // If it is for exclusive, then we need to keep the handle open so the + // file is locked, preventing other readers. Also leave it open if + // in read/write mode so we can truncate and rewrite. + if (m_hFile == INVALID_HANDLE_VALUE || + ((m_fFlags & DBPROP_TMODEF_EXCLUSIVE) == 0 && (m_fFlags & DBPROP_TMODEF_WRITE) == 0)) + { + // If there was a handle open, then free it. + if (m_hFile != INVALID_HANDLE_VALUE) + { + VERIFY(CloseHandle(m_hFile)); + m_hFile = INVALID_HANDLE_VALUE; + } + // Free the stream pointer. + else + if (m_pIStream != 0) + { + m_pIStream->Release(); + m_pIStream = 0; + } + + // Switch the type to memory only access. + m_iType = STGIO_MEM; + } + else + m_iType = STGIO_HFILEMEM; + + // Free the memory when we shut down. + m_bFreeMem = true; + } + // Finally, a real mapping file must be created. + else + { + // Now we will map, so better have it right. + _ASSERTE(m_hFile != INVALID_HANDLE_VALUE || m_iType == STGIO_STREAM); + _ASSERTE(m_rgPageMap == 0); + + // For read mode, use a memory mapped file since the size will never + // change for the life of the handle. + if ((m_fFlags & DBPROP_TMODEF_WRITE) == 0 && m_iType != STGIO_STREAM) + { + // Create a mapping object for the file. + _ASSERTE(m_hMapping == 0); + + DWORD dwProtectionFlags = PAGE_READONLY; + + if ((m_hMapping = WszCreateFileMapping(m_hFile, pAttributes, dwProtectionFlags, + 0, 0, nullptr)) == 0) + { + return (MapFileError(GetLastError())); + } + m_mtMappedType = MTYPE_FLAT; + // Check to see if the memory already exists, in which case we have + // no guarantees it is the right piece of data. + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + hr = PostError(CLDB_E_SMDUPLICATE, rcShared); + goto ErrExit; + } + + // Now map the file into memory so we can read from pointer access. + // <REVISIT_TODO>Note: Added a check for IsBadReadPtr per the Services team which + // indicates that under some conditions this API can give you back + // a totally bogus pointer.</REVISIT_TODO> + if ((m_pBaseData = m_pData = MapViewOfFile(m_hMapping, FILE_MAP_READ, + 0, 0, 0)) == 0) + { + hr = MapFileError(GetLastError()); + if (SUCCEEDED(hr)) + { + _ASSERTE_MSG(FALSE, "Error code doesn't indicate error."); + hr = PostError(CLDB_E_FILE_CORRUPT); + } + + // In case we got back a bogus pointer. + m_pBaseData = m_pData = NULL; + goto ErrExit; + } + } + // In write mode, we need the hybrid combination of being able to back up + // the data in memory via cache, but then later rewrite the contents and + // throw away our cached copy. Memory mapped files are not good for this + // case due to poor write characteristics. + else + { + ULONG iMaxSize; // How much memory required for file. + + // Figure out how many pages we'll require, round up actual data + // size to page size. + iMaxSize = (((m_cbData - 1) & ~(m_iPageSize - 1)) + m_iPageSize); + // Check integer overflow in previous statement + if (iMaxSize < m_cbData) + { + IfFailGo(PostError(COR_E_OVERFLOW)); + } + + // Allocate a bit vector to track loaded pages. + if ((m_rgPageMap = new (nothrow) BYTE[iMaxSize / m_iPageSize]) == 0) + return (PostError(OutOfMemory())); + memset(m_rgPageMap, 0, sizeof(BYTE) * (iMaxSize / m_iPageSize)); + + // Allocate space for the file contents. + if ((m_pBaseData = m_pData = ::ClrVirtualAlloc(0, iMaxSize, MEM_RESERVE, PAGE_NOACCESS)) == 0) + { + hr = PostError(OutOfMemory()); + goto ErrExit; + } + } + } + + // Reset any changes made by mapping. + IfFailGo( Seek(0, FILE_BEGIN) ); + +ErrExit: + + // Check for errors and clean up. + if (FAILED(hr)) + { + if (m_hMapping) + CloseHandle(m_hMapping); + m_hMapping = 0; + m_pBaseData = m_pData = 0; + m_cbData = 0; + } + ptr = m_pData; + if (pcbSize) + *pcbSize = m_cbData; + return (hr); +} + + +//***************************************************************************** +// Free the mapping object for shared memory but keep the rest of the internal +// state intact. +//***************************************************************************** +HRESULT StgIO::ReleaseMappingObject() // Return code. +{ + // Check type first. + if (m_iType != STGIO_SHAREDMEM) + { + _ASSERTE(FALSE); + return S_OK; + } + + // Must have an allocated handle. + _ASSERTE(m_hMapping != 0); + + // Freeing the mapping object doesn't do any good if you still have the file. + _ASSERTE(m_hFile == INVALID_HANDLE_VALUE); + + // Unmap the memory we allocated before freeing the handle. But keep the + // memory address intact. + if (m_pData) + VERIFY(UnmapViewOfFile(m_pData)); + + // Free the handle. + if (m_hMapping != 0) + { + VERIFY(CloseHandle(m_hMapping)); + m_hMapping = 0; + } + return S_OK; +} + + + +//***************************************************************************** +// Resets the logical base address and size to the value given. This is for +// cases like finding a section embedded in another format, like the .clb inside +// of an image. GetPtrForMem, Read, and Seek will then behave as though only +// data from pbStart to cbSize is valid. +//***************************************************************************** +HRESULT StgIO::SetBaseRange( // Return code. + void *pbStart, // Start of file data. + ULONG cbSize) // How big is the range. +{ + if (m_iType == STGIO_SHAREDMEM) + { + // The base range must be inside of the current range. + _ASSERTE((m_pBaseData != NULL) && (m_cbData != 0)); + _ASSERTE(((LONG_PTR) pbStart >= (LONG_PTR) m_pBaseData)); + _ASSERTE(((LONG_PTR) pbStart + cbSize <= (LONG_PTR) m_pBaseData + m_cbData)); + } + + // Save the base range per user request. + m_pData = pbStart; + m_cbData = cbSize; + return S_OK; +} + + +//***************************************************************************** +// Caller wants a pointer to a chunk of the file. This function will make sure +// that the memory for that chunk has been committed and will load from the +// file if required. This algorithm attempts to load no more data from disk +// than is necessary. It walks the required pages from lowest to highest, +// and for each block of unloaded pages, the memory is committed and the data +// is read from disk. If all pages are unloaded, all of them are loaded at +// once to speed throughput from disk. +//***************************************************************************** +HRESULT StgIO::GetPtrForMem( // Return code. + ULONG cbStart, // Where to start getting memory. + ULONG cbSize, // How much data. + void *&ptr) // Return pointer to memory here. +{ + int iFirst, iLast; // First and last page required. + ULONG iOffset, iSize; // For committing ranges of memory. + int i, j; // Loop control. + HRESULT hr; + + // We need either memory (mmf or user supplied) or a backing store to + // return a pointer. Call Read if you don't have these. + if (!IsBackingStore() && m_pData == 0) + return (PostError(BadError(E_UNEXPECTED))); + + // Validate the caller isn't asking for a data value out of range. + if (!(ClrSafeInt<ULONG>::addition(cbStart, cbSize, iOffset) + && (iOffset <= m_cbData))) + return (PostError(E_INVALIDARG)); + + // This code will check for pages that need to be paged from disk in + // order for us to return a pointer to that memory. + if (IsBackingStore()) + { + // Backing store is bogus when in rewrite mode. + if (m_bRewrite) + return (PostError(BadError(E_UNEXPECTED))); + + // Must have the page map to continue. + _ASSERTE(m_rgPageMap && m_iPageSize && m_pData); + + // Figure out the first and last page that are required for commit. + iFirst = cbStart / m_iPageSize; + iLast = (cbStart + cbSize - 1) / m_iPageSize; + + // Avoid confusion. + ptr = 0; + + // Do a smart load of every page required. Do not reload pages that have + // already been brought in from disk. + //<REVISIT_TODO>@FUTURE: add an optimization so that when all pages have been faulted, we no + // longer to a page by page search.</REVISIT_TODO> + for (i=iFirst; i<=iLast; ) + { + // Find the first page that hasn't already been loaded. + while (GetBit(m_rgPageMap, i) && i<=iLast) + ++i; + if (i > iLast) + break; + + // Offset for first thing to load. + iOffset = i * m_iPageSize; + iSize = 0; + + // See how many in a row have not been loaded. + for (j=i; i<=iLast && !GetBit(m_rgPageMap, i); i++) + { + // Safe: iSize += m_iPageSize; + if (!(ClrSafeInt<ULONG>::addition(iSize, m_iPageSize, iSize))) + { + return PostError(E_INVALIDARG); + } + } + + // First commit the memory for this part of the file. + if (::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset), + iSize, MEM_COMMIT, PAGE_READWRITE) == 0) + return (PostError(OutOfMemory())); + + // Now load that portion of the file from disk. + if (FAILED(hr = Seek(iOffset, FILE_BEGIN)) || + FAILED(hr = ReadFromDisk((void *) ((DWORD_PTR) m_pData + iOffset), iSize, 0))) + { + return (hr); + } + + // Change the memory to read only to avoid any modifications. Any faults + // that occur indicate a bug whereby the engine is trying to write to + // protected memory. + _ASSERTE(::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset), + iSize, MEM_COMMIT, PAGE_READONLY) != 0); + + // Record each new loaded page. + for (; j<i; j++) + SetBit(m_rgPageMap, j, true); + } + + // Everything was brought into memory, so now return pointer to caller. + ptr = (void *) ((DWORD_PTR) m_pData + cbStart); + } + // Memory version or memory mapped file work the same way. + else if (IsMemoryMapped() || + (m_iType == STGIO_MEM) || + (m_iType == STGIO_SHAREDMEM) || + (m_iType == STGIO_HFILEMEM)) + { + if (!(cbStart <= m_cbData)) + return (PostError(E_INVALIDARG)); + + ptr = (void *) ((DWORD_PTR) m_pData + cbStart); + } + // What's left?! Add some defense. + else + { + _ASSERTE(0); + ptr = 0; + return (PostError(BadError(E_UNEXPECTED))); + } + return (S_OK); +} + + +//***************************************************************************** +// For cached writes, flush the cache to the data store. +//***************************************************************************** +HRESULT StgIO::FlushCache() +{ + ULONG cbWritten; + HRESULT hr; + + if (m_cbBuff) + { + if (FAILED(hr = WriteToDisk(m_rgBuff, m_cbBuff, &cbWritten))) + return (hr); + m_cbBuff = 0; + } + return (S_OK); +} + +//***************************************************************************** +// Tells the file system to flush any cached data it may have. This is +// expensive, but if successful guarantees you won't lose writes short of +// a disk failure. +//***************************************************************************** +HRESULT StgIO::FlushFileBuffers() +{ + _ASSERTE(!IsReadOnly()); + + if (m_hFile != INVALID_HANDLE_VALUE) + { + if (::FlushFileBuffers(m_hFile)) + return (S_OK); + else + return (MapFileError(GetLastError())); + } + return (S_OK); +} + + +//***************************************************************************** +// Called after a successful rewrite of an existing file. The in memory +// backing store is no longer valid because all new data is in memory and +// on disk. This is essentially the same state as created, so free up some +// working set and remember this state. +//***************************************************************************** +HRESULT StgIO::ResetBackingStore() // Return code. +{ + // Don't be calling this function for read only data. + _ASSERTE(!IsReadOnly()); + + // Free up any backing store data we no longer need now that everything + // is in memory. + FreePageMap(); + return (S_OK); +} + + +// +// Private. +// + + + +//***************************************************************************** +// This version will force the data in cache out to disk for real. The code +// can handle the different types of storage we might be sitting on based on +// the open type. +//***************************************************************************** +HRESULT StgIO::WriteToDisk( // Return code. + const void *pbBuff, // Buffer to write. + ULONG cbWrite, // How much. + ULONG *pcbWritten) // Return how much written. +{ + ULONG cbWritten; // Buffer for write funcs. + HRESULT hr = S_OK; + + // Pretty obvious. + _ASSERTE(!IsReadOnly()); + + // Always need a buffer to write this data to. + if (!pcbWritten) + pcbWritten = &cbWritten; + + // Action taken depends on type of storage. + switch (m_iType) + { + case STGIO_HFILE: + case STGIO_HFILEMEM: + { + // Use the file system's move. + _ASSERTE(m_hFile != INVALID_HANDLE_VALUE); + + // Do the write to disk. + if (!::WriteFile(m_hFile, pbBuff, cbWrite, pcbWritten, 0)) + hr = MapFileError(GetLastError()); + } + break; + + // Free the stream pointer. + case STGIO_STREAM: + { + // Delegate write to stream code. + hr = m_pIStream->Write(pbBuff, cbWrite, pcbWritten); + } + break; + + // We cannot write to fixed read/only memory or LoadLibrary module. + case STGIO_HMODULE: + case STGIO_MEM: + case STGIO_SHAREDMEM: + _ASSERTE(0); + hr = BadError(E_UNEXPECTED); + break; + + // Weird to seek with no data. + case STGIO_NODATA: + default: + _ASSERTE(0); + break; + } + return (hr); +} + + +//***************************************************************************** +// This version only reads from disk. +//***************************************************************************** +HRESULT StgIO::ReadFromDisk( // Return code. + void *pbBuff, // Write buffer here. + ULONG cbBuff, // How much to read. + ULONG *pcbRead) // How much read. +{ + ULONG cbRead; + + _ASSERTE(m_iType == STGIO_HFILE || m_iType == STGIO_STREAM); + + // Need to have a buffer. + if (!pcbRead) + pcbRead = &cbRead; + + // Read only from file to avoid recursive logic. + if (m_iType == STGIO_HFILE || m_iType == STGIO_HFILEMEM) + { + if (::ReadFile(m_hFile, pbBuff, cbBuff, pcbRead, 0)) + return (S_OK); + return (MapFileError(GetLastError())); + } + // Read directly from stream. + else + { + return (m_pIStream->Read(pbBuff, cbBuff, pcbRead)); + } +} + + +//***************************************************************************** +// Copy the contents of the file for this storage to the target path. +//***************************************************************************** +HRESULT StgIO::CopyFileInternal( // Return code. + LPCWSTR szTo, // Target save path for file. + int bFailIfThere, // true to fail if target exists. + int bWriteThrough) // Should copy be written through OS cache. +{ + DWORD iCurrent; // Save original location. + DWORD cbRead; // Byte count for buffer. + DWORD cbWrite; // Check write of bytes. + const DWORD cbBuff = 4096; // Size of buffer for copy (in bytes). + BYTE *pBuff = (BYTE*)alloca(cbBuff); // Buffer for copy. + HANDLE hFile; // Target file. + HRESULT hr = S_OK; + + // Create target file. + if ((hFile = ::WszCreateFile(szTo, GENERIC_WRITE, 0, 0, + (bFailIfThere) ? CREATE_NEW : CREATE_ALWAYS, + (bWriteThrough) ? FILE_FLAG_WRITE_THROUGH : 0, + 0)) == INVALID_HANDLE_VALUE) + { + return (MapFileError(GetLastError())); + } + + // Save current location and reset it later. + iCurrent = ::SetFilePointer(m_hFile, 0, 0, FILE_CURRENT); + ::SetFilePointer(m_hFile, 0, 0, FILE_BEGIN); + + // Copy while there are bytes. + while (::ReadFile(m_hFile, pBuff, cbBuff, &cbRead, 0) && cbRead) + { + if (!::WriteFile(hFile, pBuff, cbRead, &cbWrite, 0) || cbWrite != cbRead) + { + hr = STG_E_WRITEFAULT; + break; + } + } + + // Reset file offset. + ::SetFilePointer(m_hFile, iCurrent, 0, FILE_BEGIN); + + // Close target. + if (!bWriteThrough) + VERIFY(::FlushFileBuffers(hFile)); + ::CloseHandle(hFile); + return (hr); +} + + +//***************************************************************************** +// Free the data used for backing store from disk in read/write scenario. +//***************************************************************************** +void StgIO::FreePageMap() +{ + // If a small file was allocated, then free that memory. + if (m_bFreeMem && m_pBaseData) + FreeMemory(m_pBaseData); + // For mmf, close handles and free resources. + else if (m_hMapping && m_pBaseData) + { + VERIFY(UnmapViewOfFile(m_pBaseData)); + VERIFY(CloseHandle(m_hMapping)); + } + // For our own system, free memory. + else if (m_rgPageMap && m_pBaseData) + { + delete [] m_rgPageMap; + m_rgPageMap = 0; + VERIFY(::ClrVirtualFree(m_pBaseData, (((m_cbData - 1) & ~(m_iPageSize - 1)) + m_iPageSize), MEM_DECOMMIT)); + VERIFY(::ClrVirtualFree(m_pBaseData, 0, MEM_RELEASE)); + m_pBaseData = 0; + m_cbData = 0; + } + + m_pBaseData = 0; + m_hMapping = 0; + m_cbData = 0; +} + + +//***************************************************************************** +// Check the given pointer and ensure it is aligned correct. Return true +// if it is aligned, false if it is not. +//***************************************************************************** +int StgIO::IsAlignedPtr(ULONG_PTR Value, int iAlignment) +{ + HRESULT hr; + void *ptrStart = NULL; + + if ((m_iType == STGIO_STREAM) || + (m_iType == STGIO_SHAREDMEM) || + (m_iType == STGIO_MEM)) + { + return ((Value - (ULONG_PTR) m_pData) % iAlignment == 0); + } + else + { + hr = GetPtrForMem(0, 1, ptrStart); + _ASSERTE(hr == S_OK && "GetPtrForMem failed"); + _ASSERTE(Value > (ULONG_PTR) ptrStart); + return (((Value - (ULONG_PTR) ptrStart) % iAlignment) == 0); + } +} // int StgIO::IsAlignedPtr() + + + + + +//***************************************************************************** +// These helper functions are used to allocate fairly large pieces of memory, +// more than should be taken from the runtime heap, but less that would require +// virtual memory overhead. +//***************************************************************************** +// #define _TRACE_MEM_ 1 + +void *AllocateMemory(int iSize) +{ + void * ptr; + ptr = new (nothrow) BYTE[iSize]; + +#if defined(_DEBUG) && defined(_TRACE_MEM_) + static int i=0; + DbgWriteEx(W("AllocateMemory: (%d) 0x%08x, size %d\n"), ++i, ptr, iSize); +#endif + return (ptr); +} + + +void FreeMemory(void *pbData) +{ +#if defined(_DEBUG) && defined(_TRACE_MEM_) + static int i=0; + DbgWriteEx(W("FreeMemory: (%d) 0x%08x\n"), ++i, pbData); +#endif + + _ASSERTE(pbData); + delete [] (BYTE *) pbData; +} + diff --git a/src/coreclr/md/enc/stgtiggerstorage.cpp b/src/coreclr/md/enc/stgtiggerstorage.cpp new file mode 100644 index 00000000000..435a7a8996e --- /dev/null +++ b/src/coreclr/md/enc/stgtiggerstorage.cpp @@ -0,0 +1,977 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// StgTiggerStorage.cpp +// + +// +// TiggerStorage is a stripped down version of compound doc files. Doc files +// have some very useful and complex features to them, unfortunately nothing +// comes for free. Given the incredibly tuned format of existing .tlb files, +// every single byte counts and 10% added by doc files is just too expensive. +// +//***************************************************************************** +#include "stdafx.h" // Standard header. +#include "stgio.h" // I/O subsystem. +#include "stgtiggerstorage.h" // Our interface. +#include "stgtiggerstream.h" // Stream interface. +#include "corerror.h" +#include "posterror.h" +#include "mdfileformat.h" +#include "sstring.h" +#include "clrversion.h" + +TiggerStorage::TiggerStorage() : + m_pStgIO(0), + m_cRef(1), + m_pStreamList(0), + m_pbExtra(0) +{ + memset(&m_StgHdr, 0, sizeof(STORAGEHEADER)); +} + + +TiggerStorage::~TiggerStorage() +{ + if (m_pStgIO) + { + m_pStgIO->Release(); + m_pStgIO = 0; + } +} + + +//***************************************************************************** +// Init this storage object on top of the given storage unit. +//***************************************************************************** +HRESULT +TiggerStorage::Init( + StgIO *pStgIO, // The I/O subsystem. + __in __in_z LPSTR pVersion) // 'Compiled for' CLR version +{ + PSTORAGESIGNATURE pSig; // Signature data for file. + ULONG cbData; // Offset of header data. + void *ptr; // Signature. + HRESULT hr = S_OK; + + // Make sure we always start at the beginning. + // + pStgIO->Seek(0, FILE_BEGIN); + + // Save the storage unit. + m_pStgIO = pStgIO; + m_pStgIO->AddRef(); + + // For cases where the data already exists, verify the signature. + if ((pStgIO->GetFlags() & DBPROP_TMODEF_CREATE) == 0) + { + // Map the contents into memory for easy access. + IfFailGo(pStgIO->MapFileToMem(ptr, &cbData)); + + // Get a pointer to the signature of the file, which is the first part. + IfFailGo(pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr)); + + // Finally, we can check the signature. + pSig = (PSTORAGESIGNATURE)ptr; + IfFailGo(MDFormat::VerifySignature(pSig, cbData)); + + // Read and verify the header. + IfFailGo(ReadHeader()); + } + // For write case, dump the signature into the file up front. + else + { + IfFailGo(WriteSignature(pVersion)); + } + +ErrExit: + if (FAILED(hr) && (m_pStgIO != NULL)) + { + m_pStgIO->Release(); + m_pStgIO = NULL; + } + return hr; +} // TiggerStorage::Init + +//***************************************************************************** +// This function is a workaround to allow access to the "version requested" string. +//***************************************************************************** +HRESULT +TiggerStorage::GetHeaderPointer( + const void **ppv, // Put pointer to header here. + ULONG *pcb) // Put size of pointer here. +{ + void *ptr; // Working pointer. + HRESULT hr; + + // Read the signature + if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr))) + return hr; + + PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE) ptr; + // Header data starts after signature. + *pcb = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength(); + + *ppv = ptr; + + return S_OK; + +} // TiggerStorage::GetHeaderPointer + +//***************************************************************************** +// Get the default "Compiled for" version used to emit the meta-data +//***************************************************************************** +HRESULT +TiggerStorage::GetDefaultVersion( + LPCSTR *ppVersion) +{ + *ppVersion = CLR_METADATA_VERSION; + return S_OK; +} // TiggerStorage::GetDefaultVersion + +HRESULT +TiggerStorage::SizeOfStorageSignature(LPCSTR pVersion, ULONG *pcbSignatureSize) +{ + HRESULT hr; + + if (pVersion == NULL) + { + IfFailRet(GetDefaultVersion(&pVersion)); + } + _ASSERTE(pVersion != NULL); + + ULONG versionSize = (ULONG)strlen(pVersion)+1; + ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4); + + *pcbSignatureSize = sizeof(STORAGESIGNATURE) + alignedVersionSize; + return S_OK; +} + + +//***************************************************************************** +// Retrieves a the size and a pointer to the extra data that can optionally be +// written in the header of the storage system. This data is not required to +// be in the file, in which case *pcbExtra will come back as 0 and pbData will +// be set to NULL. You must have initialized the storage using Init() before +// calling this function. +// +// Return value: S_OK if found, S_FALSE, or error. +//***************************************************************************** +HRESULT +TiggerStorage::GetExtraData( + ULONG *pcbExtra, // Return size of extra data. + BYTE *&pbData) // Return a pointer to extra data. +{ + // Assuming there is extra data, then return the size and a pointer to it. + if (m_pbExtra != NULL) + { + if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) == 0) + { + Debug_ReportError("Inconsistent information about extra data in MetaData."); + return PostError(CLDB_E_FILE_CORRUPT); + } + *pcbExtra = *(ULONG *)m_pbExtra; + pbData = (BYTE *)((ULONG *) m_pbExtra + 1); + } + else + { + *pcbExtra = 0; + pbData = NULL; + return S_FALSE; + } + return S_OK; +} // TiggerStorage::GetExtraData + + +//***************************************************************************** +// Called when this stream is going away. +//***************************************************************************** +HRESULT +TiggerStorage::WriteHeader( + STORAGESTREAMLST *pList, // List of streams. + ULONG cbExtraData, // Size of extra data, may be 0. + BYTE *pbExtraData) // Pointer to extra data for header. +{ + ULONG iLen; // For variable sized data. + ULONG cbWritten; // Track write quantity. + HRESULT hr; + SAVETRACE(ULONG cbDebugSize); // Track debug size of header. + + SAVETRACE(DbgWriteEx(W("PSS: Header:\n"))); + + // Save the count and set flags. + m_StgHdr.SetiStreams(pList->Count()); + if (cbExtraData != 0) + m_StgHdr.AddFlags(STGHDR_EXTRADATA); + + // Write out the header of the file. + IfFailRet(m_pStgIO->Write(&m_StgHdr, sizeof(STORAGEHEADER), &cbWritten)); + + // Write out extra data if there is any. + if (cbExtraData != 0) + { + _ASSERTE(pbExtraData); + _ASSERTE((cbExtraData % 4) == 0); + + // First write the length value. + IfFailRet(m_pStgIO->Write(&cbExtraData, sizeof(ULONG), &cbWritten)); + + // And then the data. + IfFailRet(m_pStgIO->Write(pbExtraData, cbExtraData, &cbWritten)); + SAVETRACE(DbgWriteEx(W("PSS: extra data size %d\n"), m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset()); + } + + // Save off each data stream. + for (int i = 0; i < pList->Count(); i++) + { + PSTORAGESTREAM pStream = pList->Get(i); + + // How big is the structure (aligned) for this struct. + iLen = (ULONG)(sizeof(STORAGESTREAM) - MAXSTREAMNAME + strlen(pStream->GetName()) + 1); + + // Write the header including the name to disk. Does not include + // full name buffer in struct, just string and null terminator. + IfFailRet(m_pStgIO->Write(pStream, iLen, &cbWritten)); + + // Align the data out to 4 bytes. + if (iLen != ALIGN4BYTE(iLen)) + { + IfFailRet(m_pStgIO->Write(&hr, ALIGN4BYTE(iLen) - iLen, 0)); + } + SAVETRACE(DbgWriteEx(W("PSS: Table %hs header size %d\n"), pStream->rcName, m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset()); + } + SAVETRACE(DbgWriteEx(W("PSS: Total size of header data %d\n"), m_pStgIO->GetCurrentOffset())); + // Make sure the whole thing is 4 byte aligned. + _ASSERTE((m_pStgIO->GetCurrentOffset() % 4) == 0); + return S_OK; +} // TiggerStorage::WriteHeader + + +//***************************************************************************** +// Called when all data has been written. Forces cached data to be flushed +// and stream lists to be validated. +//***************************************************************************** +HRESULT +TiggerStorage::WriteFinished( + STORAGESTREAMLST *pList, // List of streams. + ULONG *pcbSaveSize, // Return size of total data. + BOOL fDeltaSave) // Was this a delta +{ + PSTORAGESTREAM pEntry; // Loop control. + HRESULT hr; + + // If caller wants the total size of the file, we are there right now. + if (pcbSaveSize != NULL) + *pcbSaveSize = m_pStgIO->GetCurrentOffset(); + + // Flush our internal write cache to disk. + IfFailRet(m_pStgIO->FlushCache()); + + // Force user's data onto disk right now so that Commit() can be + // more accurate (although not totally up to the D in ACID). + hr = m_pStgIO->FlushFileBuffers(); + _ASSERTE(SUCCEEDED(hr)); + + // Run through all of the streams and validate them against the expected + // list we wrote out originally. + + // Robustness check: stream counts must match what was written. + _ASSERTE(pList->Count() == m_Streams.Count()); + if (pList->Count() != m_Streams.Count()) + { + _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // If we're saving a true delta, then this sanity check won't help. + // @TODO - Implement a sanity check for the deltas + if (!fDeltaSave) + { + // Sanity check each saved stream data size and offset. + for (int i = 0; i < pList->Count(); i++) + { + pEntry = pList->Get(i); + + _ASSERTE(pEntry->GetOffset() == m_Streams[i].GetOffset()); + _ASSERTE(pEntry->GetSize() == m_Streams[i].GetSize()); + _ASSERTE(strcmp(pEntry->GetName(), m_Streams[i].GetName()) == 0); + + // For robustness, check that everything matches expected value, + // and if it does not, refuse to save the data and force a rollback. + // The alternative is corruption of the data file. + if ((pEntry->GetOffset() != m_Streams[i].GetOffset()) || + (pEntry->GetSize() != m_Streams[i].GetSize()) || + (strcmp(pEntry->GetName(), m_Streams[i].GetName()) != 0)) + { + _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption."); + hr = PostError(CLDB_E_FILE_CORRUPT); + break; + } + + //<REVISIT_TODO>@future: + // if iOffset or iSize mismatches, it means a bug in GetSaveSize + // which we can successfully detect right here. In that case, we + // could use the pStgIO and seek back to the header and correct the + // mistmake. This will break any client who lives on the GetSaveSize + // value which came back originally, but would be more robust than + // simply throwing back an error which will corrupt the file.</REVISIT_TODO> + } + } + return hr; +} // TiggerStorage::WriteFinished + + +//***************************************************************************** +// Called after a successful rewrite of an existing file. The in memory +// backing store is no longer valid because all new data is in memory and +// on disk. This is essentially the same state as created, so free up some +// working set and remember this state. +//***************************************************************************** +HRESULT TiggerStorage::ResetBackingStore() // Return code. +{ + return (m_pStgIO->ResetBackingStore()); +} + + +//***************************************************************************** +// Given the name of a stream that will be persisted into a stream in this +// storage type, figure out how big that stream would be including the user's +// stream data and the header overhead the file format incurs. The name is +// stored in ANSI and the header struct is aligned to 4 bytes. +//***************************************************************************** +HRESULT +TiggerStorage::GetStreamSaveSize( + LPCWSTR szStreamName, // Name of stream. + UINT32 cbDataSize, // Size of data to go into stream. + UINT32 *pcbSaveSize) // Return data size plus stream overhead. +{ + UINT32 cbTotalSize; // Add up each element. + + // Find out how large the name will be. + cbTotalSize = ::WszWideCharToMultiByte(CP_ACP, 0, szStreamName, -1, 0, 0, 0, 0); + _ASSERTE(cbTotalSize != 0); + + // Add the size of the stream header minus the static name array. + cbTotalSize += sizeof(STORAGESTREAM) - MAXSTREAMNAME; + + // Finally align the header value. + cbTotalSize = ALIGN4BYTE(cbTotalSize); + + // Return the size of the user data and the header data. + *pcbSaveSize = cbTotalSize + cbDataSize; + return S_OK; +} // TiggerStorage::GetStreamSaveSize + + +//***************************************************************************** +// Return the fixed size overhead for the storage implementation. This includes +// the signature and fixed header overhead. The overhead in the header for each +// stream is calculated as part of GetStreamSaveSize because these structs are +// variable sized on the name. +//***************************************************************************** +HRESULT TiggerStorage::GetStorageSaveSize( // Return code. + ULONG *pcbSaveSize, // [in] current size, [out] plus overhead. + ULONG cbExtra, // How much extra data to store in header. + LPCSTR pRuntimeVersion) +{ + HRESULT hr; + + ULONG cbSignatureSize; + IfFailRet(SizeOfStorageSignature(pRuntimeVersion, &cbSignatureSize)); + + *pcbSaveSize += cbSignatureSize + sizeof(STORAGEHEADER); + if (cbExtra) + *pcbSaveSize += sizeof(ULONG) + cbExtra; + return (S_OK); +} + + +//***************************************************************************** +// Adjust the offset in each known stream to match where it will wind up after +// a save operation. +//***************************************************************************** +HRESULT TiggerStorage::CalcOffsets( // Return code. + STORAGESTREAMLST *pStreamList, // List of streams for header. + ULONG cbExtra, // Size of variable extra data in header. + LPCSTR pRuntimeVersion) // The version string as it's length is part of the total size. +{ + PSTORAGESTREAM pEntry; // Each entry in the list. + ULONG cbOffset=0; // Running offset for streams. + int i; // Loop control. + + // Prime offset up front. + GetStorageSaveSize(&cbOffset, cbExtra, pRuntimeVersion); + + // Add on the size of each header entry. + for (i=0; i<pStreamList->Count(); i++) + { + VERIFY(pEntry = pStreamList->Get(i)); + cbOffset += sizeof(STORAGESTREAM) - MAXSTREAMNAME; + cbOffset += (ULONG)(strlen(pEntry->GetName()) + 1); + cbOffset = ALIGN4BYTE(cbOffset); + } + + // Go through each stream and reset its expected offset. + for (i=0; i<pStreamList->Count(); i++) + { + VERIFY(pEntry = pStreamList->Get(i)); + pEntry->SetOffset(cbOffset); + cbOffset += pEntry->GetSize(); + } + return (S_OK); +} + + + +HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream( + const OLECHAR *pwcsName, + DWORD grfMode, + DWORD reserved1, + DWORD reserved2, + IStream **ppstm) +{ + char rcStream[MAXSTREAMNAME];// For converted name. + VERIFY(Wsz_wcstombs(rcStream, pwcsName, sizeof(rcStream))); + return (CreateStream(rcStream, grfMode, reserved1, reserved2, ppstm)); +} + + +#ifndef DACCESS_COMPILE +HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream( + LPCSTR szName, + DWORD grfMode, + DWORD reserved1, + DWORD reserved2, + IStream **ppstm) +{ + PSTORAGESTREAM pStream; // For lookup. + HRESULT hr; + + _ASSERTE(szName && *szName); + + // Check for existing stream, which might be an error or more likely + // a rewrite of a file. + if (SUCCEEDED(FindStream(szName, &pStream))) + { + // <REVISIT_TODO>REVIEW: STGM_FAILIFTHERE is 0, the following condition will be always false</REVISIT_TODO> + if (pStream->GetOffset() != 0xffffffff && ((grfMode & STGM_CREATE) == STGM_FAILIFTHERE)) + return (PostError(STG_E_FILEALREADYEXISTS)); + } + // Add a control to track this stream. + else if (!pStream && (pStream = m_Streams.Append()) == 0) + return (PostError(OutOfMemory())); + pStream->SetOffset(0xffffffff); + pStream->SetSize(0); + strcpy_s(pStream->GetName(), 32, szName); + + // Now create a stream object to allow reading and writing. + TiggerStream *pNew = new (nothrow) TiggerStream; + if (!pNew) + return (PostError(OutOfMemory())); + *ppstm = (IStream *) pNew; + + // Init the new object. + if (FAILED(hr = pNew->Init(this, pStream->GetName()))) + { + delete pNew; + return (hr); + } + return (S_OK); +} +#endif //!DACCESS_COMPILE + + +HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream( + const OLECHAR *pwcsName, + void *reserved1, + DWORD grfMode, + DWORD reserved2, + IStream **ppstm) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStorage( + const OLECHAR *pwcsName, + DWORD grfMode, + DWORD dwStgFmt, + DWORD reserved2, + IStorage **ppstg) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE +TiggerStorage::OpenStorage( + const OLECHAR * wcsName, + IStorage * pStgPriority, + DWORD dwMode, + __in + SNB snbExclude, + DWORD reserved, + IStorage ** ppStg) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +TiggerStorage::CopyTo( + DWORD cIidExclude, + const IID * rgIidExclude, + __in + SNB snbExclude, + IStorage * pStgDest) +{ + return E_NOTIMPL; +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::MoveElementTo( + const OLECHAR *pwcsName, + IStorage *pstgDest, + const OLECHAR *pwcsNewName, + DWORD grfFlags) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::Commit( + DWORD grfCommitFlags) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::Revert() +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::EnumElements( + DWORD reserved1, + void *reserved2, + DWORD reserved3, + IEnumSTATSTG **ppenum) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::DestroyElement( + const OLECHAR *pwcsName) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::RenameElement( + const OLECHAR *pwcsOldName, + const OLECHAR *pwcsNewName) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::SetElementTimes( + const OLECHAR *pwcsName, + const FILETIME *pctime, + const FILETIME *patime, + const FILETIME *pmtime) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::SetClass( + REFCLSID clsid) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::SetStateBits( + DWORD grfStateBits, + DWORD grfMask) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::Stat( + STATSTG *pstatstg, + DWORD grfStatFlag) +{ + return (E_NOTIMPL); +} + + + +HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream( + LPCWSTR szStream, + ULONG *pcbData, + void **ppAddress) +{ + PSTORAGESTREAM pStream; // For lookup. + char rcName[MAXSTREAMNAME]; // For conversion. + HRESULT hr; + + // Convert the name for internal use. + VERIFY(::WszWideCharToMultiByte(CP_ACP, 0, szStream, -1, rcName, sizeof(rcName), 0, 0)); + + // Look for the stream which must be found for this to work. Note that + // this error is explicitly not posted as an error object since unfound streams + // are a common occurrence and do not warrant a resource file load. + IfFailRet(FindStream(rcName, &pStream)); + + // Get the memory for the stream. + IfFailRet( m_pStgIO->GetPtrForMem(pStream->GetOffset(), pStream->GetSize(), *ppAddress) ); + *pcbData = pStream->GetSize(); + return (S_OK); +} + + + +// +// Protected. +// + + +//***************************************************************************** +// Called by the stream implementation to write data out to disk. +//***************************************************************************** +HRESULT +TiggerStorage::Write( + LPCSTR szName, // Name of stream we're writing. + const void *pData, // Data to write. + ULONG cbData, // Size of data. + ULONG *pcbWritten) // How much did we write. +{ + PSTORAGESTREAM pStream; // Update size data. + ULONG iOffset = 0; // Offset for write. + ULONG cbWritten; // Handle null case. + HRESULT hr; + + // Get the stream descriptor. + if (FAILED(FindStream(szName, &pStream))) + return CLDB_E_FILE_BADWRITE; + + // If we need to know the offset, keep it now. + if (pStream->GetOffset() == 0xffffffff) + { + iOffset = m_pStgIO->GetCurrentOffset(); + + // Align the storage on a 4 byte boundary. + if ((iOffset % 4) != 0) + { + ULONG cb; + ULONG pad = 0; + + if (FAILED(hr = m_pStgIO->Write(&pad, ALIGN4BYTE(iOffset) - iOffset, &cb))) + return hr; + iOffset = m_pStgIO->GetCurrentOffset(); + + _ASSERTE((iOffset % 4) == 0); + } + } + + // Avoid confusion. + if (pcbWritten == NULL) + pcbWritten = &cbWritten; + *pcbWritten = 0; + + // Let OS do the write. + if (SUCCEEDED(hr = m_pStgIO->Write(pData, cbData, pcbWritten))) + { + // On success, record the new data. + if (pStream->GetOffset() == 0xffffffff) + pStream->SetOffset(iOffset); + pStream->SetSize(pStream->GetSize() + *pcbWritten); + return S_OK; + } + else + { + return hr; + } +} // TiggerStorage::Write + + +// +// Private +// + +HRESULT +TiggerStorage::FindStream( + LPCSTR szName, + __out PSTORAGESTREAM *stream) +{ + *stream = NULL; + // In read mode, just walk the list and return one. + if (m_pStreamList != NULL) + { + PSTORAGESTREAM p = m_pStreamList; + + SIZE_T pStartMD = (SIZE_T)(m_pStgIO->m_pData); + SIZE_T pEndMD = NULL; + + if (!ClrSafeInt<SIZE_T>::addition(pStartMD, m_pStgIO->m_cbData, pEndMD)) + { + Debug_ReportError("Invalid MetaData storage headers - size overflow."); + return CLDB_E_FILE_CORRUPT; + } + + for (int i = 0; i < m_StgHdr.GetiStreams(); i++) + { + // Make sure this stream pointer is still inside the metadata + if (((SIZE_T)p < pStartMD) || ((SIZE_T)p > pEndMD)) + { + Debug_ReportError("Invalid MetaData storage header - reached outside headers block."); + return CLDB_E_FILE_CORRUPT; + } + + if (SString::_stricmp(p->GetName(), szName) == 0) + { + *stream = p; + return S_OK; + } + p = p->NextStream(); + } + } + // In write mode, walk the array which is not on disk yet. + else + { + for (int j = 0; j < m_Streams.Count(); j++) + { + if (SString::_stricmp(m_Streams[j].GetName(), szName) == 0) + { + *stream = &m_Streams[j]; + return S_OK; + } + } + } + return STG_E_FILENOTFOUND; +} // TiggerStorage::FindStream + + +//***************************************************************************** +// Write the signature area of the file format to disk. This includes the +// "magic" identifier and the version information. +//***************************************************************************** +HRESULT +TiggerStorage::WriteSignature( + LPCSTR pVersion) +{ + STORAGESIGNATURE sSig; + ULONG cbWritten; + HRESULT hr = S_OK; + + if (pVersion == NULL) + { + IfFailRet(GetDefaultVersion(&pVersion)); + } + _ASSERTE(pVersion != NULL); + + ULONG versionSize = (ULONG)strlen(pVersion) + 1; + ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4); + + // Signature belongs at the start of the file. + _ASSERTE(m_pStgIO->GetCurrentOffset() == 0); + + sSig.SetSignature(STORAGE_MAGIC_SIG); + sSig.SetMajorVer(FILE_VER_MAJOR); + sSig.SetMinorVer(FILE_VER_MINOR); + sSig.SetExtraDataOffset(0); // We have no extra inforation + sSig.SetVersionStringLength(alignedVersionSize); + IfFailRet(m_pStgIO->Write(&sSig, sizeof(STORAGESIGNATURE), &cbWritten)); + IfFailRet(m_pStgIO->Write(pVersion, versionSize, &cbWritten)); + + // Write padding + if (alignedVersionSize - versionSize != 0) + { + BYTE padding[4]; + ZeroMemory(padding, sizeof(padding)); + IfFailRet(m_pStgIO->Write(padding, alignedVersionSize - versionSize, &cbWritten)); + } + + return hr; +} // TiggerStorage::WriteSignature + + +//***************************************************************************** +// Read the header from disk. This reads the header for the most recent version +// of the file format which has the header at the front of the data file. +//***************************************************************************** +HRESULT +TiggerStorage::ReadHeader() +{ + PSTORAGESTREAM pAppend, pStream; // For copy of array. + void *ptr; // Working pointer. + ULONG iOffset; // Offset of header data. + ULONG cbExtra; // Size of extra data. + ULONG cbRead; // For calc of read sizes. + HRESULT hr; + + // Read the signature + if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr))) + { + Debug_ReportError("Cannot read MetaData storage signature header."); + return hr; + } + + PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE)ptr; + + // Header data starts after signature. + iOffset = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength(); + + // Read the storage header which has the stream counts. Throw in the extra + // count which might not exist, but saves us down stream. + if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr))) + { + Debug_ReportError("Cannot read first MetaData storage header."); + return hr; + } + _ASSERTE(m_pStgIO->IsAlignedPtr((ULONG_PTR) ptr, 4)); + + // Read the storage header which has the stream counts. Throw in the extra + // count which might not exist, but saves us down stream. + if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr))) + { + Debug_ReportError("Cannot read second MetaData storage header."); + return hr; + } + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4)) + { + Debug_ReportError("Invalid MetaData storage headers - unaligned size."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // Copy the header into memory and check it. + memcpy(&m_StgHdr, ptr, sizeof(STORAGEHEADER)); + IfFailRet( VerifyHeader() ); + ptr = (void *)((PSTORAGEHEADER)ptr + 1); + iOffset += sizeof(STORAGEHEADER); + + // Save off a pointer to the extra data. + if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) != 0) + { + m_pbExtra = ptr; + cbExtra = sizeof(ULONG) + *(ULONG *)ptr; + + // Force the extra data to get faulted in. + IfFailRet(m_pStgIO->GetPtrForMem(iOffset, cbExtra, ptr)); + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4)) + { + Debug_ReportError("Invalid MetaData storage signature - unaligned extra data."); + return PostError(CLDB_E_FILE_CORRUPT); + } + } + else + { + m_pbExtra = 0; + cbExtra = 0; + } + iOffset += cbExtra; + + // Force the worst case scenario of bytes to get faulted in for the + // streams. This makes the rest of this code very simple. + cbRead = sizeof(STORAGESTREAM) * m_StgHdr.GetiStreams(); + if (cbRead != 0) + { + cbRead = min(cbRead, m_pStgIO->GetDataSize() - iOffset); + if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, cbRead, ptr))) + { + Debug_ReportError("Invalid MetaData stogare headers."); + return hr; + } + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4)) + { + Debug_ReportError("Invalid MetaData stogare headers - unaligned start."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // For read only, just access the header data. + if (m_pStgIO->IsReadOnly()) + { + // Save a pointer to the current list of streams. + m_pStreamList = (PSTORAGESTREAM)ptr; + } + // For writeable, need a copy we can modify. + else + { + pStream = (PSTORAGESTREAM)ptr; + + // Copy each of the stream headers. + for (int i = 0; i < m_StgHdr.GetiStreams(); i++) + { + if ((pAppend = m_Streams.Append()) == NULL) + return PostError(OutOfMemory()); + // Validate that the stream header is not too big. + ULONG sz = pStream->GetStreamSize(); + if (sz > sizeof(STORAGESTREAM)) + { + Debug_ReportError("Invalid MetaData storage stream - data too big."); + return PostError(CLDB_E_FILE_CORRUPT); + } + memcpy (pAppend, pStream, sz); + pStream = pStream->NextStream(); + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)pStream, 4)) + { + Debug_ReportError("Invalid MetaData storage stream - unaligned data."); + return PostError(CLDB_E_FILE_CORRUPT); + } + } + + // All must be loaded and accounted for. + _ASSERTE(m_StgHdr.GetiStreams() == m_Streams.Count()); + } + } + return S_OK; +} // TiggerStorage::ReadHeader + + +//***************************************************************************** +// Verify the header is something this version of the code can support. +//***************************************************************************** +HRESULT TiggerStorage::VerifyHeader() +{ + //<REVISIT_TODO>@FUTURE: add version check for format.</REVISIT_TODO> + return S_OK; +} + +//***************************************************************************** +// Print the sizes of the various streams. +//***************************************************************************** +#if defined(_DEBUG) +ULONG TiggerStorage::PrintSizeInfo(bool verbose) +{ + ULONG total = 0; + + printf("Storage Header: %zu\n", sizeof(STORAGEHEADER)); + if (m_pStreamList != NULL) + { + PSTORAGESTREAM storStream = m_pStreamList; + PSTORAGESTREAM pNext; + for (int i = 0; i < m_StgHdr.GetiStreams(); i++) + { + pNext = storStream->NextStream(); + printf("Stream #%d (%s) Header: %zd, Data: %lu\n",i,storStream->GetName(), (size_t)((BYTE*)pNext - (BYTE*)storStream), storStream->GetSize()); + total += storStream->GetSize(); + storStream = pNext; + } + } + else + { + //<REVISIT_TODO>todo: Add support for the case where m_Streams exists and m_pStreamList does not</REVISIT_TODO> + } + + if (m_pbExtra != NULL) + { + printf("Extra bytes: %d\n",*(ULONG*)m_pbExtra); + total += *(ULONG*)m_pbExtra; + } + return total; +} +#endif // _DEBUG diff --git a/src/coreclr/md/enc/stgtiggerstream.cpp b/src/coreclr/md/enc/stgtiggerstream.cpp new file mode 100644 index 00000000000..3e5eba9b82f --- /dev/null +++ b/src/coreclr/md/enc/stgtiggerstream.cpp @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// StgTiggerStream.h +// + +// +// TiggerStream is the companion to the TiggerStorage CoClass. It handles the +// streams managed inside of the storage and does the direct file i/o. +// +//***************************************************************************** +#include "stdafx.h" +#include "stgtiggerstream.h" +#include "stgtiggerstorage.h" +#include "posterror.h" + +// +// +// IStream +// +// + + +HRESULT STDMETHODCALLTYPE TiggerStream::Read( + void *pv, + ULONG cb, + ULONG *pcbRead) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::Write( + const void *pv, + ULONG cb, + ULONG *pcbWritten) +{ + return (m_pStorage->Write(m_rcStream, pv, cb, pcbWritten)); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::Seek( + LARGE_INTEGER dlibMove, + DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::SetSize( + ULARGE_INTEGER libNewSize) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::CopyTo( + IStream *pstm, + ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, + ULARGE_INTEGER *pcbWritten) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::Commit( + DWORD grfCommitFlags) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::Revert() +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::LockRegion( + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::UnlockRegion( + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::Stat( + STATSTG *pstatstg, + DWORD grfStatFlag) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStream::Clone( + IStream **ppstm) +{ + return (E_NOTIMPL); +} + + + + + + +HRESULT TiggerStream::Init( // Return code. + TiggerStorage *pStorage, // Parent storage. + LPCSTR szStream) // Stream name. +{ + // Save off the parent data source object and stream name. + m_pStorage = pStorage; + strncpy_s(m_rcStream, sizeof(m_rcStream), szStream, sizeof(m_rcStream)-1); + m_rcStream[sizeof(m_rcStream)-1] = '\0'; // force nul termination + return (S_OK); +} + + +ULONG TiggerStream::GetStreamSize() +{ + PSTORAGESTREAM pStreamInfo; + if (FAILED(m_pStorage->FindStream(m_rcStream, &pStreamInfo))) + return 0; + return (pStreamInfo->GetSize()); +} diff --git a/src/coreclr/md/errors_metadata.h b/src/coreclr/md/errors_metadata.h new file mode 100644 index 00000000000..a21075d7df4 --- /dev/null +++ b/src/coreclr/md/errors_metadata.h @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include <corerror.h> +#include <winerror.h> + +// Index into heap/table is too large. +#define METADATA_E_INDEX_NOTFOUND CLDB_E_INDEX_NOTFOUND + // Options: + // * CLDB_E_INDEX_NOTFOUND + // * VLDTR_E_BLOB_INVALID + // * VLDTR_E_GUID_INVALID + // * VLDTR_E_STRING_INVALID + // * VLDTR_E_RID_OUTOFRANGE + +// Internal error, it's a runtime assert check to avoid security errors. If this is returned, then there's +// something wrong with MetaData code. +#define METADATA_E_INTERNAL_ERROR CLDB_E_INTERNALERROR + // Options: + // * CLDB_E_INTERNALERROR + // * COR_E_EXECUTIONENGINE + +// MetaData space (heap/table) is full, cannot store more items. +#define METADATA_E_HEAP_FULL META_E_STRINGSPACE_FULL + // Options: + // * META_E_STRINGSPACE_FULL + // * CLDB_E_TOO_BIG + +// Invalid heap (blob, user string) data encoding. +#define METADATA_E_INVALID_HEAP_DATA META_E_BADMETADATA + // Options: + // * META_E_BADMETADATA + // * META_E_CA_INVALID_BLOB + // * META_E_BAD_SIGNATURE + // * CLDB_E_FILE_CORRUPT + // * COR_E_BADIMAGEFORMAT + +// The data is too big to encode (the string/blob is larger than possible heap size). +#define METADATA_E_DATA_TOO_BIG CLDB_E_TOO_BIG + // Options: + // * CLDB_E_TOO_BIG + +// Invalid MetaData format (headers, etc.). +#define METADATA_E_INVALID_FORMAT COR_E_BADIMAGEFORMAT + // Options: + // * META_E_BADMETADATA + // * META_E_CA_INVALID_BLOB + // * META_E_BAD_SIGNATURE + // * CLDB_E_FILE_CORRUPT + // * COR_E_BADIMAGEFORMAT + +// +// Other used error codes: +// * COR_E_OUTOFMEMORY ... defined as E_OUTOFMEMORY +// Alternatives: +// * E_OUTOFMEMORY (from IfNullGo/IfNullRet macros) +// * COR_E_OVERFLOW +// Alternatives: +// * COR_E_ARITHMETIC +// diff --git a/src/coreclr/md/export.h b/src/coreclr/md/export.h new file mode 100644 index 00000000000..2991052962e --- /dev/null +++ b/src/coreclr/md/export.h @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: export.h +// + +// +// Popular types/macros defined in MetaData directory (no subdirectories). +// It's supposed to be included from other subcomponents (i.e. subdirectories), not from this directory. +// +// ====================================================================================== + +#pragma once + +#include "debug_metadata.h" +#include "errors_metadata.h" +#include "datablob.h" diff --git a/src/coreclr/md/external.h b/src/coreclr/md/external.h new file mode 100644 index 00000000000..990ef4a2829 --- /dev/null +++ b/src/coreclr/md/external.h @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: external.h +// + +// +// External types used in MetaData\* subcomponent classes. +// +// ====================================================================================== + +#pragma once + +#include <clrtypes.h> +#include <safemath.h> +#include <new.hpp> diff --git a/src/coreclr/md/heaps/blobheap.h b/src/coreclr/md/heaps/blobheap.h new file mode 100644 index 00000000000..7d9eaf1ec03 --- /dev/null +++ b/src/coreclr/md/heaps/blobheap.h @@ -0,0 +1,323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: BlobHeap.h +// + +// +// Classes code:MetaData::BlobHeapRO and code:MetaData::BlobHeapRW represent #Blob heap. +// The #Blob heap stores size-prefixed data chunks (as defined in CLI ECMA specification). Elements are +// indexed by code:#BlobHeapIndex. +// +//#BlobHeapIndex +// Blob heap indexes are 0-based. They are stored the same way in the table columns (i.e. there is no +// 0-based vs. 1-based index difference as in table record indexes code:TableRecordStorage). +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class represents read-only #Blob heap with all utility methods. +// +class BlobHeapRO +{ + friend class BlobHeapRW; + +private: + // + // Private data + // + + // The storage of blobs. + StgBlobPoolReadOnly m_BlobPool; + +public: + // + // Initialization + // + + __checkReturn + HRESULT Initialize( + DataBlob sourceData, + BOOL fCopyData) + { + _ASSERTE(!fCopyData); + return m_BlobPool.InitOnMemReadOnly((void *)sourceData.GetDataPointer(), sourceData.GetSize()); + } + +#ifdef FEATURE_PREJIT + // Can be called multiple times. + inline void InitializeHotData( + HotHeap hotHeap) + { + m_BlobPool.InitHotData(hotHeap); + } +#endif //FEATURE_PREJIT + + inline void Delete() + { + return m_BlobPool.Uninit(); + } + +public: + // + // Getters + // + + __checkReturn + inline HRESULT GetBlob( + UINT32 nIndex, + __out DataBlob *pData) + { + return m_BlobPool.GetBlob(nIndex, pData); + } + + __checkReturn + inline HRESULT GetAllData( + __inout DataBlob *pData) + { + return m_BlobPool.GetDataReadOnly(0, pData); + } + + // Gets raw size (in bytes) of the represented blob data. + inline UINT32 GetUnalignedSize() const + { + return m_BlobPool.GetPoolSize(); + } + + // Returns TRUE if the blob index (nIndex, see code:#BlobHeapIndex) is valid (i.e. in the blob + // heap). + inline BOOL IsValidIndex(UINT32 nIndex) const + { + return const_cast<StgBlobPoolReadOnly &>(m_BlobPool).IsValidCookie(nIndex); + } + +}; // class BlobHeapRO + +// -------------------------------------------------------------------------------------- +// +// This class represents read-write #Blob heap with all utility methods. +// +class BlobHeapRW +{ +private: + // + // Private data + // + + // The storage of blobs. + StgBlobPool m_BlobPool; + +public: + // + // Initialization + // + + __checkReturn + HRESULT InitializeEmpty( + UINT32 cbAllocationSize + COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite)) + { + return m_BlobPool.InitNew(cbAllocationSize, 0, TRUE); + } + __checkReturn + HRESULT InitializeEmpty_WithItemsCount( + UINT32 cbAllocationSize, + UINT32 cItemsCount + COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite)) + { + return m_BlobPool.InitNew(cbAllocationSize, cItemsCount, TRUE); + } + __checkReturn + HRESULT InitializeEmpty_WithoutDefaultEmptyBlob( + UINT32 cbAllocationSize + COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite)) + { + return m_BlobPool.InitNew(cbAllocationSize, 0, FALSE); + } + + __checkReturn + HRESULT Initialize( + DataBlob sourceData, + BOOL fCopyData) + { + return m_BlobPool.InitOnMem((void *)sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData); + } + __checkReturn + HRESULT InitializeFromBlobHeap( + const BlobHeapRO *pSourceBlobHeap, + BOOL fCopyData) + { + return m_BlobPool.InitOnMem( + (void *)pSourceBlobHeap->m_BlobPool.GetSegData(), + pSourceBlobHeap->m_BlobPool.GetDataSize(), + !fCopyData); + } + __checkReturn + HRESULT InitializeFromBlobHeap( + const BlobHeapRW *pSourceBlobHeap, + BOOL fCopyData) + { + return m_BlobPool.InitOnMem( + (void *)pSourceBlobHeap->m_BlobPool.GetSegData(), + pSourceBlobHeap->m_BlobPool.GetDataSize(), + !fCopyData); + } + + // Destroys the blob heap and all its allocated data. Can run on uninitialized blob heap. + inline void Delete() + { + return m_BlobPool.Uninit(); + } + +public: + // + // Getters + // + + __checkReturn + inline HRESULT GetBlob( + UINT32 nIndex, + __out DataBlob *pData) + { + return m_BlobPool.GetBlob(nIndex, pData); + } + + // Gets the blob with its size-prefix at index (nIndex, see code:#BlobHeapIndex), or returns error. + // + // Returns S_OK and the data (*pData) at index (nIndex). The end of the data marks the end of the blob. + // Returns error code otherwise (and clears *pData). + // + // User of this API shouldn't access memory behind the data buffer (*pData). + __checkReturn + inline HRESULT GetBlobWithSizePrefix( + UINT32 nIndex, + __out DataBlob *pData) + { + return m_BlobPool.GetBlobWithSizePrefix(nIndex, pData); + } + + // Gets raw size (in bytes) of the represented blob data. Doesn't align the size as code:GetAlignedSize. + inline UINT32 GetUnalignedSize() const + { + return m_BlobPool.GetRawSize(); + } + // Gets size (in bytes) aligned up to 4-bytes of the represented blob data. + // Fills *pcbSize with 0 on error. + __checkReturn + inline HRESULT GetAlignedSize( + __out UINT32 *pcbSize) const + { + return m_BlobPool.GetSaveSize(pcbSize); + } + // Returns TRUE if the blob heap is empty (even if it contains only default empty blob). + inline BOOL IsEmpty() const + { + return const_cast<StgBlobPool &>(m_BlobPool).IsEmpty(); + } + + // Returns TRUE if the blob index (nIndex, see code:#BlobHeapIndex) is valid (i.e. in the blob + // heap). + inline BOOL IsValidIndex(UINT32 nIndex) const + { + return const_cast<StgBlobPool &>(m_BlobPool).IsValidCookie(nIndex); + } + + __checkReturn + HRESULT SaveToStream_Aligned( + UINT32 nStartIndex, + __in IStream *pStream) const + { + if (nStartIndex == 0) + { + return const_cast<StgBlobPool &>(m_BlobPool).PersistToStream(pStream); + } + + if (nStartIndex == m_BlobPool.GetRawSize()) + { + _ASSERTE(!m_BlobPool.HaveEdits()); + return S_OK; + } + _ASSERTE(m_BlobPool.HaveEdits()); + _ASSERTE(nStartIndex == m_BlobPool.GetOffsetOfEdit()); + return const_cast<StgBlobPool &>(m_BlobPool).PersistPartialToStream(pStream, nStartIndex); + } + +public: + // + // Heap modifications + // + + __checkReturn + inline HRESULT AddBlob( + DataBlob data, + __out UINT32 *pnIndex) + { + return m_BlobPool.AddBlob(&data, pnIndex); + } + + __checkReturn + HRESULT AddBlobHeap( + const BlobHeapRW *pSourceBlobHeap, + UINT32 nStartSourceIndex) + { + return m_BlobPool.CopyPool( + nStartSourceIndex, + &pSourceBlobHeap->m_BlobPool); + } // BlobHeapRW::AddBlobHeap + + __checkReturn + inline HRESULT MakeWritable() + { + return m_BlobPool.ConvertToRW(); + } + +public: + // + // Tracking of heap modifications for EnC + // + + //#EnCSessionTracking + // EnC session starts automatically with initialization (code:Initialize or code:InitializeEmpty) or by + // user's explicit call to code:StartNewEnCSession. The heap stores its actual data size, so we can find + // out if some data were added later. + + // Gets heap size (in bytes) from the beginning of the last EnC session (code:#EnCSessionTracking). + inline UINT32 GetEnCSessionStartHeapSize() const + { + if (m_BlobPool.HaveEdits()) + { + return m_BlobPool.GetOffsetOfEdit(); + } + + return m_BlobPool.GetRawSize(); + } + // Starts new EnC session (code:#EnCSessionTracking). + inline void StartNewEnCSession() + { + m_BlobPool.ResetOffsetOfEdit(); + } + // Gets size (in bytes) aligned to 4-bytes of adds made from the beginning of the last EnC session. + __checkReturn + inline HRESULT GetEnCSessionAddedHeapSize_Aligned( + __out UINT32 *pcbSize) const + { + if (m_BlobPool.HaveEdits()) + { + return m_BlobPool.GetEditSaveSize(pcbSize); + } + + *pcbSize = 0; + return S_OK; + } + +}; // class BlobHeapRW + +}; // namespace MetaData diff --git a/src/coreclr/md/heaps/export.h b/src/coreclr/md/heaps/export.h new file mode 100644 index 00000000000..f4c8eaf427a --- /dev/null +++ b/src/coreclr/md/heaps/export.h @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: export.h +// + +// +// Popular types defined in MetaData\Heaps directory. +// It's supposed to be included from other (MetaData) subcomponents, not from this directory. +// +// ====================================================================================== + +#pragma once + +#include "stringheap.h" +#include "blobheap.h" +#include "guidheap.h" diff --git a/src/coreclr/md/heaps/external.h b/src/coreclr/md/heaps/external.h new file mode 100644 index 00000000000..6ba7685e097 --- /dev/null +++ b/src/coreclr/md/heaps/external.h @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: external.h +// + +// +// External types used in MetaData\Heaps subcomponent classes. +// This file is used for precompiled headers, so it has to be included at the beginning of every .cpp in +// this directory. +// +// ====================================================================================== + +#pragma once + +#include "../external.h" +#include "../export.h" + +#include <stgpool.h> +#include <metamodelpub.h> +#include <utilcode.h> diff --git a/src/coreclr/md/heaps/guidheap.h b/src/coreclr/md/heaps/guidheap.h new file mode 100644 index 00000000000..cb4dac2147a --- /dev/null +++ b/src/coreclr/md/heaps/guidheap.h @@ -0,0 +1,258 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: GuidHeap.h +// + +// +// Classes code:MetaData::GuidHeapRO and code:MetaData::GuidHeapRW represent #GUID heap. +// The #GUID heap stores size-prefixed data chunks (as defined in CLI ECMA specification). Elements are +// indexed by code:#GuidHeapIndex. +// +//#GuidHeapIndex +// Guid heap indexes are 1-based and they are really indexes, not offsets (as in string heap). +// The indexes correspond to: +// * 0 ... invalid index, +// * 1 ... data offset 0, +// * 2 ... data offset sizeof(GUID), +// * n ... data offset (n-1)*sizeof(GUID). +// Note that this class provides only translation from 1-based index to 0-based index. The translation of +// 0-based index to data offset is done in code:GuidHeapStorage::GetGuid. +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class represents read-only #GUID heap with all utility methods. +// +class GuidHeapRO +{ + friend class GuidHeapRW; + +private: + // + // Private data + // + + // The storage of guids. + StgPoolReadOnly m_GuidPool; + +public: + // + // Initialization + // + + __checkReturn + inline HRESULT Initialize( + DataBlob sourceData, + BOOL fCopyData) + { + _ASSERTE(!fCopyData); + return m_GuidPool.InitOnMemReadOnly((void *)sourceData.GetDataPointer(), sourceData.GetSize()); + } + +#ifdef FEATURE_PREJIT + // Can be called multiple times. + inline void InitializeHotData( + HotHeap hotHeap) + { + m_GuidPool.InitHotData(hotHeap); + } +#endif //FEATURE_PREJIT + + // Destroys the guid heap and all its allocated data. Can run on uninitialized guid heap. + inline void Delete() + { + return m_GuidPool.Uninit(); + } + +public: + // + // Getters + // + + // Gets pointer to guid (*ppGuid) at index (nIndex, see code:#GuidHeapIndex). + // Returns error code for invalid index (0, or too large index) and sets *ppGuid to NULL. + __checkReturn + inline HRESULT GetGuid( + UINT32 nIndex, + __deref_out GUID UNALIGNED **ppGuid) + { + return m_GuidPool.GetGuid(nIndex, ppGuid); + } + __checkReturn + inline HRESULT GetGuid( + UINT32 nIndex, + __deref_out const GUID UNALIGNED **ppGuid) const + { + return const_cast<StgPoolReadOnly &>(m_GuidPool).GetGuid(nIndex, const_cast<GUID UNALIGNED **>(ppGuid)); + } + + inline UINT32 GetSize() const + { + return const_cast<StgPoolReadOnly &>(m_GuidPool).GetPoolSize(); + } + +}; // class GuidHeapRO + +// -------------------------------------------------------------------------------------- +// +// This class represents read-write #GUID heap with all utility methods. +// +class GuidHeapRW +{ +private: + // + // Private data + // + + // The storage of guids. + StgGuidPool m_GuidPool; + +public: + // + // Initialization + // + + __checkReturn + inline HRESULT InitializeEmpty( + UINT32 cbAllocationSize + COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite)) + { + return m_GuidPool.InitNew(cbAllocationSize, 0); + } + __checkReturn + inline HRESULT InitializeEmpty_WithItemsCount( + UINT32 cbAllocationSize, + UINT32 cItemsCount + COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite)) + { + return m_GuidPool.InitNew(cbAllocationSize, cItemsCount); + } + __checkReturn + inline HRESULT Initialize( + DataBlob sourceData, + BOOL fCopyData) + { + return m_GuidPool.InitOnMem((void *)sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData); + } + + __checkReturn + inline HRESULT InitializeFromGuidHeap( + const GuidHeapRO *pSourceGuidHeap, + BOOL fCopyData) + { + return m_GuidPool.InitOnMem( + (void *)pSourceGuidHeap->m_GuidPool.GetSegData(), + pSourceGuidHeap->m_GuidPool.GetDataSize(), + !fCopyData); + } + __checkReturn + inline HRESULT InitializeFromGuidHeap( + const GuidHeapRW *pSourceGuidHeap, + BOOL fCopyData) + { + return m_GuidPool.InitOnMem( + (void *)pSourceGuidHeap->m_GuidPool.GetSegData(), + pSourceGuidHeap->m_GuidPool.GetDataSize(), + !fCopyData); + } + + // Destroys the guid heap and all its allocated data. Can run on uninitialized guid heap. + inline void Delete() + { + return m_GuidPool.Uninit(); + } + +public: + // + // Getters + // + + __checkReturn + inline HRESULT GetGuid( + UINT32 nIndex, + __deref_out GUID UNALIGNED **ppGuid) + { + return m_GuidPool.GetGuid(nIndex, ppGuid); + } + __checkReturn + inline HRESULT GetGuid( + UINT32 nIndex, + __deref_out const GUID UNALIGNED **ppGuid) const + { + return const_cast<StgGuidPool &>(m_GuidPool).GetGuid(nIndex, const_cast<GUID UNALIGNED **>(ppGuid)); + } + + // Gets size (in bytes) of the represented guid data. Note: the size is everytime aligned. + inline UINT32 GetSize() const + { + _ASSERTE(m_GuidPool.GetRawSize() % sizeof(GUID) == 0); + return m_GuidPool.GetRawSize(); + } + + // Returns TRUE if the guid heap is empty. + inline BOOL IsEmpty() const + { + return const_cast<StgGuidPool &>(m_GuidPool).IsEmpty(); + } + + // Returns TRUE if the guid index (nIndex, see code:#GuidHeapIndex) is valid (i.e. is in the guid + // heap). + // Note: index 0 is considered invalid. + inline BOOL IsValidIndex(UINT32 nIndex) const + { + return const_cast<StgGuidPool &>(m_GuidPool).IsValidCookie(nIndex); + } + + __checkReturn + inline HRESULT SaveToStream( + __in IStream *pStream) const + { + return const_cast<StgGuidPool &>(m_GuidPool).PersistToStream(pStream); + } + +public: + // + // Heap modifications + // + + // Adds guid (*pGuid) to the end of the heap. + // Returns S_OK and index (*pnIndex, see code:#GuidHeapIndex) of added GUID. + // Returns error code otherwise (and fills *pnIndex with 0 - an invalid GUID index). + __checkReturn + inline HRESULT AddGuid( + __in const GUID *pGuid, + __out UINT32 *pnIndex) + { + return m_GuidPool.AddGuid(pGuid, pnIndex); + } + + // Adds data from *pSourceGuidHeap starting at index (nStartSourceIndex) to the guid heap. + // Returns S_OK (even if the source is empty) or error code. + __checkReturn + HRESULT AddGuidHeap( + const GuidHeapRW *pSourceGuidHeap, + UINT32 nStartSourceIndex) + { + return m_GuidPool.CopyPool( + nStartSourceIndex, + &pSourceGuidHeap->m_GuidPool); + } // GuidHeapRW::AddGuidHeap + + __checkReturn + inline HRESULT MakeWritable() + { + return m_GuidPool.ConvertToRW(); + } + +}; // class GuidHeapRW + +}; // namespace MetaData diff --git a/src/coreclr/md/heaps/stringheap.h b/src/coreclr/md/heaps/stringheap.h new file mode 100644 index 00000000000..747fd196284 --- /dev/null +++ b/src/coreclr/md/heaps/stringheap.h @@ -0,0 +1,306 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: StringHeap.h +// + +// +// Classes code:MetaData::StringHeapRO and code:MetaData::StringHeapRW represent #String heap. +// The #String heap stores null-terminated UTF-8 strings (as defined in CLI ECMA specification). Elements +// are indexed by code:#StringHeapIndex. +// +//#StringHeapIndex +// String heap indexes are 0-based. They are stored the same way in the table columns (i.e. there is no +// 0-based vs. 1-based index difference as in table record indexes code:TableRecordStorage). +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class represents read-only #String heap with all utility methods. +// +class StringHeapRO +{ + friend class StringHeapRW; + +private: + // + // Private data + // + + // The storage of strings. + StgPoolReadOnly m_StringPool; + +public: + // + // Initialization + // + + __checkReturn + inline HRESULT Initialize( + DataBlob sourceData, + BOOL fCopyData) + { + _ASSERTE(!fCopyData); + return m_StringPool.InitOnMemReadOnly((void *)sourceData.GetDataPointer(), sourceData.GetSize()); + } + +#ifdef FEATURE_PREJIT + // Can be called multiple times. + inline void InitializeHotData( + HotHeap hotHeap) + { + m_StringPool.InitHotData(hotHeap); + } +#endif //FEATURE_PREJIT + + inline void Delete() + { + return m_StringPool.Uninit(); + } + +public: + // + // Getters + // + + __checkReturn + inline HRESULT GetString( + UINT32 nIndex, + __deref_out_z LPCSTR *pszString) const + { + return const_cast<StgPoolReadOnly &>(m_StringPool).GetStringReadOnly( + nIndex, + pszString); + } + + // Gets raw size (in bytes) of the represented strings. + inline UINT32 GetUnalignedSize() const + { + return m_StringPool.GetPoolSize(); + } + +}; // class StringHeapRO + +// -------------------------------------------------------------------------------------- +// +// This class represents read-write #String heap with all utility methods. +// +class StringHeapRW +{ +private: + // + // Private data + // + + // The storage of strings. + StgStringPool m_StringPool; + +public: + // + // Initialization + // + + __checkReturn + inline HRESULT InitializeEmpty( + UINT32 cbAllocationSize + COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite)) + { + return m_StringPool.InitNew(cbAllocationSize, 0); + } + __checkReturn + inline HRESULT InitializeEmpty_WithItemsCount( + UINT32 cbAllocationSize, + UINT32 cItemsCount + COMMA_INDEBUG_MD(BOOL debug_fIsReadWrite)) + { + return m_StringPool.InitNew(cbAllocationSize, cItemsCount); + } + __checkReturn + inline HRESULT Initialize( + DataBlob sourceData, + BOOL fCopyData) + { + return m_StringPool.InitOnMem((void *)sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData); + } + __checkReturn + inline HRESULT InitializeFromStringHeap( + const StringHeapRO *pSourceStringHeap, + BOOL fCopyData) + { + return m_StringPool.InitOnMem( + (void *)pSourceStringHeap->m_StringPool.GetSegData(), + pSourceStringHeap->m_StringPool.GetDataSize(), + !fCopyData); + } + __checkReturn + inline HRESULT InitializeFromStringHeap( + const StringHeapRW *pSourceStringHeap, + BOOL fCopyData) + { + return m_StringPool.InitOnMem( + (void *)pSourceStringHeap->m_StringPool.GetSegData(), + pSourceStringHeap->m_StringPool.GetDataSize(), + !fCopyData); + } + + // Destroys the string heap and all its allocated data. Can run on uninitialized string heap. + inline void Delete() + { + return m_StringPool.Uninit(); + } + +public: + // + // Getters + // + + __checkReturn + inline HRESULT GetString( + UINT32 nIndex, + __deref_out_z LPCSTR *pszString) const + { + return const_cast<StgStringPool &>(m_StringPool).GetString( + nIndex, + pszString); + } + + // Gets raw size (in bytes) of the represented strings. Doesn't align the size as code:GetAlignedSize. + inline UINT32 GetUnalignedSize() const + { + return m_StringPool.GetRawSize(); + } + // Gets size (in bytes) aligned up to 4-bytes of the represented strings. + // Fills *pcbSize with 0 on error. + __checkReturn + inline HRESULT GetAlignedSize( + __out UINT32 *pcbSize) const + { + return m_StringPool.GetSaveSize(pcbSize); + } + // Returns TRUE if the string heap is empty (even if it contains only default empty string). + inline BOOL IsEmpty() const + { + return const_cast<StgStringPool &>(m_StringPool).IsEmpty(); + } + + // Returns TRUE if the string index (nIndex, see code:#StringHeapIndex) is valid (i.e. in the string + // heap). + inline BOOL IsValidIndex(UINT32 nIndex) const + { + return const_cast<StgStringPool &>(m_StringPool).IsValidCookie(nIndex); + } + + __checkReturn + inline HRESULT SaveToStream_Aligned( + UINT32 nStartIndex, + __in IStream *pStream) const + { + if (nStartIndex == 0) + { + return const_cast<StgStringPool &>(m_StringPool).PersistToStream(pStream); + } + + if (nStartIndex == m_StringPool.GetRawSize()) + { + _ASSERTE(!m_StringPool.HaveEdits()); + return S_OK; + } + _ASSERTE(m_StringPool.HaveEdits()); + _ASSERTE(nStartIndex == m_StringPool.GetOffsetOfEdit()); + return const_cast<StgStringPool &>(m_StringPool).PersistPartialToStream(pStream, nStartIndex); + } + +public: + // + // Heap modifications + // + + // Adds null-terminated UTF-8 string (szString) to the end of the heap (incl. its null-terminator). + // Returns S_OK and index of added string (*pnIndex). + // Returns error code otherwise (and fills *pnIndex with 0). + __checkReturn + inline HRESULT AddString( + __in_z LPCSTR szString, + __out UINT32 *pnIndex) + { + return m_StringPool.AddString(szString, pnIndex); + } + // Adds null-terminated UTF-16 string (wszString) to the end of the heap (incl. its null-terminator). + // Returns S_OK and index of added string (*pnIndex). + // Returns error code otherwise (and fills *pnIndex with 0). + __checkReturn + inline HRESULT AddStringW( + __in_z LPCWSTR wszString, + __out UINT32 *pnIndex) + { + return m_StringPool.AddStringW(wszString, pnIndex); + } + + // Adds data from *pSourceStringHeap starting at index (nStartSourceIndex) to the string heap. + // Returns S_OK (even if the source is empty) or error code. + __checkReturn + inline HRESULT AddStringHeap( + const StringHeapRW *pSourceStringHeap, + UINT32 nStartSourceIndex) + { + return m_StringPool.CopyPool( + nStartSourceIndex, + &pSourceStringHeap->m_StringPool); + } // StringHeapRW::AddStringHeap + + __checkReturn + inline HRESULT MakeWritable() + { + return m_StringPool.ConvertToRW(); + } + +public: + // + // Tracking of heap modifications for EnC + // + + //#EnCSessionTracking + // EnC session starts automatically with initialization (code:Initialize or code:InitializeEmpty) or by + // user's explicit call to code:StartNewEnCSession. The heap stores its actual data size, so we can find + // out if some data were added later. + + // Gets heap size (in bytes) from the beginning of the last EnC session (code:#EnCSessionTracking). + inline UINT32 GetEnCSessionStartHeapSize() const + { + if (m_StringPool.HaveEdits()) + { + return m_StringPool.GetOffsetOfEdit(); + } + + return m_StringPool.GetRawSize(); + } + // Starts new EnC session (code:#EnCSessionTracking). + inline void StartNewEnCSession() + { + m_StringPool.ResetOffsetOfEdit(); + } + // Gets size (in bytes) aligned to 4-bytes of adds made from the beginning of the last EnC session. + __checkReturn + inline HRESULT GetEnCSessionAddedHeapSize_Aligned( + __out UINT32 *pcbSize) const + { + if (m_StringPool.HaveEdits()) + { + return m_StringPool.GetEditSaveSize(pcbSize); + } + + *pcbSize = 0; + return S_OK; + } + +}; // class StringHeapRW + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/CMakeLists.txt b/src/coreclr/md/hotdata/CMakeLists.txt new file mode 100644 index 00000000000..7f8ba752c4b --- /dev/null +++ b/src/coreclr/md/hotdata/CMakeLists.txt @@ -0,0 +1,52 @@ + +set(MDHOTDATA_SOURCES + hotmetadata.cpp + hottable.cpp + hotheapsdirectoryiterator.cpp + hotheap.cpp + hotheapwriter.cpp +) + +set(MDHOTDATA_HEADERS + ../../inc/metamodelpub.h + ../databuffer.h + ../heaps/export.h + ../inc/streamutil.h + ./export.h + heapindex.h + hotdataformat.h + hotheap.h + hotheapsdirectoryiterator.h + hotheapwriter.h + hotmetadata.h + hottable.h +) + +convert_to_absolute_path(MDHOTDATA_HEADERS ${MDHOTDATA_HEADERS}) +convert_to_absolute_path(MDHOTDATA_SOURCES ${MDHOTDATA_SOURCES}) + +if (CLR_CMAKE_TARGET_WIN32) + list(APPEND MDHOTDATA_SOURCES ${MDHOTDATA_HEADERS}) +endif (CLR_CMAKE_TARGET_WIN32) + +add_library_clr(mdhotdata_dac ${MDHOTDATA_SOURCES}) +set_target_properties(mdhotdata_dac PROPERTIES DAC_COMPONENT TRUE) +target_precompile_headers(mdhotdata_dac PRIVATE external.h) + +add_library_clr(mdhotdata_full_obj OBJECT ${MDHOTDATA_SOURCES}) +target_precompile_headers(mdhotdata_full_obj PRIVATE external.h) +add_library(mdhotdata_full INTERFACE) +target_sources(mdhotdata_full INTERFACE $<TARGET_OBJECTS:mdhotdata_full_obj>) + +add_library_clr(mdhotdata_crossgen ${MDHOTDATA_SOURCES}) +set_target_properties(mdhotdata_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) +target_precompile_headers(mdhotdata_crossgen PRIVATE external.h) + +if(CLR_CMAKE_HOST_WIN32) + add_library_clr(mdhotdata-staticcrt ${MDHOTDATA_SOURCES}) + target_precompile_headers(mdhotdata-staticcrt PRIVATE external.h) +endif(CLR_CMAKE_HOST_WIN32) + +add_library_clr(mdhotdata_ppdb ${MDHOTDATA_SOURCES}) +target_compile_definitions(mdhotdata_ppdb PRIVATE FEATURE_METADATA_EMIT_PORTABLE_PDB) +target_precompile_headers(mdhotdata_ppdb PRIVATE external.h) diff --git a/src/coreclr/md/hotdata/export.h b/src/coreclr/md/hotdata/export.h new file mode 100644 index 00000000000..a8480fa6e87 --- /dev/null +++ b/src/coreclr/md/hotdata/export.h @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: export.h +// + +// +// Popular types defined in MetaData\HotData directory. +// It's supposed to be included from other (MetaData) subcomponents, not from this directory. +// +// ====================================================================================== + +#pragma once + +#include "hotmetadata.h" + +#include "hottable.h" + +#include "hotheapsdirectoryiterator.h" +#include "hotheap.h" + +#include "heapindex.h" + +#include "hotheapwriter.h" diff --git a/src/coreclr/md/hotdata/external.h b/src/coreclr/md/hotdata/external.h new file mode 100644 index 00000000000..6201772c55e --- /dev/null +++ b/src/coreclr/md/hotdata/external.h @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: external.h +// + +// +// External types used in MetaData\HotData subcomponent classes. +// This file is used for precompiled headers, so it has to be included at the beginning of every .cpp in +// this directory. +// +// ====================================================================================== + +#pragma once + +#include "../external.h" +#include "../export.h" + +#include "../databuffer.h" diff --git a/src/coreclr/md/hotdata/heapindex.h b/src/coreclr/md/hotdata/heapindex.h new file mode 100644 index 00000000000..b257581ae77 --- /dev/null +++ b/src/coreclr/md/hotdata/heapindex.h @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotHeapWriter.h +// + +// +// Class code:HeapIndex represents type of MetaData heap (#String, #GUID, #Blob, or #US). +// +// ====================================================================================== + +#pragma once + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class represents type of MetaData heap (#String, #GUID, #Blob, or #US). +// +class HeapIndex +{ +private: + UINT32 m_Index; +public: + enum + { + StringHeapIndex = 0, + GuidHeapIndex = 1, + BlobHeapIndex = 2, + UserStringHeapIndex = 3, + + CountHeapIndex, + InvalidHeapIndex + }; + HeapIndex() + { + m_Index = InvalidHeapIndex; + } + HeapIndex(UINT32 index) + { + _ASSERTE(IsValid(index)); + m_Index = index; + } + void Set(UINT32 index) + { + _ASSERTE(IsValid(index)); + m_Index = index; + } + void SetInvalid() + { + m_Index = InvalidHeapIndex; + } + BOOL IsValid() const + { + return m_Index < CountHeapIndex; + } + static BOOL IsValid(UINT32 index) + { + return index < CountHeapIndex; + } + UINT32 Get() const + { return m_Index; } + +}; // class HeapIndex + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotdataformat.h b/src/coreclr/md/hotdata/hotdataformat.h new file mode 100644 index 00000000000..445463be8ef --- /dev/null +++ b/src/coreclr/md/hotdata/hotdataformat.h @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotDataFormat.h +// + +// +// Format of the hot data stored in the hot stream. The format consists of several structures: +// * code:MetaData::HotMetaDataHeader, which contains reference to: +// * code:MetaData::HotTablesDirectory, which contains array of references to: +// * code:HotTableHeader +// * code:MetaData::HotHeapsDirectory, which contains array of code:MetaData::HotHeapsDirectoryEntry, +// each containig: +// * index of the heap code:HeapIndex and a reference to: +// * code:MetaData::HotHeapHeader, which contains reference to: +// * index table, which contains sorted array of represented hot indexes in the heap +// * value offsets table, which contains offsets of values for corresponding hot indexes in +// previous table +// * value heap, which contains the values (copied out) from the original cold heap +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +// To avoid weird .h cycles, we have to include stgpool.h to get include of metamodelpub.h +#include <stgpool.h> +#include <metamodelpub.h> + +namespace MetaData +{ + +// #HotMetaData +// To help with startup time, we create a section of metadata that is only that meta-data that was touched +// durring IBC profiling. Given an offset into a pool this checks if we have any hot data associated with +// it. If we do we return a pointer to it, otherwse we return NULL. + +#include <pshpack1.h> + +// -------------------------------------------------------------------------------------- +// +// Top level hot data header. +// Ends at the end of MetaData hot stream (i.e. starts at offset end-8). +// +struct HotMetaDataHeader +{ + // Negative offset relative to the beginning of this structure. + // Points to the END (!!!) of code:HotTablesDirectory structure. + UINT32 m_nTablesDirectoryEnd_NegativeOffset; + // Negative offset relative to the beginning of this structure. + // Points to the (start of) code:HotHeapsDirectory structure. + UINT32 m_nHeapsDirectoryStart_NegativeOffset; + +}; // struct HotMetaDataHeader + +// -------------------------------------------------------------------------------------- +// +// This is the starting structure for hot data of tables. +// It's referenced (via reference to the end) from +// code:HotMetaDataHeader::m_nTablesDirectoryEnd_NegativeOffset. +// +struct HotTablesDirectory +{ + // Magic number (code:#const_nMagic) for format verification. + UINT32 m_nMagic; + // Array of signed offsets (should have negative or 0 value) relative to the beginning of this structute + // for each MetaData table. + // Points to the (start of) code:HotTableHeader structure. + INT32 m_rgTableHeader_SignedOffset[TBL_COUNT]; + + //#const_nMagic + // Magic value "HOMD" in code:m_nMagic field. + static const UINT32 const_nMagic = 0x484f4e44; + +}; // struct HotTablesDirectory + +// -------------------------------------------------------------------------------------- +// +// Describes hot data in a table. +// Entry referenced (via reference to the start) from code:HotTablesDirectory::m_rgTableHeader_SignedOffset. +// +struct HotTableHeader +{ + UINT32 m_cTableRecordCount; + // Can be 0 or sizeof(struct HotTableHeader) + UINT32 m_nFirstLevelTable_PositiveOffset; + // Can be 0 + UINT32 m_nSecondLevelTable_PositiveOffset; + UINT32 m_offsIndexMappingTable; + UINT32 m_offsHotData; + UINT16 m_shiftCount; + +}; // struct HotTableHeader + +// -------------------------------------------------------------------------------------- +// +// This is the starting structure for hot data of heaps (string, blob, guid and user string heap). +// The directory is an array of code:HotHeapsDirectoryEntry structures. +// It's referenced from code:HotMetaDataHeader::m_nHeapsDirectoryStart_NegativeOffset. +// +struct HotHeapsDirectory +{ + //code:HotHeapsDirectoryEntry m_rgEntries[*]; + +}; // struct HotHeapsDirectory + +// -------------------------------------------------------------------------------------- +// +// Describes one heap and its hot data. +// Entry in the hot heaps directory (code:HotHeapsDirectory). +// +struct HotHeapsDirectoryEntry +{ + // Index of the represented heap code:HeapIndex. + UINT32 m_nHeapIndex; + // Negative offset relative to the beginning of this structure. + // Points to the (start of) code:HotHeapHeader structure. + UINT32 m_nHeapHeaderStart_NegativeOffset; + +}; // struct HotHeapsDirectoryEntry + +// -------------------------------------------------------------------------------------- +// +// Describes hot data in a heap. +// It's referenced from code:HotHeapsDirectoryEntry::m_nHeapHeaderStart_NegativeOffset. +// +struct HotHeapHeader +{ + // Negative offset relative to the beginning of this structure. + // Points to a (start of) table of indexes (UINT32). This table is sorted, so binary search can be + // performed. If an index is cached in hot data of this heap, then the index is present in this table + // of indexes. + UINT32 m_nIndexTableStart_NegativeOffset; + // Negative offset relative to the beginning of this structure. + // Points to a (start of) table of value offsets (UINT32). This table contains value for each iteam in + // previous table of indexes. When an index is found in the previous table, then the value offset is + // stored in this table at the same index. + // The value offset is positive (!!!) offset relative to the start of heap values (see next member - + // code:m_nValueHeapStart_NegativeOffset) + UINT32 m_nValueOffsetTableStart_NegativeOffset; + // Negative offset relative to the beginning of this structure. + // Points to a (start of) values in the hot heap. This heap contains copies of values from the "normal" + // (cold) heap. The values in this heap have therefore the same encoding as the values in corresponding + // normal/cold heap. + // Offsets into this heap are stored in value offset table (code:m_nValueOffsetTableStart_NegativeOffset) + // as positive (!!!) offsets relative to the start of this hot value heap. + UINT32 m_nValueHeapStart_NegativeOffset; + +}; // struct HotHeapHeader + +#include <poppack.h> + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotheap.cpp b/src/coreclr/md/hotdata/hotheap.cpp new file mode 100644 index 00000000000..2781a81a1c2 --- /dev/null +++ b/src/coreclr/md/hotdata/hotheap.cpp @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotHeap.cpp +// + +// +// Class code:MetaData::HotHeap represents a hot heap in MetaData hot stream. +// +// ====================================================================================== + +#include "external.h" + +#include "hotheap.h" +#include "hotdataformat.h" +#include <utilcode.h> + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Initializes hot heap from its header and data. +// Provides limited debug-only validation of the structure. +// +__checkReturn +HRESULT +HotHeap::Initialize( + struct HotHeapHeader *pHotHeapHeader, + DataBuffer hotHeapData) +{ + _ASSERTE(hotHeapData.GetDataPointerBehind() == reinterpret_cast<BYTE *>(pHotHeapHeader)); + + UINT32 nMaximumNegativeOffset = hotHeapData.GetSize(); + if (pHotHeapHeader->m_nIndexTableStart_NegativeOffset > nMaximumNegativeOffset) + { + m_pHotHeapHeader = NULL; + Debug_ReportError("Invalid hot heap header format - invalid index table offset."); + return METADATA_E_INVALID_FORMAT; + } + if ((pHotHeapHeader->m_nIndexTableStart_NegativeOffset % 4) != 0) + { + m_pHotHeapHeader = NULL; + Debug_ReportError("Invalid hot heap header format - index table offset is not aligned."); + return METADATA_E_INVALID_FORMAT; + } + if (pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset > nMaximumNegativeOffset) + { + m_pHotHeapHeader = NULL; + Debug_ReportError("Invalid hot heap header format - invalid value offset table offset."); + return METADATA_E_INVALID_FORMAT; + } + if ((pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset % 4) != 0) + { + m_pHotHeapHeader = NULL; + Debug_ReportError("Invalid hot heap header format - value offset table offset is not aligned."); + return METADATA_E_INVALID_FORMAT; + } + // Index table has to be behind value offset table + if (pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset < pHotHeapHeader->m_nIndexTableStart_NegativeOffset) + { + m_pHotHeapHeader = NULL; + Debug_ReportError("Invalid hot heap header format - value offset table doesn't start before index table."); + return METADATA_E_INVALID_FORMAT; + } + if (pHotHeapHeader->m_nValueHeapStart_NegativeOffset > nMaximumNegativeOffset) + { + m_pHotHeapHeader = NULL; + Debug_ReportError("Invalid hot heap header format - invalid value heap offset."); + return METADATA_E_INVALID_FORMAT; + } + m_pHotHeapHeader = pHotHeapHeader; + return S_OK; +} // HotHeap::Initialize + +#ifdef _DEBUG_METADATA +// -------------------------------------------------------------------------------------- +// +// Validates hot heap structure (extension of code:Initialize checks). +// +__checkReturn +HRESULT +HotHeap::Debug_Validate() +{ + // Additional verification, more strict checks than in code:Initialize + S_UINT32 nValueOffsetTableStart = + S_UINT32(2) * + S_UINT32(m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset); + if (nValueOffsetTableStart.IsOverflow() || + (nValueOffsetTableStart.Value() != m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset)) + { + Debug_ReportError("Invalid hot heap header format."); + return METADATA_E_INVALID_FORMAT; + } + if (m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset <= m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset) + { + Debug_ReportError("Invalid hot heap header format."); + return METADATA_E_INVALID_FORMAT; + } + + // Already validated against underflow in code:Initialize + BYTE *pIndexTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset; + UINT32 *rgIndexTable = reinterpret_cast<UINT32 *>(pIndexTableStart); + // Already validated against underflow in code:Initialize + BYTE *pValueOffsetTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset; + UINT32 *rgValueOffsetTable = reinterpret_cast<UINT32 *>(pValueOffsetTableStart); + // Already validated against underflow in code:Initialize + BYTE *pValueHeapStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset; + DataBuffer valueHeap( + pValueHeapStart, + m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset); + + // Already validated for % 4 == 0 in code:Initialize + UINT32 cIndexTableCount = m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset / 4; + UINT32 nPreviousValue = 0; + for (UINT32 nIndex = 0; nIndex < cIndexTableCount; nIndex++) + { + if (nPreviousValue >= rgIndexTable[nIndex]) + { + Debug_ReportError("Invalid hot heap header format."); + return METADATA_E_INVALID_FORMAT; + } + UINT32 nValueOffset = rgValueOffsetTable[nIndex]; + if (nValueOffset >= valueHeap.GetSize()) + { + Debug_ReportError("Invalid hot heap header format."); + return METADATA_E_INVALID_FORMAT; + } + // TODO: Verify item (depends if it is string, blob, guid or user string) + } + return S_OK; +} // HotHeap::Debug_Validate +#endif //_DEBUG_METADATA + +// -------------------------------------------------------------------------------------- +// +// Gets stored data at index. +// Returns S_FALSE if data index is not stored in hot heap. +// +__checkReturn +HRESULT +HotHeap::GetData( + UINT32 nDataIndex, + __in DataBlob *pData) +{ + // Already validated against underflow in code:Initialize + BYTE *pIndexTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset; + // Already validated against underflow in code:Initialize + BYTE *pValueOffsetTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset; + // Already validated against underflow in code:Initialize + BYTE *pValueHeapStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset; + + const UINT32 *pnFoundDataIndex = BinarySearch<UINT32>( + reinterpret_cast<UINT32 *>(pIndexTableStart), + m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset / sizeof(UINT32), + nDataIndex); + + if (pnFoundDataIndex == NULL) + { // Index is not stored in hot data + return S_FALSE; + } + _ASSERTE(((UINT32 *)pIndexTableStart <= pnFoundDataIndex) && + (pnFoundDataIndex + 1 <= (UINT32 *)m_pHotHeapHeader)); + + // Index of found data index in the index table (note: it is not offset, but really index) + UINT32 nIndexOfFoundDataIndex = (UINT32)(pnFoundDataIndex - (UINT32 *)pIndexTableStart); + + // Value offset contains positive offset to the ValueHeap start + // Already validated against overflow in code:Initialize + UINT32 nValueOffset_PositiveOffset = reinterpret_cast<UINT32 *>(pValueOffsetTableStart)[nIndexOfFoundDataIndex]; + if (nValueOffset_PositiveOffset >= m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset) + { + pData->Clear(); + Debug_ReportError("Invalid hot data format - value offset reaches behind the hot heap data."); + return METADATA_E_INVALID_FORMAT; + } + pData->Init( + pValueHeapStart + nValueOffset_PositiveOffset, + m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset - nValueOffset_PositiveOffset); + + return S_OK; +} // HotHeap::GetData + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotheap.h b/src/coreclr/md/hotdata/hotheap.h new file mode 100644 index 00000000000..711ddf7d774 --- /dev/null +++ b/src/coreclr/md/hotdata/hotheap.h @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotHeap.h +// + +// +// Class code:MetaData::HotHeap represents a hot heap in MetaData hot stream. +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +class VerifyLayoutsMD; + +namespace MetaData +{ + +// Forward declarations +struct HotHeapHeader; + +// -------------------------------------------------------------------------------------- +// +// This class represents a hot heap in MetaData hot stream. +// +class HotHeap +{ + friend class ::VerifyLayoutsMD; +private: + struct HotHeapHeader *m_pHotHeapHeader; + +private: + friend class HotHeapsDirectoryIterator; + + // Initializes hot heap from its header and data. + __checkReturn + HRESULT Initialize(struct HotHeapHeader *pHotHeapHeader, DataBuffer hotHeapData); + +public: + HotHeap() + { m_pHotHeapHeader = NULL; } + HotHeap(const HotHeap &source) + { m_pHotHeapHeader = source.m_pHotHeapHeader; } + + void Clear() + { m_pHotHeapHeader = NULL; } + + // Gets stored data at index. + // Returns S_FALSE if data index is not stored in hot heap. + __checkReturn + HRESULT GetData( + UINT32 nDataIndex, + __out DataBlob *pData); + + inline BOOL IsEmpty() const + { return m_pHotHeapHeader == NULL; } + +#ifdef _DEBUG_METADATA + // Validates hot heap structure (extension of code:Initialize checks). + __checkReturn + HRESULT Debug_Validate(); +#endif //_DEBUG_METADATA + +}; // class HotHeap + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotheapsdirectoryiterator.cpp b/src/coreclr/md/hotdata/hotheapsdirectoryiterator.cpp new file mode 100644 index 00000000000..33653a2404b --- /dev/null +++ b/src/coreclr/md/hotdata/hotheapsdirectoryiterator.cpp @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotHeapsDirectoryIterator.h +// + +// +// Class code:MetaData::HotHeapsDirectoryIterator represents an iterator through hot heaps directory +// (code:HotHeapsDirectory). +// +// ====================================================================================== + +#include "external.h" + +#include "hotheapsdirectoryiterator.h" +#include "hotdataformat.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Creates empty iterator. +// +HotHeapsDirectoryIterator::HotHeapsDirectoryIterator() +{ + m_RemainingHeapsDirectoryData.Clear(); + m_HotHeapsData.Clear(); +} // HotHeapsDirectoryIterator::HotHeapsDirectoryIterator + +// -------------------------------------------------------------------------------------- +// +// Initialize iteration on heaps directory (hotHeapsDirectoryData) with heap hot data (hotHeapsData). +// The caller guarantees that the heap hot data end where heaps directory beggins. +// +void +HotHeapsDirectoryIterator::Initialize( + DataBuffer hotHeapsDirectoryData, + DataBuffer hotHeapsData) +{ + _ASSERTE(hotHeapsData.GetDataPointerBehind() == hotHeapsDirectoryData.GetDataPointer()); + m_RemainingHeapsDirectoryData = hotHeapsDirectoryData; + m_HotHeapsData = hotHeapsData; +} // HotHeapsDirectoryIterator::Initialize + +// -------------------------------------------------------------------------------------- +// +// Gets next hot heap (*pHotHeap, of index *pHotHeapIndex) from the heaps directory. +// Returns S_OK and fills *pHotHeap and *pHotHeapIndex with the next code:HotHeap information. +// Returns S_FALSE, if the last hot heap was already returned. Clears *pHotHeap and *pHotHeapIndex in this +// case. +// Returns error code if the format is invalid. Clears *pHotHeap and *pHotHeapIndex in this case. +// +__checkReturn +HRESULT +HotHeapsDirectoryIterator::GetNext( + HotHeap *pHotHeap, + HeapIndex *pHotHeapIndex) +{ + HRESULT hr; + DataBuffer hotHeapHeaderData; + DataBuffer hotHeapData; + + struct HotHeapsDirectoryEntry *pEntry; + if (!m_RemainingHeapsDirectoryData.GetData<struct HotHeapsDirectoryEntry>( + &pEntry)) + { + hr = S_FALSE; + goto ErrExit; + } + + if (!HeapIndex::IsValid(pEntry->m_nHeapIndex)) + { + Debug_ReportError("Invalid hot heaps directory format - invalid heap index."); + IfFailGo(METADATA_E_INVALID_FORMAT); + } + pHotHeapIndex->Set(pEntry->m_nHeapIndex); + + hotHeapHeaderData = m_HotHeapsData; + if (!hotHeapHeaderData.SkipToExactSize(pEntry->m_nHeapHeaderStart_NegativeOffset)) + { + Debug_ReportError("Invalid hot heaps directory format - heap header offset reaches in front of of hot heaps data."); + IfFailGo(METADATA_E_INVALID_FORMAT); + } + + struct HotHeapHeader *pHeader; + if (!hotHeapHeaderData.PeekData<struct HotHeapHeader>(&pHeader)) + { + Debug_ReportError("Invalid hot heaps directory format - heap header reaches behind hot heaps data."); + IfFailGo(METADATA_E_INVALID_FORMAT); + } + + hotHeapData = m_HotHeapsData; + if (!hotHeapData.TruncateBySize(pEntry->m_nHeapHeaderStart_NegativeOffset)) + { + Debug_ReportInternalError("There's a bug because previous call to SkipToExactSize succeeded."); + IfFailGo(METADATA_E_INVALID_FORMAT); + } + + IfFailGo(pHotHeap->Initialize(pHeader, hotHeapData)); + _ASSERTE(hr == S_OK); + return hr; +ErrExit: + pHotHeap->Clear(); + pHotHeapIndex->SetInvalid(); + return hr; +} // HotHeapsDirectoryIterator::GetNext + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotheapsdirectoryiterator.h b/src/coreclr/md/hotdata/hotheapsdirectoryiterator.h new file mode 100644 index 00000000000..79a99b3d3e2 --- /dev/null +++ b/src/coreclr/md/hotdata/hotheapsdirectoryiterator.h @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotHeapsDirectoryIterator.h +// + +// +// Class code:MetaData::HotHeapsDirectoryIterator represents an iterator through hot heaps directory +// (code:HotHeapsDirectory). +// +// ====================================================================================== + +#pragma once + +#include "external.h" +#include "heapindex.h" +#include "hotheap.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class represents an iterator through hot heaps directory (code:HotHeapsDirectory), i.e. through an +// array of code:HotHeapsDirectoryEntry. +// +class HotHeapsDirectoryIterator +{ +private: + // + // Private data + // + + // Remaining data from the heaps directory. On each iteration this will be shrinked (the + // code:HotHeapsDirectoryEntry will be skipped). + DataBuffer m_RemainingHeapsDirectoryData; + // Data for the hot heaps. It has to end exactly where heaps directory starts. + DataBuffer m_HotHeapsData; + +private: + // + // Operations with restricted access + // + + // code:HotMetaData is the only class allowed to create this iteration. + friend class HotMetaData; + + // Initialize iteration on heaps directory (hotHeapsDirectoryData) with heap hot data (hotHeapsData). + // The caller guarantees that the heap hot data end where heaps directory beggins. + void Initialize( + DataBuffer hotHeapsDirectoryData, + DataBuffer hotHeapsData); + +public: + // + // Operations + // + + // Creates empty iterator. + HotHeapsDirectoryIterator(); + + // S_OK, S_FALSE, error code (clears the HotHeap if not S_OK) + __checkReturn + HRESULT GetNext( + HotHeap *pHotHeap, + HeapIndex *pHotHeapIndex); + +}; // class HotHeapsDirectoryIterator + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotheapwriter.cpp b/src/coreclr/md/hotdata/hotheapwriter.cpp new file mode 100644 index 00000000000..c3a483bf6ab --- /dev/null +++ b/src/coreclr/md/hotdata/hotheapwriter.cpp @@ -0,0 +1,304 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotHeapWriter.cpp +// + +// +// Class code:HotHeapWriter represents a writer of hot heap into MetaData hot stream (collected by IBC). +// +// ====================================================================================== + +#include "external.h" + +#include "hotheapwriter.h" +#include "../heaps/export.h" + +#include <stgpool.h> +#include <metamodelpub.h> +#include <utilcode.h> +#include "../inc/streamutil.h" + +#include "hotdataformat.h" + +#ifdef FEATURE_PREJIT +// Cannot be included without FEATURE_PREJIT: +#include <corcompile.h> +#endif //FEATURE_PREJIT + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Creates writer for #String heap. +// +HotHeapWriter::HotHeapWriter( + const StringHeapRW *pStringHeap) +{ + m_HeapIndex = HeapIndex::StringHeapIndex; + m_pStringHeap = pStringHeap; +} // HotHeapWriter::HotHeapWriter + +// -------------------------------------------------------------------------------------- +// +// Creates writer for #Blob or #US heap (if fUserStringHeap is TRUE). +// +HotHeapWriter::HotHeapWriter( + const BlobHeapRW *pBlobHeap, + BOOL fUserStringHeap) +{ + m_HeapIndex = fUserStringHeap ? HeapIndex::UserStringHeapIndex : HeapIndex::BlobHeapIndex; + m_pBlobHeap = pBlobHeap; +} // HotHeapWriter::HotHeapWriter + +// -------------------------------------------------------------------------------------- +// +// Creates writer for #GUID heap. +// +HotHeapWriter::HotHeapWriter( + const GuidHeapRW *pGuidHeap) +{ + m_HeapIndex = HeapIndex::GuidHeapIndex; + m_pGuidHeap = pGuidHeap; +} // HotHeapWriter::HotHeapWriter + +// -------------------------------------------------------------------------------------- +// +// Destroys the writer of hot heap. +// +void +HotHeapWriter::Delete() +{ +} // HotHeapWriter::Delete + +typedef struct _RidOffsetPair +{ + ULONG rid; + ULONG offset; + // compare function for qsort + static int __cdecl Compare(void const *_x, void const *_y); +} RidOffsetPair; + +// static +int __cdecl +RidOffsetPair::Compare(void const *_x, void const *_y) +{ + RidOffsetPair const *x = reinterpret_cast<RidOffsetPair const *>(_x); + RidOffsetPair const *y = reinterpret_cast<RidOffsetPair const *>(_y); + + return x->rid - y->rid; +} + +// -------------------------------------------------------------------------------------- +// +// Stores hot data reported by IBC in profile data (code:CorProfileData) to a stream. +// Aligns output stream to 4-bytes. +// +__checkReturn +HRESULT +HotHeapWriter::SaveToStream( + IStream *pStream, + CorProfileData *pProfileData, + UINT32 *pnSavedSize) const +{ + _ASSERTE(pStream != NULL); + _ASSERTE(pProfileData != NULL); + _ASSERTE(pnSavedSize != NULL); + +#ifdef FEATURE_PREJIT + HRESULT hr = S_OK; + UINT32 nOffset = 0; + UINT32 nValueHeapStart_PositiveOffset; + UINT32 nValueOffsetTableStart_PositiveOffset; + UINT32 nIndexTableStart_PositiveOffset; + + // data + // + + // number of hot tokens + UINT32 nHotItemsCount = pProfileData->GetHotTokens( + GetTableIndex(), + 1 << ProfilingFlags_MetaData, + 1 << ProfilingFlags_MetaData, + NULL, + 0); + CONSISTENCY_CHECK(nHotItemsCount != 0); + + NewArrayHolder<UINT32> hotItemArr = new (nothrow) UINT32[nHotItemsCount]; + IfNullRet(hotItemArr); + + // get hot tokens + static_assert_no_msg(sizeof(UINT32) == sizeof(mdToken)); + pProfileData->GetHotTokens( + GetTableIndex(), + 1 << ProfilingFlags_MetaData, + 1 << ProfilingFlags_MetaData, + reinterpret_cast<mdToken *>(&hotItemArr[0]), + nHotItemsCount); + + // convert tokens to rids + for (UINT32 i = 0; i < nHotItemsCount; i++) + { + hotItemArr[i] = RidFromToken(hotItemArr[i]); + } + + NewArrayHolder<RidOffsetPair> offsetMapping = new (nothrow) RidOffsetPair[nHotItemsCount]; + IfNullRet(offsetMapping); + + // write data + nValueHeapStart_PositiveOffset = nOffset; + + // note that we write hot items in the order they appear in pProfileData->GetHotTokens + // this is so that we preserve the ordering optimizations done by IbcMerge + for (UINT32 i = 0; i < nHotItemsCount; i++) + { + DataBlob data; + IfFailRet(GetData( + hotItemArr[i], + &data)); + + // keep track of the offset at which each hot item is written + offsetMapping[i].rid = hotItemArr[i]; + offsetMapping[i].offset = nOffset; + + IfFailRet(StreamUtil::WriteToStream( + pStream, + data.GetDataPointer(), + data.GetSize(), + &nOffset)); + } + + IfFailRet(StreamUtil::AlignDWORD(pStream, &nOffset)); + + // sort by rid so that a hot rid can be looked up by binary search + qsort(offsetMapping, nHotItemsCount, sizeof(RidOffsetPair), RidOffsetPair::Compare); + + // initialize table of offsets to data + NewArrayHolder<UINT32> dataIndices = new (nothrow) UINT32[nHotItemsCount]; + IfNullRet(dataIndices); + + // fill in the hotItemArr (now sorted by rid) and dataIndices array with each offset + for (UINT32 i = 0; i < nHotItemsCount; i++) + { + hotItemArr[i] = offsetMapping[i].rid; + dataIndices[i] = offsetMapping[i].offset; + } + + // table of offsets to data + // + + nValueOffsetTableStart_PositiveOffset = nOffset; + IfFailRet(StreamUtil::WriteToStream(pStream, &dataIndices[0], sizeof(UINT32) * nHotItemsCount, &nOffset)); + + // rid table (sorted) + // + + nIndexTableStart_PositiveOffset = nOffset; + + IfFailRet(StreamUtil::WriteToStream(pStream, &hotItemArr[0], nHotItemsCount * sizeof(UINT32), &nOffset)); + IfFailRet(StreamUtil::AlignDWORD(pStream, &nOffset)); + + { + // hot pool header + struct HotHeapHeader header; + + // fix offsets + header.m_nIndexTableStart_NegativeOffset = nOffset - nIndexTableStart_PositiveOffset; + header.m_nValueOffsetTableStart_NegativeOffset = nOffset - nValueOffsetTableStart_PositiveOffset; + header.m_nValueHeapStart_NegativeOffset = nOffset - nValueHeapStart_PositiveOffset; + + // write header + IfFailRet(StreamUtil::WriteToStream(pStream, &header, sizeof(header), &nOffset)); + } + + *pnSavedSize = nOffset; + +#endif //FEATURE_PREJIT + + return S_OK; +} // HotHeapWriter::PersistHotToStream + +// -------------------------------------------------------------------------------------- +// +// Returns index of the heap as table index used by IBC (code:CorProfileData). +// +UINT32 +HotHeapWriter::GetTableIndex() const +{ + return TBL_COUNT + m_HeapIndex.Get(); +} // HotHeapWriter::GetTableIndex + +// -------------------------------------------------------------------------------------- +// +// Returns heap data at index (nIndex). +// +__checkReturn +HRESULT +HotHeapWriter::GetData( + UINT32 nIndex, + DataBlob *pData) const +{ + HRESULT hr; + + switch (m_HeapIndex.Get()) + { + case HeapIndex::StringHeapIndex: + { + LPCSTR szString; + IfFailGo(m_pStringHeap->GetString( + nIndex, + &szString)); + _ASSERTE(hr == S_OK); + + // This should not overflow, because we checked it before, but it doesn't hurt + S_UINT32 cbStringSize = S_UINT32(strlen(szString)) + S_UINT32(1); + if (cbStringSize.IsOverflow()) + { + Debug_ReportInternalError("There's a bug in the string heap consistency - string is too long."); + IfFailGo(METADATA_E_INTERNAL_ERROR); + } + + pData->Init((BYTE *)szString, cbStringSize.Value()); + return S_OK; + } + case HeapIndex::GuidHeapIndex: + { + // The nIndex is in fact 0-based offset into GUID heap (0, 16, 32, ...), convert it to 1-based element index (1, 2, 3, ...) for GetGuid method + if ((nIndex % sizeof(GUID)) != 0) + { + Debug_ReportInternalError("There's a bug in the caller/IBC - this should be GUID offset aligned to 16-B."); + IfFailGo(METADATA_E_INTERNAL_ERROR); + } + nIndex = (nIndex / sizeof(GUID)) + 1; + + GUID UNALIGNED *pGuid; + IfFailGo(const_cast<GuidHeapRW *>(m_pGuidHeap)->GetGuid( + nIndex, + &pGuid)); + _ASSERTE(hr == S_OK); + pData->Init((BYTE *)pGuid, sizeof(GUID)); + return S_OK; + } + case HeapIndex::BlobHeapIndex: + case HeapIndex::UserStringHeapIndex: + { + IfFailGo(const_cast<BlobHeapRW *>(m_pBlobHeap)->GetBlobWithSizePrefix( + nIndex, + pData)); + _ASSERTE(hr == S_OK); + + return S_OK; + } + default: + Debug_ReportInternalError("There's a bug in the caller - this is wrong heap index."); + IfFailGo(METADATA_E_INTERNAL_ERROR); + } + return S_OK; + +ErrExit: + pData->Clear(); + return hr; +} // HotHeapWriter::GetData + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotheapwriter.h b/src/coreclr/md/hotdata/hotheapwriter.h new file mode 100644 index 00000000000..ee2d78f664e --- /dev/null +++ b/src/coreclr/md/hotdata/hotheapwriter.h @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotHeapWriter.h +// + +// +// Class code:HotHeapWriter represents a writer of hot heap into MetaData hot stream (collected by IBC). +// +// ====================================================================================== + +#pragma once + +#include "external.h" +#include "heapindex.h" + +// Forward declarations +class CorProfileData; +struct IStream; + +namespace MetaData +{ + +// Forward declarations +class StringHeapRW; +class BlobHeapRW; +class GuidHeapRW; + +// -------------------------------------------------------------------------------------- +// +// This class represents a writer of hot heap into MetaData hot stream (collected by IBC). +// +class HotHeapWriter +{ +private: + // Index of the represented heap (type of the heap). + HeapIndex m_HeapIndex; + union + { + const StringHeapRW *m_pStringHeap; + // Both #Blob and #US heaps are represented as code:BlobHeapRW. + const BlobHeapRW *m_pBlobHeap; + const GuidHeapRW *m_pGuidHeap; + }; + +public: + // Creates writer for #String heap. + HotHeapWriter(const StringHeapRW *pStringHeap); + // Creates writer for #Blob or #US heap (if fUserStringHeap is TRUE). + HotHeapWriter( + const BlobHeapRW *pBlobHeap, + BOOL fUserStringHeap); + // Creates writer for #GUID heap. + HotHeapWriter(const GuidHeapRW *pGuidHeap); + + // Destroys the writer of hot heap. + void Delete(); + + // Stores hot data reported by IBC in profile data (code:CorProfileData) to a stream. + // Aligns output stream to 4-bytes. + __checkReturn + HRESULT SaveToStream( + IStream *pStream, + CorProfileData *pProfileData, + UINT32 *pnSavedSize) const; + + // Returns index of the heap as table index used by IBC (code:CorProfileData). + UINT32 GetTableIndex() const; + +private: + // + // Helpers + // + + // Returns heap data at index (nIndex). + __checkReturn + HRESULT GetData( + UINT32 nIndex, + DataBlob *pData) const; + +}; // class HotHeapWriter + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotmetadata.cpp b/src/coreclr/md/hotdata/hotmetadata.cpp new file mode 100644 index 00000000000..b68896a7fcd --- /dev/null +++ b/src/coreclr/md/hotdata/hotmetadata.cpp @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotMetaData.cpp +// + +// +// Class code:MetaData::HotMetaData represents a reader of hot data in MetaData hot stream. +// +// ====================================================================================== + +#include "external.h" + +#include "hotmetadata.h" +#include "hotdataformat.h" +#include "hotheapsdirectoryiterator.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Class code:MetaData::HotMetaData represents a reader of hot data in MetaData hot stream. +// +__checkReturn +HRESULT +HotMetaData::Initialize( + DataBuffer data) +{ + m_Data = data; + + return S_OK; +} // HotMetaData::Initialize + +// -------------------------------------------------------------------------------------- +// +// Returns iterator of stored hot heaps (code:HotHeap). +// +__checkReturn +HRESULT +HotMetaData::GetHeapsDirectoryIterator( + HotHeapsDirectoryIterator *pHeapsDirectoryIterator) +{ + if (m_Data.GetSize() < sizeof(struct HotMetaDataHeader)) + { + Debug_ReportError("Invalid hot MetaData format - header doesn't fit into the hot data."); + return METADATA_E_INVALID_FORMAT; + } + + struct HotMetaDataHeader *pHeader; + if (!m_Data.PeekDataAt<struct HotMetaDataHeader>( + m_Data.GetSize() - sizeof(struct HotMetaDataHeader), + &pHeader)) + { + Debug_ReportInternalError("There's a bug, because previous size check succeeded."); + return METADATA_E_INTERNAL_ERROR; + } + // Get rid of the read header + DataBuffer heapsData = m_Data; + if (!heapsData.TruncateBySize(sizeof(struct HotMetaDataHeader))) + { + Debug_ReportInternalError("There's a bug, because previous size check succeeded."); + return METADATA_E_INTERNAL_ERROR; + } + DataBuffer heapsDirectoryData = heapsData; + if (!heapsDirectoryData.SkipToExactSize(pHeader->m_nHeapsDirectoryStart_NegativeOffset)) + { + Debug_ReportError("Invalid hot MetaData format - heaps directory offset reaches in front of hot data."); + return METADATA_E_INVALID_FORMAT; + } + if (!heapsData.TruncateBySize(pHeader->m_nHeapsDirectoryStart_NegativeOffset)) + { + Debug_ReportInternalError("There's a bug, because previous call to SkipToExactSize succeeded."); + return METADATA_E_INVALID_FORMAT; + } + + pHeapsDirectoryIterator->Initialize( + heapsDirectoryData, + heapsData); + + return S_OK; +} // HotMetaData::GetHeapsDirectoryIterator + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hotmetadata.h b/src/coreclr/md/hotdata/hotmetadata.h new file mode 100644 index 00000000000..86ab49dc816 --- /dev/null +++ b/src/coreclr/md/hotdata/hotmetadata.h @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotMetaData.h +// + +// +// Class code:MetaData::HotMetaData represents a reader of hot data in MetaData hot stream. +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +namespace MetaData +{ + +// Forward declaration +class HotHeapsDirectoryIterator; + +// -------------------------------------------------------------------------------------- +// +// This class represents a reader of hot data in MetaData hot stream. +// +class HotMetaData +{ +private: + DataBuffer m_Data; + +public: + // Creates reader for MetaData hot stream of format file:HotDataFormat.h. + __checkReturn + HRESULT Initialize(DataBuffer data); + + // Returns iterator of stored hot heaps (code:HotHeap). + __checkReturn + HRESULT GetHeapsDirectoryIterator(HotHeapsDirectoryIterator *pHeapsDirectoryIterator); + +}; // class HotMetaData + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hottable.cpp b/src/coreclr/md/hotdata/hottable.cpp new file mode 100644 index 00000000000..81031b77419 --- /dev/null +++ b/src/coreclr/md/hotdata/hottable.cpp @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotTable.cpp +// + +// +// Class code:MetaData::HotTable stores hot table records cache, a cache of often-accessed +// table records stored only in NGEN images. +// The cache is created using IBC logging. +// +// ====================================================================================== + +#include "external.h" + +#include "hottable.h" +#include "hotdataformat.h" + +#include <metamodelpub.h> + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Returns S_OK if index (nIndex) is present in the hot table record cache and returns its value from +// cache (*ppRecord). +// Returns S_FALSE if offset is not in the hot table record cache, DOES NOT initialize *ppRecord in this +// case! +// Returns error code otherwise (and sets *pRecord to NULL). +// + +//static +__checkReturn +HRESULT +HotTable::GetData( + UINT32 nRowIndex, + __deref_out_opt BYTE **ppRecord, + UINT32 cbRecordSize, + struct HotTableHeader *pHotTableHeader) +{ + BYTE *pHotTableHeaderData = (BYTE *)pHotTableHeader; + + if (pHotTableHeader->m_nFirstLevelTable_PositiveOffset != 0) + { // Has first level table + // fetch the first level table + WORD *pFirstLevelTable = (WORD *)(pHotTableHeaderData + pHotTableHeader->m_nFirstLevelTable_PositiveOffset); + + // find the high order bits of the rid + BYTE bRid = (BYTE)(nRowIndex >> pHotTableHeader->m_shiftCount); + + // use the low order bits of the rid to index into + // the first level table. + UINT32 nMask = (1 << pHotTableHeader->m_shiftCount) - 1; + int i = pFirstLevelTable[nRowIndex & nMask]; + int end = pFirstLevelTable[(nRowIndex & nMask) + 1]; + + // the generation logic should make sure either all tables are present + // or all absent. + _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset != 0); + _ASSERTE(pHotTableHeader->m_offsIndexMappingTable != 0); + + // fetch second level and index mapping tables + BYTE *pSecondLevelTable = pHotTableHeaderData + pHotTableHeader->m_nSecondLevelTable_PositiveOffset; + WORD *pIndexMappingTable = (WORD *)(pHotTableHeaderData + pHotTableHeader->m_offsIndexMappingTable); + + // look for the high order bits of the rid in the second level table. + // search is linear, but should be short on average. + for ( ; i < end; i++) + { + if (pSecondLevelTable[i] == bRid) + { + // we have found the hot rid we are looking for + + // now consult the index mapping table to find where the hot data is stored + int index = pIndexMappingTable[i]; + + *ppRecord = pHotTableHeaderData + pHotTableHeader->m_offsHotData + (index * cbRecordSize); + return S_OK; + } + } + // Not found in hot data + return S_FALSE; + } + // no first level table - this implies the whole table is replicated + // in the hot section. simply multiply and fetch the right record. + // hot indices are 0-based, rids are 1-base, so need to subtract 1 from rid. + + *ppRecord = pHotTableHeaderData + pHotTableHeader->m_offsHotData + ((nRowIndex - 1) * cbRecordSize); + return S_OK; +} // HotTable::GetData + +// static +void +HotTable::CheckTables(struct HotTablesDirectory *pHotTablesDirectory) +{ +#ifdef _DEBUG + _ASSERTE(pHotTablesDirectory->m_nMagic == HotTablesDirectory::const_nMagic); + + for (UINT32 nTableIndex = 0; nTableIndex < TBL_COUNT; nTableIndex++) + { + if (pHotTablesDirectory->m_rgTableHeader_SignedOffset[nTableIndex] != 0) + { + struct HotTableHeader *pHotTableHeader = GetTableHeader(pHotTablesDirectory, nTableIndex); + + _ASSERTE((pHotTableHeader->m_cTableRecordCount > 0) && (pHotTableHeader->m_cTableRecordCount <= USHRT_MAX)); + if (pHotTableHeader->m_nFirstLevelTable_PositiveOffset == 0) + { + _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset == 0); + _ASSERTE(pHotTableHeader->m_offsIndexMappingTable == 0); + _ASSERTE(pHotTableHeader->m_offsHotData == Align4(sizeof(struct HotTableHeader))); + } + else + { + UINT32 nFirstLevelTableOffset = sizeof(struct HotTableHeader); + _ASSERTE(pHotTableHeader->m_nFirstLevelTable_PositiveOffset == nFirstLevelTableOffset); + UINT32 cbFirstLevelTableSize = sizeof(WORD) * ((1 << pHotTableHeader->m_shiftCount) + 1); + + _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset != 0); + UINT32 nSecondLevelTableOffset = nFirstLevelTableOffset + cbFirstLevelTableSize; + _ASSERTE(pHotTableHeader->m_nSecondLevelTable_PositiveOffset == nSecondLevelTableOffset); + UINT32 cbSecondLevelTableSize = sizeof(BYTE) * pHotTableHeader->m_cTableRecordCount; + + _ASSERTE(pHotTableHeader->m_offsIndexMappingTable != 0); + UINT32 nIndexMappingTableOffset = nSecondLevelTableOffset + cbSecondLevelTableSize; + _ASSERTE(pHotTableHeader->m_offsIndexMappingTable == nIndexMappingTableOffset); + UINT32 cbIndexMappingTableSize = sizeof(WORD) * pHotTableHeader->m_cTableRecordCount; + + UINT32 nHotDataOffset = nIndexMappingTableOffset + cbIndexMappingTableSize; + _ASSERTE(pHotTableHeader->m_offsHotData == Align4(nHotDataOffset)); + } + } + } +#endif //_DEBUG +} // HotTable::CheckTables + +}; // namespace MetaData diff --git a/src/coreclr/md/hotdata/hottable.h b/src/coreclr/md/hotdata/hottable.h new file mode 100644 index 00000000000..3cfd82ea8a5 --- /dev/null +++ b/src/coreclr/md/hotdata/hottable.h @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: HotTable.h +// + +// +// Class code:MetaData::HotTable stores hot table records cache, a cache of often-accessed +// table records stored only in NGEN images. +// The cache is created using IBC logging. +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +#include "hotdataformat.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class stores hot table records cache, a cache of often-accessed table records stored only in NGEN +// images. +// The cache is created using IBC logging. +// +class HotTable +{ +public: + __checkReturn + static HRESULT GetData( + UINT32 nRowIndex, + __deref_out_opt BYTE **ppRecord, + UINT32 cbRecordSize, + struct HotTableHeader *pHotTableHeader); + + inline static struct HotTableHeader *GetTableHeader( + struct HotTablesDirectory *pHotTablesDirectory, + UINT32 nTableIndex) + { + _ASSERTE(pHotTablesDirectory != NULL); + + INT32 nTableOffset = pHotTablesDirectory->m_rgTableHeader_SignedOffset[nTableIndex]; + _ASSERTE(nTableOffset != 0); + + BYTE *pHotTableHeaderData = ((BYTE *)pHotTablesDirectory) + nTableOffset; + return (struct HotTableHeader *)pHotTableHeaderData; + } + + static void CheckTables(struct HotTablesDirectory *pHotTablesDirectory); + +}; // class HotTable + +}; // namespace MetaData diff --git a/src/coreclr/md/inc/VerifyLayouts.inc b/src/coreclr/md/inc/VerifyLayouts.inc new file mode 100644 index 00000000000..14e068ecefa --- /dev/null +++ b/src/coreclr/md/inc/VerifyLayouts.inc @@ -0,0 +1,344 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +// This file provides an explicit check of field layouts using some macro magic +// in VerifyLayouts.h. The goal is that if any field changes offset or type changes +// size then a build time assert should trigger. DO NOT change these definitions without +// reading the comments in VerifyLayouts.h and understanding what other code you need +// to change at the same time. +// +// +// AN EXAMPLE: +// You want to validate the fields in type CDerived +// +// class CFoo +// { +// void* m_ptrField; +// } +// +// struct BigStruct +// { +// DWORD m_one; +// DWORD m_two; +// DWORD m_three; +// } +// +// CDerived : CFoo +// { +// DWORD m_cRef; +// SomeOtherType* m_otherType; +// #ifdef _SOME_DEFINE +// BigStruct m_struct; +// #endif //_SOME_DEFINE +// } +// +// +// and the layout validation is written as: +// +// BEGIN_TYPE(CDerived, sizeof(CFoo)) // a) The first field starts at sizeof(CFoo) to account for base class data +// // b) Beware of vtable pointers, they take up space before the data fields too +// // c) Beware of using sizeof(some_other_type) unless you also explicitly verify +// // the layout of that type here. Changing the base type would change the derived types +// // layout and won't be caught unless the base type is explicitly verified. +// // d) sizeof(<primitive_type>) is fine - they never change over time and we know how +// // to deal with platform pointer size differences +// FIELD(CDerived, m_cRef, 4) +// FIELD(CDerived, m_otherType, sizeof(void*)) +// #ifdef _SOME_DEFINE_ +// ALIGN_FIELD(CDerived, m_struct, sizeof(BigStruct), 4) // We need to use the ALIGN_FIELD macro because the alignment isn't the same as +// // the field size. The alignment of a structure is typically the max alignment +// // of any member. The alignment of a primitive type is typically the size of the type. +// #endif _SOME_DEFINE_ +// END_TYPE(CDerived, sizeof(void*) +// +// +// BEGIN_TYPE(CFoo, 0) // We must verify this type because we used it to define the starting offset of CDerived +// FIELD(CFoo, m_ptrField, sizeof(void*)) +// END_TYPE(CFoo, sizeof(void*)) +// +// +// BEGIN_TYPE(BigStruct, 0) // We must verify this type because we used sizeof(BigStruct) to define the size of +// // CDerived::m_struct field +// FIELD(BigStruct, m_one, 4) +// FIELD(BigStruct, m_two, 4) +// FIELD(BigStruct, m_thee, 4) +// END_TYPE(BigStruct, 4) +// +// +// +// +// OTHER CONSIDERATIONS: +// +// 1) if the type layout is conditional on a define, just include the same define here in the layout verification +// Make sure that the define is registered in the list of defines and the debugger reading code knows how to dynamically +// adjust for it (See VerifyLayouts.h comment item (b) and (c)) +// +// 2) If your type names use characters that aren't valid identifiers (such as the '<', '>' and ',' chars in templates) +// then you need to provide an escaped name. Use the USING_ALIAS macros to do that: +// USING_ALIAS(escapedTypeName, typeName) +// +// If CFoo above had instead been CFoo<ULONG> we would write: +// USING_ALIAS(CFoo__ULONG__, CFoo<ULONG>) +// BEGIN_TYPE(CFoo__ULONG__, 0) +// FIELD(CFoo__ULONG__, m_ptrField, sizeof(void*)) +// END_TYPE(CFoo__ULONG__, sizeof(void*)) +// +// The escapedTypeName is relatively arbitrary, but convention is to replace the illegal characters with double underscore +// The name does show up in build error messages, so it should close to the real name for people to understand +// +// 3) If you have a bitfield in your type, the offsetof macro can't be used which will break the static asserts. +// There is a special BITFIELD macro that can work around it: +// BITFIELD(typeName, fieldName, expectedFieldOffset, fieldSize, fieldAlign) +// +// The macro is just like FIELD execpt you must provide the offset yourself. Since you can't use offsetof on the field directly +// the convention is to use the offset of the previous field and then add the size and alignment requirements. For example if your +// type had this: +// CMiniMdRW +// { +// ULONG m_cbSaveSize; +// int m_fIsReadOnly : 1; +// int m_bPreSaveDone : 1; +// int m_bSaveCompressed : 1; +// int m_bPostGSSMod : 1; +// } +// +// You could write +// FIELD(CMiniMdRW, m_cbSaveSize, 4) +// BITFIELD(CMiniMdRW, m_fIsReadOnly, offsetof(CMiniMdRW, m_cbSaveSize)+4, 4) +// +// Don't include al the fields in the bitfield, just pick one as the canonical field name +// +// +// +// HOW DO I DEBUG THIS STUFF WHEN THE BUILD DOESN'T WORK? +// +// One way that has been effective for me is to write a few static_assert_no_msg entries manually in VerifyLayouts.h +// You can use those to verify your assumptions such as: +// static_assert_no_msg(sizeof(Foo) == 24) +// static_assert_no_msg(offset(Foo, m_lastField) == 20) +// static_assert_no_msg(offset_of_field_affter_Foo_m_lastField == 24) +// Then rebuild and find out where the compiler disagress with you. +// +// Another option is to run the source through the preprocessor +// +// +// + + + + + + +BEGIN_TYPE(MDInternalRW, 2*sizeof(void*)) +FIELD(MDInternalRW, m_pStgdb, sizeof(void*)) +FIELD(MDInternalRW, m_tdModule, 4) +FIELD(MDInternalRW, m_cRefs, 4) +FIELD(MDInternalRW, m_fOwnStgdb, 4) +FIELD(MDInternalRW, m_pUnk, sizeof(void*)) +FIELD(MDInternalRW, m_pUserUnk, sizeof(void*)) +FIELD(MDInternalRW, m_pIMetaDataHelper, sizeof(void*)) +FIELD(MDInternalRW, m_pSemReadWrite, sizeof(void*)) +FIELD(MDInternalRW, m_fOwnSem, 4) +END_TYPE(MDInternalRW, sizeof(void*)) + +BEGIN_TYPE(CLiteWeightStgdbRW, sizeof(CLiteWeightStgdb<CMiniMdRW>)) +FIELD(CLiteWeightStgdbRW, m_cbSaveSize, 4) +FIELD(CLiteWeightStgdbRW, m_bSaveCompressed, 4) +FIELD(CLiteWeightStgdbRW, m_pImage, sizeof(void*)) +FIELD(CLiteWeightStgdbRW, m_dwImageSize, 4) +FIELD(CLiteWeightStgdbRW, m_dwPEKind, 4) +FIELD(CLiteWeightStgdbRW, m_dwMachine, 4) +FIELD(CLiteWeightStgdbRW, m_pStreamList, sizeof(void*)) +FIELD(CLiteWeightStgdbRW, m_pNextStgdb, sizeof(void*)) +FIELD(CLiteWeightStgdbRW, m_eFileType, 4) +FIELD(CLiteWeightStgdbRW, m_wszFileName, sizeof(void*)) +FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFT, 4) +FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFS, 4) +FIELD(CLiteWeightStgdbRW, m_pStgIO, sizeof(void*)) +END_TYPE(CLiteWeightStgdbRW, 8) + +USING_ALIAS(CLiteWeightStgdb__CMiniMdRW__, CLiteWeightStgdb<CMiniMdRW>) +BEGIN_TYPE(CLiteWeightStgdb__CMiniMdRW__, 0) +ALIGN_FIELD(CLiteWeightStgdb__CMiniMdRW__, m_MiniMd, sizeof(CMiniMdRW), sizeof(void*)) +FIELD(CLiteWeightStgdb__CMiniMdRW__, m_pvMd, sizeof(void*)) +FIELD(CLiteWeightStgdb__CMiniMdRW__, m_cbMd, 4) +END_TYPE(CLiteWeightStgdb__CMiniMdRW__, sizeof(void*)) + +BEGIN_TYPE(CMiniMdRW, sizeof(CMiniMdTemplate<CMiniMdRW>)) +FIELD(CMiniMdRW, m_pMemberRefHash, sizeof(void*)) +FIELD(CMiniMdRW, m_pMemberDefHash, sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_pLookUpHashs, sizeof(void*)*TBL_COUNT, sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_StringPoolOffsetHash, sizeof(MapSHash<UINT32, UINT32>), 4) +FIELD(CMiniMdRW, m_pNamedItemHash, sizeof(void*)) +FIELD(CMiniMdRW, m_maxRid, 4) +FIELD(CMiniMdRW, m_limRid, 4) +FIELD(CMiniMdRW, m_maxIx, 4) +FIELD(CMiniMdRW, m_limIx, 4) +FIELD(CMiniMdRW, m_eGrow, 4) +ALIGN_FIELD(CMiniMdRW, m_Tables, sizeof(RecordPool)*TBL_COUNT, sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_pVS, sizeof(void*)*TBL_COUNT, sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_StringHeap, sizeof(StgStringPool), sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_BlobHeap, sizeof(StgBlobPool), sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_UserStringHeap, sizeof(StgBlobPool), sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_GuidHeap, sizeof(StgGuidPool), sizeof(void*)) +FIELD(CMiniMdRW, m_pHandler, sizeof(void*)) +FIELD(CMiniMdRW, m_cbSaveSize, 4) +BITFIELD(CMiniMdRW, m_fIsReadOnly, offsetof(CMiniMdRW, m_cbSaveSize)+4, 4) +FIELD(CMiniMdRW, m_pMethodMap, sizeof(void*)) +FIELD(CMiniMdRW, m_pFieldMap, sizeof(void*)) +FIELD(CMiniMdRW, m_pPropertyMap, sizeof(void*)) +FIELD(CMiniMdRW, m_pEventMap, sizeof(void*)) +FIELD(CMiniMdRW, m_pParamMap, sizeof(void*)) +FIELD(CMiniMdRW, m_pFilterTable, sizeof(void*)) +FIELD(CMiniMdRW, m_pHostFilter, sizeof(void*)) +FIELD(CMiniMdRW, m_pTokenRemapManager, sizeof(void*)) +ALIGN_FIELD(CMiniMdRW, m_OptionValue, sizeof(OptionValue), 4) +ALIGN_FIELD(CMiniMdRW, m_StartupSchema, sizeof(CMiniMdSchema), 8) +ALIGN_FIELD(CMiniMdRW, m_bSortable, sizeof(BYTE)*TBL_COUNT, sizeof(BYTE)) +#ifdef _DEBUG +FIELD(CMiniMdRW, dbg_m_pLock, sizeof(void*)) +#endif +FIELD(CMiniMdRW, m_fMinimalDelta, 4) +FIELD(CMiniMdRW, m_rENCRecs, sizeof(void*)) +END_TYPE(CMiniMdRW, 8) + +BEGIN_TYPE(OptionValue, 0) +FIELD(OptionValue, m_DupCheck, 4) +FIELD(OptionValue, m_RefToDefCheck, 4) +FIELD(OptionValue, m_NotifyRemap, 4) +FIELD(OptionValue, m_UpdateMode, 4) +FIELD(OptionValue, m_ErrorIfEmitOutOfOrder, 4) +FIELD(OptionValue, m_ThreadSafetyOptions, 4) +FIELD(OptionValue, m_ImportOption, 4) +FIELD(OptionValue, m_LinkerOption, 4) +FIELD(OptionValue, m_GenerateTCEAdapters, 4) +FIELD(OptionValue, m_RuntimeVersion, sizeof(void*)) +FIELD(OptionValue, m_MetadataVersion, 4) +FIELD(OptionValue, m_MergeOptions, 4) +FIELD(OptionValue, m_InitialSize, 4) +FIELD(OptionValue, m_LocalRefPreservation, 4) +END_TYPE(OptionValue, sizeof(void*)) + + +BEGIN_TYPE(StgBlobPool, sizeof(StgPool)) +ALIGN_FIELD(StgBlobPool, m_Hash, sizeof(CBlobPoolHash), sizeof(void*)) +END_TYPE(StgBlobPool, sizeof(void*)) + +BEGIN_TYPE(StgStringPool, sizeof(StgPool)) +ALIGN_FIELD(StgStringPool, m_Hash, sizeof(CStringPoolHash), sizeof(void*)) +FIELD(StgStringPool, m_bHash, sizeof(BOOL)) +END_TYPE(StgStringPool, sizeof(void*)) + +BEGIN_TYPE(StgGuidPool, sizeof(StgPool)) +ALIGN_FIELD(StgGuidPool, m_Hash, sizeof(CGuidPoolHash), sizeof(void*)) +FIELD(StgGuidPool, m_bHash, sizeof(BOOL)) +END_TYPE(StgGuidPool, sizeof(void*)) + +BEGIN_TYPE(RecordPool, sizeof(StgPool)) +FIELD(RecordPool, m_cbRec, 4) +END_TYPE(RecordPool, sizeof(void*)) + +BEGIN_TYPE(StgPool, sizeof(StgPoolReadOnly)) +FIELD(StgPool, m_ulGrowInc, 4) +FIELD(StgPool, m_pCurSeg, sizeof(void*)) +FIELD(StgPool, m_cbCurSegOffset, 4) +BITFIELD(StgPool, m_bFree, offsetof(StgPool, m_cbCurSegOffset)+4, 4) // can't take offsetof on a bitfield so we have to provide the offset another way +FIELD(StgPool, m_nVariableAlignmentMask, 4) +FIELD(StgPool, m_cbStartOffsetOfEdit, 4) +FIELD(StgPool, m_fValidOffsetOfEdit, 4) +END_TYPE(StgPool, sizeof(void*)) + + +BEGIN_TYPE(CStringPoolHash, sizeof(CChainedHash<STRINGHASH>)) +FIELD(CStringPoolHash, m_Pool, sizeof(void*)) +END_TYPE(CStringPoolHash, sizeof(void*)) + + +BEGIN_TYPE(CBlobPoolHash, sizeof(CChainedHash<STRINGHASH>)) +FIELD(CBlobPoolHash, m_Pool, sizeof(void*)) +END_TYPE(CStringPoolHash, sizeof(void*)) + + +BEGIN_TYPE(CGuidPoolHash, sizeof(CChainedHash<GUIDHASH>)) +FIELD(CGuidPoolHash, m_Pool, sizeof(void*)) +END_TYPE(CGuidPoolHash, sizeof(void*)) + + +USING_ALIAS(MetaData__HotHeap, MetaData::HotHeap) +BEGIN_TYPE(MetaData__HotHeap, 0) +FIELD(MetaData__HotHeap, m_pHotHeapHeader, sizeof(void*)) +END_TYPE(MetaData__HotHeap, sizeof(void*)) + + +BEGIN_TYPE(StgPoolReadOnly, sizeof(StgPoolSeg) + sizeof(void*)) //vtable pointer +ALIGN_FIELD(StgPoolReadOnly, m_HotHeap, sizeof(MetaData::HotHeap), sizeof(void*)) +END_TYPE(StgPoolReadOnly, sizeof(void*)) + +USING_ALIAS(MapSHash__ULONG__ULONG, MapSHash<ULONG, ULONG>) // Create a using alias to avoid commas in the type name +BEGIN_TYPE(MapSHash__ULONG__ULONG, 0) +FIELD(MapSHash__ULONG__ULONG, m_table, sizeof(void*)) +FIELD(MapSHash__ULONG__ULONG, m_tableSize, 4) +FIELD(MapSHash__ULONG__ULONG, m_tableCount, 4) +FIELD(MapSHash__ULONG__ULONG, m_tableOccupied, 4) +FIELD(MapSHash__ULONG__ULONG, m_tableMax, 4) +END_TYPE(MapSHash__ULONG__ULONG, sizeof(void*)) + + +BEGIN_TYPE(StgPoolSeg, 0) +FIELD(StgPoolSeg, m_pSegData, sizeof(void*)) +FIELD(StgPoolSeg, m_pNextSeg, sizeof(void*)) +FIELD(StgPoolSeg, m_cbSegSize, 4) +FIELD(StgPoolSeg, m_cbSegNext, 4) +END_TYPE(StgPoolSeg, sizeof(void*)) + +USING_ALIAS(CCHainedHash_STRINGHASH, CChainedHash<STRINGHASH>) +BEGIN_TYPE(CCHainedHash_STRINGHASH, sizeof(void*)) // vtable pointer +FIELD(CCHainedHash_STRINGHASH, m_rgData, sizeof(void*)) +FIELD(CCHainedHash_STRINGHASH, m_iBuckets, 4) +FIELD(CCHainedHash_STRINGHASH, m_iSize, 4) +FIELD(CCHainedHash_STRINGHASH, m_iCount, 4) +FIELD(CCHainedHash_STRINGHASH, m_iMaxChain, 4) +FIELD(CCHainedHash_STRINGHASH, m_iFree, 4) +END_TYPE(CCHainedHash_STRINGHASH, sizeof(void*)) + + +BEGIN_TYPE(CMiniColDef, 0) +FIELD(CMiniColDef, m_Type, 1) +FIELD(CMiniColDef, m_oColumn, 1) +FIELD(CMiniColDef, m_cbColumn, 1) +END_TYPE(CMiniColDef, 1) + + +BEGIN_TYPE(CMiniTableDef, 0) +FIELD(CMiniTableDef, m_pColDefs, sizeof(void*)) +FIELD(CMiniTableDef, m_cCols, 1) +FIELD(CMiniTableDef, m_iKey, 1) +FIELD(CMiniTableDef, m_cbRec, 1) +END_TYPE(CMiniTableDef, sizeof(void*)) + +BEGIN_TYPE(CMiniMdBase, 8) //vtable ptr and first field 8-byte alignment +ALIGN_FIELD(CMiniMdBase, m_Schema, sizeof(CMiniMdSchema), 8) +FIELD(CMiniMdBase, m_TblCount, 4) +FIELD(CMiniMdBase, m_fVerifiedByTrustedSource, 4) +ALIGN_FIELD(CMiniMdBase, m_TableDefs, sizeof(CMiniTableDef)*TBL_COUNT, sizeof(void*)) +FIELD(CMiniMdBase, m_iStringsMask, 4) +FIELD(CMiniMdBase, m_iGuidsMask, 4) +FIELD(CMiniMdBase, m_iBlobsMask, 4) +END_TYPE(CMiniMdBase, 8) + + +BEGIN_TYPE(CMiniMdSchemaBase, 0) +FIELD(CMiniMdSchemaBase, m_ulReserved, 4) +FIELD(CMiniMdSchemaBase, m_major, 1) +FIELD(CMiniMdSchemaBase, m_minor, 1) +FIELD(CMiniMdSchemaBase, m_heaps, 1) +FIELD(CMiniMdSchemaBase, m_rid, 1) +FIELD(CMiniMdSchemaBase, m_maskvalid, 8) +FIELD(CMiniMdSchemaBase, m_sorted, 8) +END_TYPE(CMiniMdSchemaBase, 8) + +BEGIN_TYPE(CMiniMdSchema, sizeof(CMiniMdSchemaBase)) +ALIGN_FIELD(CMiniMdSchema, m_cRecs, 4*TBL_COUNT, 4) +FIELD(CMiniMdSchema, m_ulExtra, 4) +END_TYPE(CMiniMdSchema, 8) diff --git a/src/coreclr/md/inc/assemblymdinternaldisp.h b/src/coreclr/md/inc/assemblymdinternaldisp.h new file mode 100644 index 00000000000..286fbd58f83 --- /dev/null +++ b/src/coreclr/md/inc/assemblymdinternaldisp.h @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// AssemblyMDInternalDispenser.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __AssemblyMDInternalDispenser__h__ +#define __AssemblyMDInternalDispenser__h__ + +#include "../runtime/mdinternalro.h" + + +#endif // __AssemblyMDInternalDispenser__h__ diff --git a/src/coreclr/md/inc/cahlprinternal.h b/src/coreclr/md/inc/cahlprinternal.h new file mode 100644 index 00000000000..e7235d13aa6 --- /dev/null +++ b/src/coreclr/md/inc/cahlprinternal.h @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// + +#ifndef __CAHLPR_H__ +#define __CAHLPR_H__ + +#include "sarray.h" +#include "caparser.h" + +//***************************************************************************** +// This class assists in the parsing of CustomAttribute blobs. +//***************************************************************************** +struct CaType +{ + void Init(CorSerializationType _type) + { + _ASSERTE(_type != SERIALIZATION_TYPE_SZARRAY && _type != SERIALIZATION_TYPE_ENUM); + tag = _type; + arrayType = SERIALIZATION_TYPE_UNDEFINED; + enumType = SERIALIZATION_TYPE_UNDEFINED; + szEnumName = NULL; + cEnumName = 0; + } + + void Init(CorSerializationType _type, CorSerializationType _arrayType, CorSerializationType _enumType, LPCSTR _szEnumName, ULONG _cEnumName) + { + tag = _type; + arrayType = _arrayType; + enumType = _enumType; + szEnumName = _szEnumName; + cEnumName = _cEnumName; + } + + CorSerializationType tag; + CorSerializationType arrayType; + CorSerializationType enumType; + LPCSTR szEnumName; + ULONG cEnumName; +}; + +struct CaTypeCtor : public CaType +{ + CaTypeCtor(CorSerializationType _type) + { + Init(_type); + } + + CaTypeCtor(CorSerializationType _type, CorSerializationType _arrayType, CorSerializationType _enumType, LPCSTR _szEnumName, ULONG _cEnumName) + { + Init(_type, _arrayType, _enumType, _szEnumName, _cEnumName); + } +}; + +typedef struct CaValue +{ + union + { + unsigned __int8 boolean; + signed __int8 i1; + unsigned __int8 u1; + signed __int16 i2; + unsigned __int16 u2; + signed __int32 i4; + unsigned __int32 u4; + signed __int64 i8; + unsigned __int64 u8; + float r4; + double r8; + + struct + { + CorSerializationType tag; + SArray<CaValue>* pSArray; + ULONG length; + inline CaValue &operator[](int index) { return (*pSArray)[index]; } + } arr; + + struct + { + LPCUTF8 pStr; + ULONG cbStr; + } str; + }; + + CaType type; + +} CaValue; + + + +#endif // __CAHLPR_H__ diff --git a/src/coreclr/md/inc/liteweightstgdb.h b/src/coreclr/md/inc/liteweightstgdb.h new file mode 100644 index 00000000000..062dee00a59 --- /dev/null +++ b/src/coreclr/md/inc/liteweightstgdb.h @@ -0,0 +1,264 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// LiteWeightStgdb.h +// + +// +// This contains definition of class CLiteWeightStgDB. This is light weight +// read-only implementation for accessing compressed meta data format. +// +//***************************************************************************** +#ifndef __LiteWeightStgdb_h__ +#define __LiteWeightStgdb_h__ + +#include "metadata.h" +#include "metamodelro.h" +#include "metamodelrw.h" + +#include "stgtiggerstorage.h" + +class StgIO; + +#include "mdcommon.h" +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +#include "pdbheap.h" +#endif + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:28718) // public header missing SAL annotations +#endif // _PREFAST_ +class TiggerStorage; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif // _PREFAST_ + +//***************************************************************************** +// This class provides common definitions for heap segments. It is both the +// base class for the heap, and the class for heap extensions (additional +// memory that must be allocated to grow the heap). +//***************************************************************************** +template <class MiniMd> +class CLiteWeightStgdb +{ + friend class VerifyLayoutsMD; +public: + CLiteWeightStgdb() : m_pvMd(NULL), m_cbMd(0) + {} + + ~CLiteWeightStgdb() + { Uninit(); } + + // open an in-memory metadata section for read. + __checkReturn + HRESULT InitOnMem( + ULONG cbData, + LPCVOID pbData); + + __checkReturn + HRESULT InitHotPools(DataBuffer hotMetaData); + + void Uninit(); + +protected: + MiniMd m_MiniMd; // embedded compress meta data schemas definition + const void *m_pvMd; // Pointer to meta data. + ULONG m_cbMd; // Size of the meta data. + + friend class CorMetaDataScope; + friend class COR; + friend class RegMeta; + friend class MDInternalRO; + friend class MDInternalRW; +}; + +//***************************************************************************** +// Open an in-memory metadata section for read +//***************************************************************************** +template <class MiniMd> +void CLiteWeightStgdb<MiniMd>::Uninit() +{ + m_MiniMd.m_StringHeap.Delete(); + m_MiniMd.m_UserStringHeap.Delete(); + m_MiniMd.m_GuidHeap.Delete(); + m_MiniMd.m_BlobHeap.Delete(); + m_pvMd = NULL; + m_cbMd = 0; +} + +class CLiteWeightStgdbRW : public CLiteWeightStgdb<CMiniMdRW> +{ + friend class CImportTlb; + friend class RegMeta; + friend class VerifyLayoutsMD; + friend HRESULT TranslateSigHelper( + IMDInternalImport* pImport, + IMDInternalImport* pAssemImport, + const void* pbHashValue, + ULONG cbHashValue, + PCCOR_SIGNATURE pbSigBlob, + ULONG cbSigBlob, + IMetaDataAssemblyEmit* pAssemEmit, + IMetaDataEmit* emit, + CQuickBytes* pqkSigEmit, + ULONG* pcbSig); +public: + CLiteWeightStgdbRW() : m_cbSaveSize(0), m_pStreamList(0), m_pNextStgdb(NULL), m_pStgIO(NULL) + { + m_wszFileName = NULL; + m_pImage = NULL; + m_dwImageSize = 0; + m_dwPEKind = (DWORD)(-1); + m_dwDatabaseLFS = 0; + m_dwDatabaseLFT = 0; +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + m_pPdbHeap = NULL; +#endif + } + ~CLiteWeightStgdbRW(); + + __checkReturn + HRESULT InitNew(); + + // open an in-memory metadata section for read. + __checkReturn + HRESULT InitOnMem( + ULONG cbData, + LPCVOID pbData, + int bReadOnly); + + __checkReturn + HRESULT GetSaveSize( + CorSaveSize fSize, + UINT32 *pcbSaveSize, + MetaDataReorderingOptions reorderingOptions = NoReordering, + CorProfileData *pProfileData = NULL); // optional IBC profile data for working set optimization + + __checkReturn + HRESULT SaveToStream( + IStream *pIStream, // Stream to which to write + MetaDataReorderingOptions reorderingOptions = NoReordering, + CorProfileData *pProfileData = NULL); // optional IBC profile data for working set optimization + + __checkReturn + HRESULT Save( + LPCWSTR szFile, + DWORD dwSaveFlags); + + // Open a metadata section for read/write + __checkReturn + HRESULT OpenForRead( + LPCWSTR szDatabase, // Name of database. + void *pbData, // Data to open on top of, 0 default. + ULONG cbData, // How big is the data. + DWORD dwFlags); // Flags for the open. + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + // Open a metadata section for read/write + __checkReturn + HRESULT OpenForRead( + IMDCustomDataSource *pDataSource, // data to open on top of + DWORD dwFlags); // Flags for the open. +#endif + + __checkReturn + HRESULT FindImageMetaData( + PVOID pImage, // Pointer to head of a file + DWORD dwFileLength, // length of a flat file + BOOL bMappedImage, // Is the file mapped + PVOID *ppMetaData, // [out] pointer to the metadata + ULONG *pcbMetaData); // [out] size of the metadata + + __checkReturn + HRESULT FindObjMetaData( + PVOID pImage, // Pointer to an OBJ file + DWORD dwFileLength, // Length of the file + PVOID *ppMetaData, // [out] pointer to the metadata + ULONG *pcbMetaData); // [out] size of the metadata + + __checkReturn + HRESULT GetPEKind( // S_OK or error. + MAPPINGTYPE mtMapping, // The type of mapping the image has + DWORD* pdwPEKind, // [OUT] The kind of PE (0 - not a PE) + DWORD* pdwMachine); // [OUT] Machine as defined in NT header + + // Low level data access; not useful for most clients. + __checkReturn + HRESULT GetRawData( + const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB'). + ULONG *pcbMd); // [OUT] put size of the stream here. + + __checkReturn + STDMETHODIMP GetRawStreamInfo( // Get info about the MD stream. + ULONG ix, // [IN] Stream ordinal desired. + const char **ppchName, // [OUT] put pointer to stream name here. + const void **ppv, // [OUT] put pointer to MD stream here. + ULONG *pcb); // [OUT] put size of the stream here. + + DAC_ALIGNAS(CLiteWeightStgdb<CMiniMdRW>) // Align the first member to the alignment of the base class + UINT32 m_cbSaveSize; // Size of the saved streams. + int m_bSaveCompressed; // If true, save as compressed stream (#-, not #~) + VOID* m_pImage; // Set in OpenForRead, NULL for anything but PE files + DWORD m_dwImageSize; // On-disk size of image + +protected: + DWORD m_dwPEKind; // The kind of PE - 0: not a PE. + DWORD m_dwMachine; // Machine as defined in NT header. + + __checkReturn + HRESULT GetPoolSaveSize( + LPCWSTR szHeap, // Name of the heap stream. + int iPool, // The pool whose size to get. + UINT32 *pcbSaveSize); // Add pool data to this value. + + __checkReturn + HRESULT GetTablesSaveSize( + CorSaveSize fSave, + UINT32 *pcbSaveSize, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData = NULL); // Add pool data to this value. + + __checkReturn + HRESULT AddStreamToList( + UINT32 cbSize, // Size of the stream data. + LPCWSTR szName); // Name of the stream. + + __checkReturn + HRESULT SaveToStorage( + TiggerStorage *pStorage, + MetaDataReorderingOptions reorderingOptions = NoReordering, + CorProfileData *pProfileData = NULL); + + __checkReturn + HRESULT SavePool(LPCWSTR szName, TiggerStorage *pStorage, int iPool); + + STORAGESTREAMLST *m_pStreamList; + + __checkReturn + HRESULT InitFileForRead( + StgIO *pStgIO, // For file i/o. + int bReadOnly=true); // If read-only. + + // Set file name of this database (makes copy of the file name). + __checkReturn HRESULT SetFileName(const WCHAR * wszFileName); + // Returns TRUE if wszFileName has valid file name length. + static BOOL IsValidFileNameLength(const WCHAR * wszFileName); + + CLiteWeightStgdbRW *m_pNextStgdb; + +public: + FORCEINLINE FILETYPE GetFileType() { return m_eFileType; } + +private: + FILETYPE m_eFileType; + WCHAR * m_wszFileName; // Database file name (NULL or non-empty string) + DWORD m_dwDatabaseLFT; // Low bytes of the database file's last write time + DWORD m_dwDatabaseLFS; // Low bytes of the database file's size + StgIO * m_pStgIO; // For file i/o. +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + PdbHeap *m_pPdbHeap; +#endif +}; // class CLiteWeightStgdbRW + +#endif // __LiteWeightStgdb_h__ diff --git a/src/coreclr/md/inc/mdcolumndescriptors.h b/src/coreclr/md/inc/mdcolumndescriptors.h new file mode 100644 index 00000000000..496aa6c33e2 --- /dev/null +++ b/src/coreclr/md/inc/mdcolumndescriptors.h @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// + +static const BYTE s_ModuleCol[]; +static const BYTE s_TypeRefCol[]; +static const BYTE s_TypeDefCol[]; +static const BYTE s_FieldPtrCol[]; +static const BYTE s_FieldCol[]; +static const BYTE s_MethodPtrCol[]; +static const BYTE s_MethodCol[]; +static const BYTE s_ParamPtrCol[]; +static const BYTE s_ParamCol[]; +static const BYTE s_InterfaceImplCol[]; +static const BYTE s_MemberRefCol[]; +static const BYTE s_ConstantCol[]; +static const BYTE s_CustomAttributeCol[]; +static const BYTE s_FieldMarshalCol[]; +static const BYTE s_DeclSecurityCol[]; +static const BYTE s_ClassLayoutCol[]; +static const BYTE s_FieldLayoutCol[]; +static const BYTE s_StandAloneSigCol[]; +static const BYTE s_EventMapCol[]; +static const BYTE s_EventPtrCol[]; +static const BYTE s_EventCol[]; +static const BYTE s_PropertyMapCol[]; +static const BYTE s_PropertyPtrCol[]; +static const BYTE* s_PropertyCol; +static const BYTE s_MethodSemanticsCol[]; +static const BYTE s_MethodImplCol[]; +static const BYTE s_ModuleRefCol[]; +static const BYTE* s_TypeSpecCol; +static const BYTE s_ImplMapCol[]; +static const BYTE* s_FieldRVACol; +static const BYTE s_ENCLogCol[]; +static const BYTE s_ENCMapCol[]; +static const BYTE s_AssemblyCol[]; +static const BYTE* s_AssemblyProcessorCol; +static const BYTE s_AssemblyOSCol[]; +static const BYTE s_AssemblyRefCol[]; +static const BYTE s_AssemblyRefProcessorCol[]; +static const BYTE s_AssemblyRefOSCol[]; +static const BYTE s_FileCol[]; +static const BYTE s_ExportedTypeCol[]; +static const BYTE s_ManifestResourceCol[]; +static const BYTE s_NestedClassCol[]; +static const BYTE s_GenericParamCol[]; +static const BYTE s_MethodSpecCol[]; +static const BYTE s_GenericParamConstraintCol[]; +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +// Dummy descriptors to fill the gap to 0x30 +static const BYTE s_Dummy1Col[]; +static const BYTE s_Dummy2Col[]; +static const BYTE s_Dummy3Col[]; +// Actual portable PDB tables descriptors +static const BYTE s_DocumentCol[]; +static const BYTE s_MethodDebugInformationCol[]; +static const BYTE s_LocalScopeCol[]; +static const BYTE s_LocalVariableCol[]; +static const BYTE s_LocalConstantCol[]; +static const BYTE s_ImportScopeCol[]; +// TODO: +// static const BYTE s_StateMachineMethodCol[]; +// static const BYTE s_CustomDebugInformationCol[]; +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB diff --git a/src/coreclr/md/inc/mdinternalrw.h b/src/coreclr/md/inc/mdinternalrw.h new file mode 100644 index 00000000000..ad1dc9a6cc7 --- /dev/null +++ b/src/coreclr/md/inc/mdinternalrw.h @@ -0,0 +1,815 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDInternalRW.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __MDInternalRW__h__ +#define __MDInternalRW__h__ + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +#include "../inc/mdlog.h" + +class UTSemReadWrite; + +class MDInternalRW : public IMDInternalImportENC, public IMDCommon +{ + friend class VerifyLayoutsMD; +public: + + + MDInternalRW(); + virtual ~MDInternalRW(); + __checkReturn + HRESULT Init(LPVOID pData, ULONG cbData, int bReadOnly); + __checkReturn + HRESULT InitWithStgdb(IUnknown *pUnk, CLiteWeightStgdbRW *pStgdb); + __checkReturn + HRESULT InitWithRO(MDInternalRO *pRO, int bReadOnly); + + // *** IUnknown methods *** + __checkReturn + STDMETHODIMP QueryInterface(REFIID riid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + __checkReturn + STDMETHODIMP TranslateSigWithScope( + IMDInternalImport *pAssemImport, // [IN] import assembly scope. + const void *pbHashValue, // [IN] hash value for the import assembly. + ULONG cbHashValue, // [IN] count of bytes in the hash value. + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit *pAssemEmit, // [IN] assembly emit scope. + IMetaDataEmit *emit, // [IN] emit interface + CQuickBytes *pqkSigEmit, // [OUT] buffer to hold translated signature + ULONG *pcbSig) // [OUT] count of bytes in the translated signature + DAC_UNEXPECTED(); + + __checkReturn + STDMETHODIMP GetTypeDefRefTokenInTypeSpec(// return S_FALSE if enclosing type does not have a token + mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at + mdToken *tkEnclosedToken) // [OUT] The enclosed type token + DAC_UNEXPECTED(); + + STDMETHODIMP_(IMetaModelCommon*) GetMetaModelCommon() + { + return static_cast<IMetaModelCommon*>(&m_pStgdb->m_MiniMd); + } + + STDMETHODIMP_(IMetaModelCommonRO*) GetMetaModelCommonRO() + { + if (m_pStgdb->m_MiniMd.IsWritable()) + { + _ASSERTE(!"IMetaModelCommonRO methods cannot be used because this importer is writable."); + return NULL; + } + + return static_cast<IMetaModelCommonRO*>(&m_pStgdb->m_MiniMd); + } + + __checkReturn + STDMETHODIMP SetOptimizeAccessForSpeed(// return hresult + BOOL fOptSpeed) + { + // If there is any optional work we can avoid (for example, because we have + // traded space for speed) this is the place to turn it off or on. + + return S_OK; + } + + //***************************************************************************** + // return the count of entries of a given kind in a scope + // For example, pass in mdtMethodDef will tell you how many MethodDef + // contained in a scope + //***************************************************************************** + STDMETHODIMP_(ULONG) GetCountWithTokenKind(// return hresult + DWORD tkKind) // [IN] pass in the kind of token. + DAC_UNEXPECTED(); + + //***************************************************************************** + // enumerator for typedef + //***************************************************************************** + __checkReturn + STDMETHODIMP EnumTypeDefInit( // return hresult + HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data + + //***************************************************************************** + // enumerator for MethodImpl + //***************************************************************************** + __checkReturn + STDMETHODIMP EnumMethodImplInit( // return hresult + mdTypeDef td, // [IN] TypeDef over which to scope the enumeration. + HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens. + HENUMInternal *phEnumDecl) // [OUT] buffer to fill for enumerator data for MethodDecl tokens. + DAC_UNEXPECTED(); + + STDMETHODIMP_(ULONG) EnumMethodImplGetCount( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. + DAC_UNEXPECTED(); + + STDMETHODIMP_(void) EnumMethodImplReset( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. + DAC_UNEXPECTED(); + + __checkReturn + STDMETHODIMP EnumMethodImplNext( // return hresult (S_OK = TRUE, S_FALSE = FALSE or error code) + HENUMInternal *phEnumBody, // [IN] input enum for MethodBody + HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl + mdToken *ptkBody, // [OUT] return token for MethodBody + mdToken *ptkDecl) // [OUT] return token for MethodDecl + DAC_UNEXPECTED(); + + STDMETHODIMP_(void) EnumMethodImplClose( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. + DAC_UNEXPECTED(); + + //***************************************** + // Enumerator helpers for memberdef, memberref, interfaceimp, + // event, property, param, methodimpl + //***************************************** + + __checkReturn + STDMETHODIMP EnumGlobalFunctionsInit( // return hresult + HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data + + __checkReturn + STDMETHODIMP EnumGlobalFieldsInit( // return hresult + HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data + + + __checkReturn + STDMETHODIMP EnumInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + mdToken tkParent, // [IN] token to scope the search + HENUMInternal *phEnum); // [OUT] the enumerator to fill + + __checkReturn + STDMETHODIMP EnumAllInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + HENUMInternal *phEnum); // [OUT] the enumerator to fill + + __checkReturn + STDMETHODIMP EnumCustomAttributeByNameInit(// return S_FALSE if record not found + mdToken tkParent, // [IN] token to scope the search + LPCSTR szName, // [IN] CustomAttribute's name to scope the search + HENUMInternal *phEnum); // [OUT] the enumerator to fill + + __checkReturn + STDMETHODIMP GetParentToken( + mdToken tkChild, // [IN] given child token + mdToken *ptkParent); // [OUT] returning parent + + __checkReturn + STDMETHODIMP GetCustomAttributeProps( + mdCustomAttribute at, // The attribute. + mdToken *ptkType); // Put attribute type here. + + __checkReturn + STDMETHODIMP GetCustomAttributeAsBlob( + mdCustomAttribute cv, // [IN] given custom attribute token + void const **ppBlob, // [OUT] return the pointer to internal blob + ULONG *pcbSize); // [OUT] return the size of the blob + + __checkReturn + STDMETHODIMP GetCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData); // [OUT] Put size of data here. + + __checkReturn + STDMETHODIMP GetNameOfCustomAttribute( // S_OK or error. + mdCustomAttribute mdAttribute, // [IN] The Custom Attribute + LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute. + LPCUTF8 *pszName); // [OUT] Name of Custom Attribute. + + // returned void in v1.0/v1.1 + __checkReturn + STDMETHODIMP GetScopeProps( + LPCSTR *pszName, // [OUT] scope name + GUID *pmvid); // [OUT] version id + + // finding a particular method + __checkReturn + STDMETHODIMP FindMethodDef( + mdTypeDef classdef, // [IN] given typedef + LPCSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodDef *pmd); // [OUT] matching memberdef + + // return a iSeq's param given a MethodDef + __checkReturn + STDMETHODIMP FindParamOfMethod( // S_OK or error. + mdMethodDef md, // [IN] The owning method of the param. + ULONG iSeq, // [IN} The sequence # of the param. + mdParamDef *pparamdef); // [OUT] Put ParamDef token here. + + //***************************************** + // + // GetName* functions + // + //***************************************** + + // return the name and namespace of typedef + __checkReturn + STDMETHODIMP GetNameOfTypeDef( + mdTypeDef classdef, // given classdef + LPCSTR *pszname, // return class name(unqualified) + LPCSTR *psznamespace); // return the name space name + + __checkReturn + STDMETHODIMP GetIsDualOfTypeDef( + mdTypeDef classdef, // [IN] given classdef. + ULONG *pDual); // [OUT] return dual flag here. + + __checkReturn + STDMETHODIMP GetIfaceTypeOfTypeDef( + mdTypeDef tkTypeDef, + ULONG * pIface); // [OUT] 0=dual, 1=vtable, 2=dispinterface + + __checkReturn + STDMETHODIMP GetNameOfMethodDef( + mdMethodDef tkMethodDef, + LPCSTR * pszName); + + __checkReturn + STDMETHODIMP GetNameAndSigOfMethodDef( + mdMethodDef methoddef, // [IN] given memberdef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszName); + + // return the name of a FieldDef + __checkReturn + STDMETHODIMP GetNameOfFieldDef( + mdFieldDef fd, // given memberdef + LPCSTR *pszName); + + // return the name of typeref + __checkReturn + STDMETHODIMP GetNameOfTypeRef( + mdTypeRef classref, // [IN] given typeref + LPCSTR *psznamespace, // [OUT] return typeref name + LPCSTR *pszname); // [OUT] return typeref namespace + + // return the resolutionscope of typeref + __checkReturn + STDMETHODIMP GetResolutionScopeOfTypeRef( + mdTypeRef classref, // given classref + mdToken *ptkResolutionScope); + + // return the typeref token given the name. + __checkReturn + STDMETHODIMP FindTypeRefByName( + LPCSTR szNamespace, // [IN] Namespace for the TypeRef. + LPCSTR szName, // [IN] Name of the TypeRef. + mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef. + mdTypeRef *ptk); // [OUT] TypeRef token returned. + + // return the TypeDef properties + __checkReturn + STDMETHODIMP GetTypeDefProps( // return hresult + mdTypeDef classdef, // given classdef + DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract + mdToken *ptkExtends); // [OUT] Put base class TypeDef/TypeRef here. + + // return the item's guid + __checkReturn + STDMETHODIMP GetItemGuid( // return hresult + mdToken tkObj, // [IN] given item. + CLSID *pGuid); // [OUT] Put guid here. + + // get enclosing class of NestedClass. + __checkReturn + STDMETHODIMP GetNestedClassProps( // S_OK or error + mdTypeDef tkNestedClass, // [IN] NestedClass token. + mdTypeDef *ptkEnclosingClass); // [OUT] EnclosingClass token. + + // Get count of Nested classes given the enclosing class. + __checkReturn + STDMETHODIMP GetCountNestedClasses( // return count of Nested classes. + mdTypeDef tkEnclosingClass, // [IN]Enclosing class. + ULONG *pcNestedClassesCount); + + // Return array of Nested classes given the enclosing class. + __checkReturn + STDMETHODIMP GetNestedClasses( // Return actual count. + mdTypeDef tkEnclosingClass, // [IN] Enclosing class. + mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens. + ULONG ulNestedClasses, // [IN] Size of array. + ULONG *pcNestedClasses); + + // return the ModuleRef properties + __checkReturn + STDMETHODIMP GetModuleRefProps( + mdModuleRef mur, // [IN] moduleref token + LPCSTR *pszName); // [OUT] buffer to fill with the moduleref name + + + //***************************************** + // + // GetSig* functions + // + //***************************************** + __checkReturn + STDMETHODIMP GetSigOfMethodDef( + mdMethodDef methoddef, // [IN] given memberdef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig); + + __checkReturn + STDMETHODIMP GetSigOfFieldDef( + mdMethodDef methoddef, // [IN] given memberdef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig); + + __checkReturn + STDMETHODIMP GetSigFromToken( + mdToken tk, // FieldDef, MethodDef, Signature or TypeSpec token + ULONG * pcbSig, + PCCOR_SIGNATURE * ppSig); + + + + //***************************************** + // get method property + //***************************************** + __checkReturn + STDMETHODIMP GetMethodDefProps( + mdMethodDef md, // The method for which to get props. + DWORD *pdwFlags); + + STDMETHODIMP_(ULONG) GetMethodDefSlot( + mdMethodDef mb); // The method for which to get props. + + //***************************************** + // return method implementation informaiton, like RVA and implflags + //***************************************** + __checkReturn + STDMETHODIMP GetMethodImplProps( + mdToken tk, // [IN] MethodDef or MethodImpl + DWORD *pulCodeRVA, // [OUT] CodeRVA + DWORD *pdwImplFlags); // [OUT] Impl. Flags + + //***************************************************************************** + // return the field RVA + //***************************************************************************** + __checkReturn + STDMETHODIMP GetFieldRVA( + mdToken fd, // [IN] FieldDef + ULONG *pulCodeRVA); // [OUT] CodeRVA + + //***************************************************************************** + // return the field offset for a given field + //***************************************************************************** + __checkReturn + STDMETHODIMP GetFieldOffset( + mdFieldDef fd, // [IN] fielddef + ULONG *pulOffset); // [OUT] FieldOffset + + //***************************************** + // get field property + //***************************************** + __checkReturn + STDMETHODIMP GetFieldDefProps( + mdFieldDef fd, // [IN] given fielddef + DWORD *pdwFlags); // [OUT] return fdPublic, fdPrive, etc flags + + //***************************************************************************** + // return default value of a token(could be paramdef, fielddef, or property + //***************************************************************************** + __checkReturn + STDMETHODIMP GetDefaultValue( + mdToken tk, // [IN] given FieldDef, ParamDef, or Property + MDDefaultValue *pDefaultValue); // [OUT] default value to fill + + + //***************************************** + // get dispid of a MethodDef or a FieldDef + //***************************************** + __checkReturn + STDMETHODIMP GetDispIdOfMemberDef( // return hresult + mdToken tk, // [IN] given methoddef or fielddef + ULONG *pDispid); // [OUT] Put the dispid here. + + //***************************************** + // return TypeRef/TypeDef given an InterfaceImpl token + //***************************************** + __checkReturn + STDMETHODIMP GetTypeOfInterfaceImpl( // return the TypeRef/typedef token for the interfaceimpl + mdInterfaceImpl iiImpl, // given a interfaceimpl + mdToken *ptkType); + + __checkReturn + STDMETHODIMP GetMethodSpecProps( + mdMethodSpec mi, // [IN] The method instantiation + mdToken *tkParent, // [OUT] MethodDef or MemberRef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob); // [OUT] actual size of signature blob + + //***************************************** + // look up function for TypeDef + //***************************************** + __checkReturn + STDMETHODIMP FindTypeDef( + LPCSTR szNamespace, // [IN] Namespace for the TypeDef. + LPCSTR szName, // [IN] Name of the TypeDef. + mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class. + mdTypeDef *ptypedef); // [OUT] return typedef + + __checkReturn + STDMETHODIMP FindTypeDefByGUID( + REFGUID guid, // guid to look up + mdTypeDef *ptypedef); // return typedef + + + + //***************************************** + // return name and sig of a memberref + //***************************************** + __checkReturn + STDMETHODIMP GetNameAndSigOfMemberRef( // return name here + mdMemberRef memberref, // given memberref + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszName); + + //***************************************************************************** + // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef + //***************************************************************************** + __checkReturn + STDMETHODIMP GetParentOfMemberRef( + mdMemberRef memberref, // given memberref + mdToken *ptkParent); // return the parent token + + __checkReturn + STDMETHODIMP GetParamDefProps( + mdParamDef paramdef, // given a paramdef + USHORT *pusSequence, // [OUT] slot number for this parameter + DWORD *pdwAttr, // [OUT] flags + LPCSTR *pszName); // [OUT] return the name of the parameter + + //****************************************** + // property info for method. + //****************************************** + __checkReturn + STDMETHODIMP GetPropertyInfoForMethodDef( // Result. + mdMethodDef md, // [IN] memberdef + mdProperty *ppd, // [OUT] put property token here + LPCSTR *pName, // [OUT] put pointer to name here + ULONG *pSemantic); // [OUT] put semantic here + + //***************************************** + // class layout/sequence information + //***************************************** + __checkReturn + STDMETHODIMP GetClassPackSize( // [OUT] return error if a class doesn't have packsize info + mdTypeDef td, // [IN] give typedef + ULONG *pdwPackSize); // [OUT] return the pack size of the class. 1, 2, 4, 8 or 16 + + __checkReturn + STDMETHODIMP GetClassTotalSize( // [OUT] return error if a class doesn't have total size info + mdTypeDef td, // [IN] give typedef + ULONG *pdwClassSize); // [OUT] return the total size of the class + + __checkReturn + STDMETHODIMP GetClassLayoutInit( + mdTypeDef td, // [IN] give typedef + MD_CLASS_LAYOUT *pLayout); // [OUT] set up the status of query here + + __checkReturn + STDMETHODIMP GetClassLayoutNext( + MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here + mdFieldDef *pfd, // [OUT] return the fielddef + ULONG *pulOffset); // [OUT] return the offset/ulSequence associate with it + + //***************************************** + // marshal information of a field + //***************************************** + __checkReturn + STDMETHODIMP GetFieldMarshal( // return error if no native type associate with the token + mdFieldDef fd, // [IN] given fielddef + PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature + ULONG *pcbNativeType); // [OUT] the count of bytes of *ppvNativeType + + + //***************************************** + // property APIs + //***************************************** + // find a property by name + __checkReturn + STDMETHODIMP FindProperty( + mdTypeDef td, // [IN] given a typdef + LPCSTR szPropName, // [IN] property name + mdProperty *pProp); // [OUT] return property token + + __checkReturn + STDMETHODIMP GetPropertyProps( + mdProperty prop, // [IN] property token + LPCSTR *szProperty, // [OUT] property name + DWORD *pdwPropFlags, // [OUT] property flags. + PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob + ULONG *pcbSig); // [OUT] count of bytes in *ppvSig + + //********************************** + // Event APIs + //********************************** + __checkReturn + STDMETHODIMP FindEvent( + mdTypeDef td, // [IN] given a typdef + LPCSTR szEventName, // [IN] event name + mdEvent *pEvent); // [OUT] return event token + + __checkReturn + STDMETHODIMP GetEventProps( // S_OK, S_FALSE, or error. + mdEvent ev, // [IN] event token + LPCSTR *pszEvent, // [OUT] Event name + DWORD *pdwEventFlags, // [OUT] Event flags. + mdToken *ptkEventType); // [OUT] EventType class + + + //********************************** + // Generics APIs + //********************************** + __checkReturn + STDMETHODIMP GetGenericParamProps( // S_OK or error. + mdGenericParam rd, // [IN] The type parameter + ULONG* pulSequence, // [OUT] Parameter sequence number + DWORD* pdwAttr, // [OUT] Type parameter flags (for future use) + mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef) + DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use) + LPCSTR *szName); // [OUT] The name + + __checkReturn + STDMETHODIMP GetGenericParamConstraintProps( // S_OK or error. + mdGenericParamConstraint rd, // [IN] The constraint token + mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained + mdToken *ptkConstraintType); // [OUT] TypeDef/Ref/Spec constraint + + //********************************** + // find a particular associate of a property or an event + //********************************** + __checkReturn + STDMETHODIMP FindAssociate( + mdToken evprop, // [IN] given a property or event token + DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire) + mdMethodDef *pmd); // [OUT] return method def token + + __checkReturn + STDMETHODIMP EnumAssociateInit( + mdToken evprop, // [IN] given a property or an event token + HENUMInternal *phEnum); // [OUT] cursor to hold the query result + + __checkReturn + STDMETHODIMP GetAllAssociates( + HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts + ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output + ULONG cAssociateRec); // [IN] size of the buffer + + + //********************************** + // Get info about a PermissionSet. + //********************************** + __checkReturn + STDMETHODIMP GetPermissionSetProps( + mdPermission pm, // [IN] the permission token. + DWORD *pdwAction, // [OUT] CorDeclSecurity. + void const **ppvPermission, // [OUT] permission blob. + ULONG *pcbPermission); // [OUT] count of bytes of pvPermission. + + //**************************************** + // Get the String given the String token. + // Returns a pointer to the string, or NULL in case of error. + //**************************************** + __checkReturn + STDMETHODIMP GetUserString( + mdString stk, // [IN] the string token. + ULONG *pchString, // [OUT] count of characters in the string. + BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80. + LPCWSTR *pwszUserString); + + //***************************************************************************** + // p-invoke APIs. + //***************************************************************************** + __checkReturn + STDMETHODIMP GetPinvokeMap( + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD *pdwMappingFlags, // [OUT] Flags used for mapping. + LPCSTR *pszImportName, // [OUT] Import name. + mdModuleRef *pmrImportDLL); // [OUT] ModuleRef token for the target DLL. + + //***************************************************************************** + // Assembly MetaData APIs. + //***************************************************************************** + __checkReturn + STDMETHODIMP GetAssemblyProps( + mdAssembly mda, // [IN] The Assembly for which to get the properties. + const void **ppbPublicKey, // [OUT] Pointer to the public key. + ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key. + ULONG *pulHashAlgId, // [OUT] Hash Algorithm. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + DWORD *pdwAssemblyFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetAssemblyRefProps( + mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties. + const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token. + ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + const void **ppbHashValue, // [OUT] Hash blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob. + DWORD *pdwAssemblyRefFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetFileProps( + mdFile mdf, // [IN] The File for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob. + DWORD *pdwFileFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetExportedTypeProps( + mdExportedType mdct, // [IN] The ExportedType for which to get the properties. + LPCSTR *pszNamespace, // [OUT] Buffer to fill with namespace. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file. + DWORD *pdwExportedTypeFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetManifestResourceProps( + mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file. + DWORD *pdwResourceFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP FindExportedTypeByName( // S_OK or error + LPCSTR szNamespace, // [IN] Namespace of the ExportedType. + LPCSTR szName, // [IN] Name of the ExportedType. + mdExportedType tkEnclosingType, // [IN] Token for the enclosing Type. + mdExportedType *pmct); // [OUT] Put ExportedType token here. + + __checkReturn + STDMETHODIMP FindManifestResourceByName(// S_OK or error + LPCSTR szName, // [IN] Name of the resource. + mdManifestResource *pmmr); // [OUT] Put ManifestResource token here. + + __checkReturn + STDMETHODIMP GetAssemblyFromScope( // S_OK or error + mdAssembly *ptkAssembly); // [OUT] Put token here. + + //*************************************************************************** + // return properties regarding a TypeSpec + //*************************************************************************** + __checkReturn + STDMETHODIMP GetTypeSpecFromToken( // S_OK or error. + mdTypeSpec typespec, // [IN] Signature token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token. + ULONG *pcbSig); // [OUT] return size of signature. + + + //***************************************************************************** + // This function gets the "built for" version of a metadata scope. + // NOTE: if the scope has never been saved, it will not have a built-for + // version, and an empty string will be returned. + //***************************************************************************** + __checkReturn + STDMETHODIMP GetVersionString( // S_OK or error. + LPCSTR *pVer); // [OUT] Put version string here. + + + //***************************************************************************** + // helpers to convert a text signature to a com format + //***************************************************************************** + __checkReturn + STDMETHODIMP ConvertTextSigToComSig( // Return hresult. + BOOL fCreateTrIfNotFound, // [IN] create typeref if not found + LPCSTR pSignature, // [IN] class file format signature + CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature + ULONG *pcbCount); // [OUT] the result size of signature + + __checkReturn + STDMETHODIMP SetUserContextData( // S_OK or E_NOTIMPL + IUnknown *pIUnk); // The user context. + + STDMETHODIMP_(BOOL) IsValidToken( // True or False. + mdToken tk); // [IN] Given token. + + STDMETHODIMP_(IUnknown *) GetCachedPublicInterface(BOOL fWithLock); // return the cached public interface + __checkReturn + STDMETHODIMP SetCachedPublicInterface(IUnknown *pUnk); // return hresult + STDMETHODIMP_(UTSemReadWrite*) GetReaderWriterLock(); // return the reader writer lock + __checkReturn + STDMETHODIMP SetReaderWriterLock(UTSemReadWrite *pSem) + { + _ASSERTE(m_pSemReadWrite == NULL); + m_pSemReadWrite = pSem; + INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);) + return NOERROR; + } + + // *** IMDInternalImportENC methods *** + __checkReturn + STDMETHODIMP ApplyEditAndContinue( // S_OK or error. + MDInternalRW *pDelta); // MD with the ENC delta. + + __checkReturn + STDMETHODIMP EnumDeltaTokensInit( // return hresult + HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data + + STDMETHODIMP_(mdModule) GetModuleFromScope(void); + + // finding a particular method + __checkReturn + STDMETHODIMP FindMethodDefUsingCompare( + mdTypeDef classdef, // [IN] given typedef + LPCSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + PSIGCOMPARE pSignatureCompare, // [IN] Routine to compare signatures + void* pSignatureArgs, // [IN] Additional info to supply the compare function + mdMethodDef *pmd); // [OUT] matching memberdef + + //***************************************************************************** + // return the table pointer and size for a given table index + //***************************************************************************** + __checkReturn + STDMETHODIMP GetTableInfoWithIndex( + ULONG index, // [IN] pass in the index + void **pTable, // [OUT] pointer to table at index + void **pTableSize); // [OUT] size of table at index + + __checkReturn + STDMETHODIMP ApplyEditAndContinue( + void *pData, // [IN] the delta metadata + ULONG cbData, // [IN] length of pData + IMDInternalImport **ppv); // [OUT] the resulting metadata interface + + + FORCEINLINE CLiteWeightStgdbRW* GetMiniStgdb() { return m_pStgdb; } + FORCEINLINE UTSemReadWrite *getReaderWriterLock() { return m_pSemReadWrite; } + + + CLiteWeightStgdbRW *m_pStgdb; + +private: + mdTypeDef m_tdModule; // <Module> typedef value. + LONG m_cRefs; // Ref count. + bool m_fOwnStgdb; + IUnknown *m_pUnk; + IUnknown *m_pUserUnk; // Release at shutdown. + IMetaDataHelper *m_pIMetaDataHelper;// pointer to cached public interface + UTSemReadWrite *m_pSemReadWrite; // read write lock for multi-threading. + bool m_fOwnSem; // Does MDInternalRW own this read write lock object? + +public: + STDMETHODIMP_(DWORD) GetMetadataStreamVersion() + { + return (DWORD)m_pStgdb->m_MiniMd.m_Schema.m_minor | + ((DWORD)m_pStgdb->m_MiniMd.m_Schema.m_major << 16); + }; + + __checkReturn + STDMETHODIMP SetVerifiedByTrustedSource(// return hresult + BOOL fVerified) + { + m_pStgdb->m_MiniMd.SetVerifiedByTrustedSource(fVerified); + return S_OK; + } + + STDMETHODIMP GetRvaOffsetData(// S_OK or error + DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table. + DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table. + DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table. + DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table. + DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table. + DWORD *pFieldRvaCount) // [OUT] Number of records in FieldRVA table. + { + return m_pStgdb->m_MiniMd.GetRvaOffsetData( + pFirstMethodRvaOffset, + pMethodDefRecordSize, + pMethodDefCount, + pFirstFieldRvaOffset, + pFieldRvaRecordSize, + pFieldRvaCount); + } +}; // class MDInternalRW + +#endif //FEATURE_METADATA_INTERNAL_APIS + +#endif // __MDInternalRW__h__ diff --git a/src/coreclr/md/inc/mdlog.h b/src/coreclr/md/inc/mdlog.h new file mode 100644 index 00000000000..4459183f15f --- /dev/null +++ b/src/coreclr/md/inc/mdlog.h @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDLog.h - Meta data logging helper. +// + +// +//***************************************************************************** +#ifndef __MDLog_h__ +#define __MDLog_h__ + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) +#define LOGGING +#endif + +#include <log.h> + +#define LOGMD LF_METADATA, LL_INFO10000 +#define LOG_MDCALL(func) LOG((LF_METADATA, LL_INFO10000, "MD: %s\n", #func)) + +#define MDSTR(str) ((str) ? str : W("<null>")) +#define MDSTRA(str) ((str) ? str : "<null>") + +#endif // __MDLog_h__ diff --git a/src/coreclr/md/inc/metadatahash.h b/src/coreclr/md/inc/metadatahash.h new file mode 100644 index 00000000000..5f78416da93 --- /dev/null +++ b/src/coreclr/md/inc/metadatahash.h @@ -0,0 +1,206 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaDataHash.h -- Meta data hash data structures. +// + +// +// Used by Emitters and by E&C. +// +//***************************************************************************** +#ifndef _MetaDataHash_h_ +#define _MetaDataHash_h_ + +#if _MSC_VER >= 1100 + # pragma once +#endif + +#include "utilcode.h" + + +#define REHASH_THREADSHOLD 3 + + +//***************************************************************************** +// A hash entry list item. +//***************************************************************************** +struct TOKENHASHENTRY +{ + mdToken tok; + ULONG ulHash; + ULONG iNext; +}; + +//***************************************************************************** +// The following is a hash class definition used for hashing MemberDef. The difference +// from the hash table above is because it is expansive to retrieve the parent for MemberDef. +// +//***************************************************************************** +struct MEMBERDEFHASHENTRY +{ + mdToken tok; + mdToken tkParent; + ULONG ulHash; + ULONG iNext; +}; + + +//***************************************************************************** +// This class is used to create transient indexes for meta data structures. +// This class is generic; one must override it to provide hashing and +// accessor methods for your specific record type. It can start out on top +// of malloc with a small memory footprint, and as you get larger, it must +// be capable of rehashing. +//***************************************************************************** +template <class Entry> class CMetaDataHashTemplate +{ +public: + CMetaDataHashTemplate() + { + m_rgBuckets = 0; + m_cItems = 0; + m_iBuckets = 0; + } + + ~CMetaDataHashTemplate() + { + // Free the bucket list. + if (m_rgBuckets) + { + delete [] m_rgBuckets; + m_rgBuckets = 0; + m_cItems = 0; + m_iBuckets = 0; + } + } + +//***************************************************************************** +// Called to allocate the hash table entries so that new data may be added. +//***************************************************************************** + HRESULT NewInit( // Return status. + int iBuckets=17) // How many buckets you want. + { + m_rgBuckets = new (nothrow) int[iBuckets]; + if (!m_rgBuckets) + return (OutOfMemory()); + m_iBuckets = iBuckets; + memset(m_rgBuckets, ~0, sizeof(int) * iBuckets); + return (S_OK); + } + +//***************************************************************************** +// Add new items to the hash list. +//***************************************************************************** + Entry *Add( // Pointer to element to write to. + ULONG iHash) // Hash value of entry to add. + { + Entry *p = 0; + HRESULT hr; + + int iBucket = iHash % m_iBuckets; + + if (m_cItems > REHASH_THREADSHOLD * m_iBuckets) + { + hr = ReHash(); + if (FAILED(hr)) + return (0); + iBucket = iHash % m_iBuckets; + } + + // Add a new item pointer. + p = m_Heap.Append(); + if (!p) + return (0); + + // Chain the new item to the front of the heap. + p->iNext = m_rgBuckets[iBucket]; + p->ulHash = iHash; + m_cItems++; + m_rgBuckets[iBucket] = m_Heap.ItemIndex(p); + return (p); + } + + +//***************************************************************************** +// Grow the hash table +//***************************************************************************** + HRESULT ReHash() + { + int *rgBuckets; + int iBuckets; + int iBucket; + int index; + int iCount; + Entry *p = 0; + + iBuckets = m_iBuckets*2 -1; + rgBuckets = new (nothrow) int[iBuckets]; + if (!rgBuckets) + return (OutOfMemory()); + memset(rgBuckets, ~0, sizeof(int) * iBuckets); + + // loop through each of data and rehash them + iCount = m_Heap.Count(); + for (index = 0; index < iCount; index++) + { + // get the hash value of the entry + p = m_Heap.Get(index); + + // rehash the entry + iBucket = p->ulHash % iBuckets; + + // Chain the item to the front of the new heap. + p->iNext = rgBuckets[iBucket]; + rgBuckets[iBucket] = index; + } + + // swap the hash table + delete [] m_rgBuckets; + m_rgBuckets = rgBuckets; + m_iBuckets = iBuckets; + return NOERROR; + + } + +//***************************************************************************** +// Find first/find next node for a chain given the hash. +//***************************************************************************** + Entry *FindFirst( // Return entry. + ULONG iHash, // The hash value for the entry. + int &POS) // Current position. + { + int iBucket = iHash % m_iBuckets; + POS = m_rgBuckets[iBucket]; + return (FindNext(POS)); + } + + Entry *FindNext( // Return entry or 0. + int &POS) // Current location. + { + Entry *p; + + if (POS == ~0) + return (0); + + p = m_Heap.Get(POS); + POS = p->iNext; + return (p); + } + +private: + CDynArray<Entry> m_Heap; // First heap in the list. + int *m_rgBuckets; // Bucket list. + int m_iBuckets; // How many buckets. + int m_cItems; // Number of items in the hash +}; + + +class CMetaDataHashBase : public CMetaDataHashTemplate<TOKENHASHENTRY> +{ +}; + +class CMemberDefHash : public CMetaDataHashTemplate<MEMBERDEFHASHENTRY> +{ +}; + +#endif // _MetaDataHash_h_ diff --git a/src/coreclr/md/inc/metamodel.h b/src/coreclr/md/inc/metamodel.h new file mode 100644 index 00000000000..d06932e1011 --- /dev/null +++ b/src/coreclr/md/inc/metamodel.h @@ -0,0 +1,2165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaModel.h -- header file for compressed COM+ metadata. +// + +// +//***************************************************************************** +#ifndef _METAMODEL_H_ +#define _METAMODEL_H_ + +#if _MSC_VER >= 1100 +#pragma once +#endif + +#include <cor.h> +#include <stgpool.h> +#include <metamodelpub.h> +#include "metadatatracker.h" + +#include "../datablob.h" +#include "../debug_metadata.h" + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +#include "portablepdbmdds.h" +#include "portablepdbmdi.h" +#endif + +#define ALLOCATED_MEMORY_MARKER 0xff + +// Version numbers for metadata format. + +#define METAMODEL_MAJOR_VER_V1_0 1 // Major version for v1.0 +#define METAMODEL_MINOR_VER_V1_0 0 // Minor version for v1.0 + +#define METAMODEL_MAJOR_VER_V2_0 2 // Major version for v2.0 +#define METAMODEL_MINOR_VER_V2_0 0 // Minor version for v2.0 + +#define METAMODEL_MAJOR_VER 2 +#define METAMODEL_MINOR_VER 0 + +// Metadata version number up through Whidbey Beta2 +#define METAMODEL_MAJOR_VER_B1 1 +#define METAMODEL_MINOR_VER_B1 1 + + +typedef enum MetadataVersion +{ + MDVersion1 = 0x00000001, + MDVersion2 = 0x00000002, + + // @TODO - this value should be updated when we increase the version number + MDDefaultVersion = 0x00000002 +} MetadataVersion; + + + +struct HENUMInternal; +extern const CCodedTokenDef g_CodedTokens[CDTKN_COUNT]; +extern const CMiniTableDefEx g_Tables[TBL_COUNT]; // The table definitions. + +struct TblCol +{ + ULONG m_ixtbl; // Table ID. + ULONG m_ixcol; // Column ID. +}; +extern TblCol g_PtrTableIxs[TBL_COUNT]; + +// This abstract defines the common functions that can be used for RW and RO internally +// (The primary user for this is Compiler\ImportHelper.cpp) +class IMetaModelCommon +{ +public: + __checkReturn + virtual HRESULT CommonGetScopeProps( + LPCUTF8 *pszName, + GUID *pMvid) = 0; + + __checkReturn + virtual HRESULT CommonGetTypeRefProps( + mdTypeRef tr, + LPCUTF8 *pszNamespace, + LPCUTF8 *pszName, + mdToken *ptkResolution) = 0; + + __checkReturn + virtual HRESULT CommonGetTypeDefProps( + mdTypeDef td, + LPCUTF8 *pszNameSpace, + LPCUTF8 *pszName, + DWORD *pdwFlags, + mdToken *pdwExtends, + ULONG *pMethodList) = 0; + + __checkReturn + virtual HRESULT CommonGetTypeSpecProps( + mdTypeSpec ts, + PCCOR_SIGNATURE *ppvSig, + ULONG *pcbSig) = 0; + + __checkReturn + virtual HRESULT CommonGetEnclosingClassOfTypeDef( + mdTypeDef td, + mdTypeDef *ptkEnclosingTypeDef) = 0; + + __checkReturn + virtual HRESULT CommonGetAssemblyProps( + USHORT *pusMajorVersion, + USHORT *pusMinorVersion, + USHORT *pusBuildNumber, + USHORT *pusRevisionNumber, + DWORD *pdwFlags, + const void **ppbPublicKey, + ULONG *pcbPublicKey, + LPCUTF8 *pszName, + LPCUTF8 *pszLocale) = 0; + + __checkReturn + virtual HRESULT CommonGetAssemblyRefProps( + mdAssemblyRef tkAssemRef, + USHORT *pusMajorVersion, + USHORT *pusMinorVersion, + USHORT *pusBuildNumber, + USHORT *pusRevisionNumber, + DWORD *pdwFlags, + const void **ppbPublicKeyOrToken, + ULONG *pcbPublicKeyOrToken, + LPCUTF8 *pszName, + LPCUTF8 *pszLocale, + const void **ppbHashValue, + ULONG *pcbHashValue) = 0; + + __checkReturn + virtual HRESULT CommonGetModuleRefProps( + mdModuleRef tkModuleRef, + LPCUTF8 *pszName) = 0; + + __checkReturn + virtual HRESULT CommonFindExportedType( + LPCUTF8 szNamespace, + LPCUTF8 szName, + mdToken tkEnclosingType, + mdExportedType *ptkExportedType) = 0; + + __checkReturn + virtual HRESULT CommonGetExportedTypeProps( + mdToken tkExportedType, + LPCUTF8 *pszNamespace, + LPCUTF8 *pszName, + mdToken *ptkImpl) = 0; + + virtual int CommonIsRo() = 0; + + __checkReturn + HRESULT CommonGetCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData) // [OUT] Put size of data here. + { + return CommonGetCustomAttributeByNameEx(tkObj, szName, NULL, ppData, pcbData); + } + + __checkReturn + virtual HRESULT CommonGetCustomAttributeByNameEx( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + mdCustomAttribute *ptkCA, // [OUT] put custom attribute token here + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData) = 0; // [OUT] Put size of data here. + + __checkReturn + virtual HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd) = 0; + +}; // class IMetaModelCommon + + + +// An extension of IMetaModelCommon, exposed by read-only importers only. +// +// These methods were separated from IMetaModelCommon as +// Enc-aware versions of these methods haven't been needed +// and we don't want the maintainence and code-coverage cost +// of providing Enc-aware versions of these methods. +class IMetaModelCommonRO : public IMetaModelCommon +{ +public: + virtual HRESULT CommonGetMethodDefProps( + mdMethodDef tkMethodDef, + LPCUTF8 *pszName, + DWORD *pdwFlags, + PCCOR_SIGNATURE *ppvSigBlob, + ULONG *pcbSigBlob + ) = 0; + + virtual HRESULT CommonGetMemberRefProps( + mdMemberRef tkMemberRef, + mdToken *pParentToken + ) = 0; + + virtual ULONG CommonGetRowCount( // return hresult + DWORD tkKind) = 0; // [IN] pass in the kind of token. + + virtual HRESULT CommonGetMethodImpls( + mdTypeDef tkTypeDef, // [IN] typeDef to scope search + mdToken *ptkMethodImplFirst, // [OUT] returns first methodImpl token + ULONG *pMethodImplCount // [OUT] returns # of methodImpl tokens scoped to type + ) = 0; + + virtual HRESULT CommonGetMethodImplProps( + mdToken tkMethodImpl, // [IN] methodImpl + mdToken *pBody, // [OUT] returns body token + mdToken *pDecl // [OUT] returns decl token + ) = 0; + + virtual HRESULT CommonGetCustomAttributeProps( + mdCustomAttribute cv, // [IN] CustomAttribute token. + mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here. + mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here. + const void **ppBlob, // [OUT, OPTIONAL] Put pointer to data here. + ULONG *pcbSize) = 0; // [OUT, OPTIONAL] Put size of date here. + + virtual HRESULT CommonGetFieldDefProps( + mdFieldDef tkFieldDef, + mdTypeDef *ptkParent, + LPCUTF8 *pszName, + DWORD *pdwFlags + ) = 0; +}; // class IMetaModelCommonRO + + +//***************************************************************************** +// The mini, hard-coded schema. For each table, we persist the count of +// records. We also persist the size of string, blob, guid, and rid +// columns. From this information, we can calculate the record sizes, and +// then the sizes of the tables. +//***************************************************************************** + +class CMiniMdSchemaBase +{ +public: + ULONG m_ulReserved; // Reserved, must be zero. + BYTE m_major; // Version numbers. + BYTE m_minor; + BYTE m_heaps; // Bits for heap sizes. + BYTE m_rid; // log-base-2 of largest rid. + + // Bits for heap sizes. + enum { + HEAP_STRING_4 = 0x01, + HEAP_GUID_4 = 0x02, + HEAP_BLOB_4 = 0x04, + + PADDING_BIT = 0x08, // Tables can be created with an extra bit in columns, for growth. + + DELTA_ONLY = 0x20, // If set, only deltas were persisted. + EXTRA_DATA = 0x40, // If set, schema persists an extra 4 bytes of data. + HAS_DELETE = 0x80, // If set, this metadata can contain _Delete tokens. + }; + + unsigned __int64 m_maskvalid; // Bit mask of present table counts. + + unsigned __int64 m_sorted; // Bit mask of sorted tables. + FORCEINLINE bool IsSorted(ULONG ixTbl) + { return m_sorted & BIT(ixTbl) ? true : false; } + void SetSorted(ULONG ixTbl, int bVal) + { if (bVal) m_sorted |= BIT(ixTbl); + else m_sorted &= ~BIT(ixTbl); } + +#if BIGENDIAN + // Verify that the size hasn't changed (Update if necessary) + void ConvertEndianness() + { + _ASSERTE(sizeof(CMiniMdSchemaBase) == 0x18); + m_ulReserved = VAL32(m_ulReserved); + m_maskvalid = VAL64(m_maskvalid); + m_sorted = VAL64(m_sorted); + } +#else + // Nothing to do on little endian machine + void ConvertEndianness() {return ;} +#endif + +private: + FORCEINLINE unsigned __int64 BIT(ULONG ixBit) + { _ASSERTE(ixBit < (sizeof(__int64)*CHAR_BIT)); + return UI64(1) << ixBit; } + +}; + +class CMiniMdSchema : public CMiniMdSchemaBase +{ +public: + // These are not all persisted to disk. See LoadFrom() for details. + ULONG m_cRecs[TBL_COUNT]; // Counts of various tables. + + ULONG m_ulExtra; // Extra data, only persisted if non-zero. (m_heaps&EXTRA_DATA flags) + + ULONG LoadFrom(const void*, ULONG); // Load from a compressed version. Return bytes consumed. + ULONG SaveTo(void *); // Store a compressed version. Return bytes used in buffer. + __checkReturn + HRESULT InitNew(MetadataVersion); +}; + +//***************************************************************************** +// Helper macros and inline functions for navigating through the data. Many +// of the macros are used to define inline accessor functions. Everything +// is based on the naming conventions outlined at the top of the file. +//***************************************************************************** +#define _GETTER(tbl,fld) get##fld##Of##tbl(tbl##Rec *pRec) +#define _GETTER2(tbl,fld,x) get##fld##Of##tbl(tbl##Rec *pRec, x) +#define _GETTER3(tbl,fld,x,y) get##fld##Of##tbl(tbl##Rec *pRec, x, y) +#define _GETTER4(tbl,fld,x,y,z) get##fld##Of##tbl(tbl##Rec *pRec, x, y,z) + +// Direct getter for a field. Defines an inline function like: +// getSomeFieldOfXyz(XyzRec *pRec) { return pRec->m_SomeField;} +// Note that the returned value declaration is NOT included. +#if METADATATRACKER_ENABLED +#define _GETFLD(tbl,fld) _GETTER(tbl,fld){ PVOID pVal = (BYTE*)pRec + offsetof(tbl##Rec, m_##fld); \ + pVal = MetaDataTracker::NoteAccess(pVal); \ + return ((tbl##Rec*)((BYTE*)pVal - offsetof(tbl##Rec, m_##fld)))->Get##fld(); } +#else +#define _GETFLD(tbl,fld) _GETTER(tbl,fld){ return pRec->Get##fld();} +#endif + +// These functions call the helper function getIX to get a two or four byte value from a record, +// and then use that value as an index into the appropriate pool. +// getSomeFieldOfXyz(XyzRec *pRec) { return m_pStrings->GetString(getIX(pRec, _COLDEF(tbl,fld))); } +// Note that the returned value declaration is NOT included. + +// Column definition of a field: Looks like: +// m_XyzCol[XyzRec::COL_SomeField] +#define _COLDEF(tbl,fld) m_TableDefs[TBL_##tbl].m_pColDefs[tbl##Rec::COL_##fld] +#define _COLPAIR(tbl,fld) _COLDEF(tbl,fld), tbl##Rec::COL_##fld +// Size of a record. +#define _CBREC(tbl) m_TableDefs[TBL_##tbl].m_cbRec +// Count of records in a table. +#define _TBLCNT(tbl) m_Schema.m_cRecs[TBL_##tbl] + +#define _GETSTRA(tbl,fld) _GETTER2(tbl, fld, LPCSTR *pszString) \ +{ return getString(getI4(pRec, _COLDEF(tbl,fld)) & m_iStringsMask, pszString); } + +#define _GETSTRW(tbl,fld) _GETTER4(tbl, fld, LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer) \ +{ return getStringW(getI4(pRec, _COLDEF(tbl,fld)) & m_iStringsMask, szOut, cchBuffer, pcchBuffer); } + +#define _GETSTR(tbl, fld) \ + __checkReturn HRESULT _GETSTRA(tbl, fld); \ + __checkReturn HRESULT _GETSTRW(tbl, fld); + + +#define _GETGUID(tbl,fld) _GETTER2(tbl,fld,GUID *pGuid) \ +{ return getGuid(getI4(pRec, _COLDEF(tbl,fld)) & m_iGuidsMask, pGuid); } + +#define _GETBLOB(tbl,fld) __checkReturn HRESULT _GETTER3(tbl,fld,const BYTE **ppbData,ULONG *pcbSize) \ +{ \ + MetaData::DataBlob data; \ + HRESULT hr = getBlob(getI4(pRec, _COLDEF(tbl,fld)) & m_iBlobsMask, &data); \ + *ppbData = data.GetDataPointer(); \ + *pcbSize = (ULONG)data.GetSize(); \ + return hr; \ +} + +#define _GETSIGBLOB(tbl,fld) __checkReturn HRESULT _GETTER3(tbl,fld,PCCOR_SIGNATURE *ppbData,ULONG *pcbSize) \ +{ \ + MetaData::DataBlob data; \ + HRESULT hr = getBlob(getI4(pRec, _COLDEF(tbl,fld)) & m_iBlobsMask, &data); \ + *ppbData = (PCCOR_SIGNATURE)data.GetDataPointer(); \ + *pcbSize = (ULONG)data.GetSize(); \ + return hr; \ +} + +// Like the above functions, but just returns the RID, not a looked-up value. +#define _GETRID(tbl,fld) _GETTER(tbl,fld) \ +{ return getIX(pRec, _COLDEF(tbl,fld)); } + +// Like a RID, but turn into an actual token. +#define _GETTKN(tbl,fld,tok) _GETTER(tbl,fld) \ +{ return TokenFromRid(getIX(pRec, _COLDEF(tbl,fld)), tok); } + +// Get a coded token. +#define _GETCDTKN(tbl,fld,toks) _GETTER(tbl,fld) \ +{ return decodeToken(getIX(pRec, _COLDEF(tbl,fld)), toks, sizeof(toks)/sizeof(toks[0])); } + +// Functions for the start and end of a list. +#define _GETLIST(tbl,fld,tbl2) \ + RID _GETRID(tbl,fld); \ + __checkReturn HRESULT getEnd##fld##Of##tbl(RID nRowIndex, RID *pEndRid) { return getEndRidForColumn(TBL_##tbl, nRowIndex, _COLDEF(tbl,fld), TBL_##tbl2, pEndRid); } + + +#define BYTEARRAY_TO_COLDES(bytearray) (CMiniColDef*)((bytearray) + 1) +#define COLDES_TO_BYTEARRAY(coldes) (((BYTE*)(coldes))-1) + + +//***************************************************************************** +// Base class for the MiniMd. This class provides the schema to derived +// classes. It defines some virtual functions for access to data, suitable +// for use by functions where utmost performance is NOT a requirement. +// Finally, it provides some searching functions, built on the virtual +// data access functions (it is here assumed that if we are searching a table +// for some value, the cost of a virtual function call is acceptable). +// Some static utility functions and associated static data, shared across +// implementations, is provided here. +// +// NB: It's unfortunate that CMiniMDBase "implements" IMetaModelCommonRO rather +// than IMetaModelCommon, as methods on IMetaModelCommonRO are by definition, +// not Enc-aware. Ideally, CMiniMDBase should only implement IMetaModelCommon +// and CMiniMd should be the one implementing IMetaModelCommonRO. +// +// To make that happen would be a substantial refactoring job as RegMeta +// always embeds CMiniMdRW even when it was opened for ReadOnly. +//***************************************************************************** +class CMiniMdBase : public IMetaModelCommonRO +{ + + friend class VerifyLayoutsMD; // verifies class layout doesn't accidentally change + +public: + CMiniMdBase(); + ~CMiniMdBase(); + __checkReturn + virtual HRESULT vGetRow(UINT32 nTableIndex, UINT32 nRowIndex, void **ppRow) = 0; + ULONG GetCountRecs(ULONG ixTbl); + ULONG GetCountTables() { return m_TblCount;} + + // Search a table for the row containing the given key value. + // EG. Constant table has pointer back to Param or Field. + __checkReturn + virtual HRESULT vSearchTable( // RID of matching row, or 0. + ULONG ixTbl, // Table to search. + CMiniColDef sColumn, // Sorted key column, containing search value. + ULONG ulTarget, // Target for search. + RID *pRid) = 0; + + // Search a table for the highest-RID row containing a value that is less than + // or equal to the target value. EG. TypeDef points to first Field, but if + // a TypeDef has no fields, it points to first field of next TypeDef. + __checkReturn + virtual HRESULT vSearchTableNotGreater( // RID of matching row, or 0. + ULONG ixTbl, // Table to search. + CMiniColDef sColumn, // the column def containing search value + ULONG ulTarget, // target for search + RID *pRid) = 0; + + // Search for a custom value with a given type. + __checkReturn + HRESULT FindCustomAttributeFor(// RID of custom value, or 0. + RID rid, // The object's rid. + mdToken tkOjb, // The object's type. + mdToken tkType, // Type of custom value. + RID *pFoundRid); + + // Search for the specified Column Definition array in the global cache + BOOL FindSharedColDefs(// TRUE if we found a match in the global cache and updated pTable, FALSE otherwise + CMiniTableDef *pTable, // The table def that wants the column definition array + CMiniColDef *pColsToMatch, // The columns that we need to match + DWORD ixTbl); + + // Return RID to EventMap table, given the rid to a TypeDef. + __checkReturn + HRESULT FindEventMapFor(RID ridParent, RID *pFoundRid); + + // Return RID to PropertyMap table, given the rid to a TypeDef. + __checkReturn + HRESULT FindPropertyMapFor(RID ridParent, RID *pFoundRid); + +#if BIGENDIAN + // Swap a constant + __checkReturn + HRESULT SwapConstant(const void *pBlobValue, DWORD dwType, VOID *pConstant, ULONG BlobLength); +#endif + + // Pull two or four bytes out of a record. + inline static ULONG getIX(const void *pRec, CMiniColDef &def) + { + PVOID pVal = (BYTE *)pRec + def.m_oColumn; + if (def.m_cbColumn == 2) + { + METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal)); + ULONG ix = GET_UNALIGNED_VAL16(pVal); + return ix; + } + _ASSERTE(def.m_cbColumn == 4); + METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal)); + return GET_UNALIGNED_VAL32(pVal); + } + + inline static ULONG getIX_NoLogging(const void *pRec, CMiniColDef &def) + { + PVOID pVal = (BYTE *)pRec + def.m_oColumn; + if (def.m_cbColumn == 2) + { + ULONG ix = GET_UNALIGNED_VAL16(pVal); + return ix; + } + _ASSERTE(def.m_cbColumn == 4); + return GET_UNALIGNED_VAL32(pVal); + } + + // Pull four bytes out of a record. + FORCEINLINE static ULONG getI1(const void *pRec, CMiniColDef &def) + { + PVOID pVal = (BYTE *)pRec + def.m_oColumn; + METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal)); + return *(BYTE*)pVal; + } + + // Pull four bytes out of a record. + FORCEINLINE static ULONG getI4(const void *pRec, CMiniColDef &def) + { + PVOID pVal = (BYTE *)pRec + def.m_oColumn; + METADATATRACKER_ONLY(pVal = MetaDataTracker::NoteAccess(pVal)); + return GET_UNALIGNED_VAL32(pVal); + } + + // Function to encode a token into fewer bits. Looks up token type in array of types. + ULONG static encodeToken(RID rid, mdToken typ, const mdToken rTokens[], ULONG32 cTokens); + + // Decode a token. + inline static mdToken decodeToken(mdToken val, const mdToken rTokens[], ULONG32 cTokens) + { + //<TODO>@FUTURE: make compile-time calculation</TODO> + ULONG32 ix = (ULONG32)(val & ~(-1 << m_cb[cTokens])); + // If the coded token has an invalid table index, return the first entry + // from the array of valid token types. It would be preferable to + // return an error or to raise an exception. + if (ix >= cTokens) + return rTokens[0]; + return TokenFromRid(val >> m_cb[cTokens], rTokens[ix]); + } + static const int m_cb[]; + + // Given a token, what table does it live in? + inline ULONG GetTblForToken(mdToken tk) + { + tk = TypeFromToken(tk); + return (tk < mdtString) ? tk >> 24 : (ULONG) -1; + } + + //***************************************************************************** + // Returns whether the data has been verified, which means it was verified by a + // trusted source and has not changed. + // + // If so, this means the following aspects of the data can be trusted as accurate: + // - m_Schema.IsSorted[TBL_PropertyMap] reflects whether that table is sorted by Parent (see CMiniMdRW::PreSaveFull) + // - m_Schema.IsSorted[TBL_EventMap] reflects whether that table is sorted by Parent (see CMiniMdRW::PreSaveFull) + // + // Currently, metadata saved in NGen images is the only trusted source. + //***************************************************************************** + BOOL IsVerified() + { + return m_fVerifiedByTrustedSource && CommonIsRo(); + } + + void SetVerifiedByTrustedSource(BOOL fVerifiedByTrustedSource) + { + m_fVerifiedByTrustedSource = fVerifiedByTrustedSource; + } + + STDMETHODIMP GetRvaOffsetData(// S_OK or error + DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table. + DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table. + DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table. + DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table. + DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table. + DWORD *pFieldRvaCount) // [OUT] Number of records in FieldRVA table. + { + _ASSERTE("Not implemented"); + return E_NOTIMPL; + } + + //***************************************************************************** + // Some of the tables need coded tokens, not just rids (ie, the column can + // refer to more than one other table). Code the tokens into as few bits + // as possible, by using 1, 2, 3, etc., bits to code the token type, then + // use that value to index into an array of token types. + //***************************************************************************** + static const mdToken mdtTypeDefOrRef[3]; + static const mdToken mdtHasConstant[3]; + static const mdToken mdtHasCustomAttribute[24]; + static const mdToken mdtHasFieldMarshal[2]; + static const mdToken mdtHasDeclSecurity[3]; + static const mdToken mdtMemberRefParent[5]; + static const mdToken mdtHasSemantic[2]; + static const mdToken mdtMethodDefOrRef[2]; + static const mdToken mdtMemberForwarded[2]; + static const mdToken mdtImplementation[3]; + static const mdToken mdtCustomAttributeType[5]; + static const mdToken mdtResolutionScope[4]; + static const mdToken mdtTypeOrMethodDef[2]; + + +public: + virtual BOOL IsWritable() = 0; + + +protected: + CMiniMdSchema m_Schema; // data header. + ULONG m_TblCount; // Tables in this database. + BOOL m_fVerifiedByTrustedSource; // whether the data was verified by a trusted source + + // Declare CMiniColDefs for every table. They look like either: + // static const BYTE s_xyz[]; + // or + // static const BYTE* s_xyz; + + #include "mdcolumndescriptors.h" + + static const BYTE* const s_TableColumnDescriptors[TBL_COUNT]; + CMiniTableDef m_TableDefs[TBL_COUNT]; + + ULONG m_iStringsMask; + ULONG m_iGuidsMask; + ULONG m_iBlobsMask; + + __checkReturn + HRESULT SchemaPopulate(const void *pvData, ULONG cbData, ULONG *pcbUsed); + __checkReturn + HRESULT SchemaPopulate(const CMiniMdBase &that); + __checkReturn + HRESULT InitColsForTable(CMiniMdSchema &Schema, int ixTbl, CMiniTableDef *pTable, int bExtra, BOOL fUsePointers); + __checkReturn + HRESULT SchemaPopulate2(ULONG *pcbTables, int bExtra=false); + const CMiniTableDef* GetTableDefTemplate(int ixTbl); + __checkReturn + HRESULT SetNewColumnDefinition(CMiniTableDef *pTable, CMiniColDef* pCols, DWORD ixTbl); + +private: + + BOOL UsesAllocatedMemory(CMiniColDef* pCols); +}; + + +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN +#define MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED() MarkUnsafeToDelete() +#else +#define MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED() +#endif + +//***************************************************************************** +// This class defines the interface to the MiniMd. The template parameter is +// a derived class which provides implementations for a few primitives that +// the interface is built upon. +// To use, declare a class: +// class CMyMiniMd : public CMiniMdTemplate<CMyMiniMd> {...}; +// and provide implementations of the primitives. Any non-trivial +// implementation will also provide initialization, and probably serialization +// functions as well. +//***************************************************************************** +template <class Impl> class CMiniMdTemplate : public CMiniMdBase +{ +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN +protected: + CMiniMdTemplate() : m_isSafeToDelete(TRUE) { } +#endif + + // Primitives -- these must be implemented in the Impl class. +public: + __checkReturn + FORCEINLINE HRESULT getString(UINT32 nIndex, __out LPCSTR *pszString) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return static_cast<Impl*>(this)->Impl_GetString(nIndex, pszString); + } + __checkReturn + FORCEINLINE HRESULT getStringW(ULONG nIndex, __inout_ecount (cchBuffer) LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return static_cast<Impl*>(this)->Impl_GetStringW(nIndex, szOut, cchBuffer, pcchBuffer); + } + __checkReturn + FORCEINLINE HRESULT getGuid(UINT32 nIndex, GUID *pGuid) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return static_cast<Impl*>(this)->Impl_GetGuid(nIndex, pGuid); + } + __checkReturn + FORCEINLINE HRESULT getBlob(UINT32 nIndex, __out MetaData::DataBlob *pData) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return static_cast<Impl*>(this)->Impl_GetBlob(nIndex, pData); + } + __checkReturn + FORCEINLINE HRESULT getRow(UINT32 nTableIndex, UINT32 nRowIndex, __deref_out void **ppRow) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return static_cast<Impl*>(this)->Impl_GetRow(nTableIndex, nRowIndex, reinterpret_cast<BYTE **>(ppRow)); + } + __checkReturn + FORCEINLINE HRESULT getEndRidForColumn( + UINT32 nTableIndex, + RID nRowIndex, + CMiniColDef &columnDefinition, + UINT32 nTargetTableIndex, + __out RID *pEndRid) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return static_cast<Impl*>(this)->Impl_GetEndRidForColumn(nTableIndex, nRowIndex, columnDefinition, nTargetTableIndex, pEndRid); + } + __checkReturn + FORCEINLINE HRESULT doSearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ixColumn, ULONG ulTarget, RID *pFoundRid) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return static_cast<Impl*>(this)->Impl_SearchTable(ixTbl, sColumn, ixColumn, ulTarget, pFoundRid); + } + + // IMetaModelCommonRO interface beginning + __checkReturn + HRESULT CommonGetScopeProps( + LPCUTF8 *pszName, + GUID *pMvid) + { + HRESULT hr = S_OK; + ModuleRec *pRec; + IfFailRet(GetModuleRecord(1, &pRec)); + if (pszName != NULL) + { + IfFailRet(getNameOfModule(pRec, pszName)); + } + if (pMvid != NULL) + { + IfFailRet(getMvidOfModule(pRec, pMvid)); + } + return hr; + } + + //***************************************************************************** + // Search a table for multiple (adjacent) rows containing the given + // key value. EG, InterfaceImpls all point back to the implementing class. + //***************************************************************************** + __checkReturn + HRESULT SearchTableForMultipleRows( + ULONG ixTbl, // Table to search. + CMiniColDef sColumn, // Sorted key column, containing search value. + ULONG ulTarget, // Target for search. + RID *pEnd, // [OPTIONAL, OUT] + RID *pFoundRid) // First RID found, or 0. + { + HRESULT hr; + ULONG ridBegin; // RID of first entry. + ULONG ridEnd; // RID of first entry past last entry. + + // Search for any entry in the table. + IfFailRet(static_cast<Impl*>(this)->vSearchTable(ixTbl, sColumn, ulTarget, &ridBegin)); + + // If nothing found, return invalid RID. + if (ridBegin == 0) + { + if (pEnd != NULL) + { + *pEnd = 0; + } + *pFoundRid = 0; + return S_OK; + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // If you change the rows touched while searching, please update + // CMiniMdRW::GetHotMetadataTokensSearchAware + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + // End will be at least one larger than found record. + ridEnd = ridBegin + 1; + + // Search back to start of group. + for (;;) + { + void *pRow; + if (ridBegin <= 1) + { + break; + } + IfFailRet(static_cast<Impl*>(this)->vGetRow(ixTbl, ridBegin-1, &pRow)); + if (getIX(pRow, sColumn) != ulTarget) + { + break; + } + --ridBegin; + } + + // If desired, search forward to end of group. + if (pEnd != NULL) + { + for (;;) + { + void *pRow; + if (ridEnd > GetCountRecs(ixTbl)) + { + break; + } + IfFailRet(static_cast<Impl*>(this)->vGetRow(ixTbl, ridEnd, &pRow)); + if (getIX(pRow, sColumn) != ulTarget) + { + break; + } + ++ridEnd; + } + *pEnd = ridEnd; + } + + *pFoundRid = ridBegin; + return S_OK; + } // SearchTableForMultipleRows + + //***************************************************************************** + // Get name and sig of a methodDef + //***************************************************************************** + HRESULT CommonGetMethodDefProps( + mdMethodDef tkMethodDef, + LPCUTF8 *pszName, + DWORD *pdwFlags, + PCCOR_SIGNATURE *ppvSigBlob, + ULONG *pcbSigBlob + ) + { + + _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable."); + + HRESULT hr; + + LPCUTF8 szName; + DWORD dwFlags; + PCCOR_SIGNATURE pvSigBlob; + ULONG cbSigBlob; + + _ASSERTE(TypeFromToken(tkMethodDef) == mdtMethodDef); + MethodRec *pMethodRec; + IfFailRet(GetMethodRecord(RidFromToken(tkMethodDef), &pMethodRec)); + IfFailRet(getNameOfMethod(pMethodRec, &szName)); + dwFlags = getFlagsOfMethod(pMethodRec); + IfFailRet(getSignatureOfMethod(pMethodRec, &pvSigBlob, &cbSigBlob)); + + if (pszName) + *pszName = szName; + if (pdwFlags) + *pdwFlags = dwFlags; + if (ppvSigBlob) + *ppvSigBlob = pvSigBlob; + if (pcbSigBlob) + *pcbSigBlob = cbSigBlob; + + return S_OK; + } + + HRESULT CommonGetMemberRefProps( + mdMemberRef tkMemberRef, + mdToken *pParentToken + ) + { + _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable."); + + HRESULT hr; + + _ASSERTE(TypeFromToken(tkMemberRef) == mdtMemberRef); + + MemberRefRec *pMemberRefRec; + IfFailRet(GetMemberRefRecord(RidFromToken(tkMemberRef), &pMemberRefRec)); + if (pParentToken != NULL) + *pParentToken = getClassOfMemberRef(pMemberRefRec); + + return S_OK; + } + + + + + __checkReturn + HRESULT CommonGetTypeRefProps( + mdTypeRef tr, + LPCUTF8 *pszNamespace, + LPCUTF8 *pszName, + mdToken *ptkResolution) + { + HRESULT hr = S_OK; + TypeRefRec *pRec; + IfFailRet(GetTypeRefRecord(RidFromToken(tr), &pRec)); + if (pszNamespace != NULL) + { + IfFailRet(getNamespaceOfTypeRef(pRec, pszNamespace)); + } + if (pszName != NULL) + { + IfFailRet(getNameOfTypeRef(pRec, pszName)); + } + if (ptkResolution != NULL) + { + *ptkResolution = getResolutionScopeOfTypeRef(pRec); + } + return hr; + } + + __checkReturn + virtual HRESULT CommonGetTypeDefProps( + mdTypeDef td, + LPCUTF8 *pszNamespace, + LPCUTF8 *pszName, + DWORD *pdwFlags, + mdToken *pdwExtends, + ULONG *pMethodList) + { + HRESULT hr = S_OK; + TypeDefRec *pRec; + IfFailRet(GetTypeDefRecord(RidFromToken(td), &pRec)); + if (pszNamespace != NULL) + { + IfFailRet(getNamespaceOfTypeDef(pRec, pszNamespace)); + } + if (pszName != NULL) + { + IfFailRet(getNameOfTypeDef(pRec, pszName)); + } + if (pdwFlags != NULL) + { + *pdwFlags = getFlagsOfTypeDef(pRec); + } + if (pdwExtends != NULL) + { + *pdwExtends = getExtendsOfTypeDef(pRec); + } + if (pMethodList != NULL) + { + *pMethodList = getMethodListOfTypeDef(pRec); + } + return hr; + } + + __checkReturn + virtual HRESULT CommonGetTypeSpecProps( + mdTypeSpec ts, + PCCOR_SIGNATURE *ppvSig, + ULONG *pcbSig) + { + HRESULT hr = S_OK; + TypeSpecRec *pRec; + IfFailRet(GetTypeSpecRecord(RidFromToken(ts), &pRec)); + ULONG cb; + IfFailRet(getSignatureOfTypeSpec(pRec, ppvSig, &cb)); + *pcbSig = cb; + return hr; + } + + __checkReturn + virtual HRESULT CommonGetEnclosingClassOfTypeDef( + mdTypeDef td, + mdTypeDef *ptkEnclosingTypeDef) + { + _ASSERTE(ptkEnclosingTypeDef != NULL); + + HRESULT hr; + NestedClassRec *pRec; + RID iRec; + + IfFailRet(FindNestedClassFor(RidFromToken(td), &iRec)); + if (iRec == 0) + { + *ptkEnclosingTypeDef = mdTypeDefNil; + return S_OK; + } + + IfFailRet(GetNestedClassRecord(iRec, &pRec)); + *ptkEnclosingTypeDef = getEnclosingClassOfNestedClass(pRec); + return S_OK; + } + + + __checkReturn + virtual HRESULT CommonGetAssemblyProps( + USHORT *pusMajorVersion, + USHORT *pusMinorVersion, + USHORT *pusBuildNumber, + USHORT *pusRevisionNumber, + DWORD *pdwFlags, + const void **ppbPublicKey, + ULONG *pcbPublicKey, + LPCUTF8 *pszName, + LPCUTF8 *pszLocale) + { + HRESULT hr = S_OK; + AssemblyRec *pRec; + + IfFailRet(GetAssemblyRecord(1, &pRec)); + + if (pusMajorVersion) *pusMajorVersion = pRec->GetMajorVersion(); + if (pusMinorVersion) *pusMinorVersion = pRec->GetMinorVersion(); + if (pusBuildNumber) *pusBuildNumber = pRec->GetBuildNumber(); + if (pusRevisionNumber) *pusRevisionNumber = pRec->GetRevisionNumber(); + if (pdwFlags != NULL) + { + *pdwFlags = pRec->GetFlags(); + } + + // Turn on the afPublicKey if PublicKey blob is not empty + if (pdwFlags != NULL) + { + DWORD cbPublicKey; + const BYTE *pbPublicKey; + IfFailRet(getPublicKeyOfAssembly(pRec, &pbPublicKey, &cbPublicKey)); + if (cbPublicKey) + *pdwFlags |= afPublicKey; + } + if (ppbPublicKey != NULL) + { + IfFailRet(getPublicKeyOfAssembly(pRec, reinterpret_cast<const BYTE **>(ppbPublicKey), pcbPublicKey)); + } + if (pszName != NULL) + { + IfFailRet(getNameOfAssembly(pRec, pszName)); + } + if (pszLocale != NULL) + { + IfFailRet(getLocaleOfAssembly(pRec, pszLocale)); + } + return hr; + } + + __checkReturn + virtual HRESULT CommonGetAssemblyRefProps( + mdAssemblyRef tkAssemRef, + USHORT *pusMajorVersion, + USHORT *pusMinorVersion, + USHORT *pusBuildNumber, + USHORT *pusRevisionNumber, + DWORD *pdwFlags, + const void **ppbPublicKeyOrToken, + ULONG *pcbPublicKeyOrToken, + LPCUTF8 *pszName, + LPCUTF8 *pszLocale, + const void **ppbHashValue, + ULONG *pcbHashValue) + { + HRESULT hr = S_OK; + AssemblyRefRec *pRec; + + IfFailRet(GetAssemblyRefRecord(RidFromToken(tkAssemRef), &pRec)); + + if (pusMajorVersion) *pusMajorVersion = pRec->GetMajorVersion(); + if (pusMinorVersion) *pusMinorVersion = pRec->GetMinorVersion(); + if (pusBuildNumber) *pusBuildNumber = pRec->GetBuildNumber(); + if (pusRevisionNumber) *pusRevisionNumber = pRec->GetRevisionNumber(); + if (pdwFlags) *pdwFlags = pRec->GetFlags(); + if (ppbPublicKeyOrToken != NULL) + { + IfFailRet(getPublicKeyOrTokenOfAssemblyRef(pRec, reinterpret_cast<const BYTE **>(ppbPublicKeyOrToken), pcbPublicKeyOrToken)); + } + if (pszName != NULL) + { + IfFailRet(getNameOfAssemblyRef(pRec, pszName)); + } + if (pszLocale != NULL) + { + IfFailRet(getLocaleOfAssemblyRef(pRec, pszLocale)); + } + if (ppbHashValue != NULL) + { + IfFailRet(getHashValueOfAssemblyRef(pRec, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue)); + } + return hr; + } + + __checkReturn + virtual HRESULT CommonGetModuleRefProps( + mdModuleRef tkModuleRef, + LPCUTF8 *pszName) + { + HRESULT hr = S_OK; + ModuleRefRec *pRec; + + IfFailRet(GetModuleRefRecord(RidFromToken(tkModuleRef), &pRec)); + IfFailRet(getNameOfModuleRef(pRec, pszName)); + return hr; + } + + __checkReturn + HRESULT CommonFindExportedType( + LPCUTF8 szNamespace, + LPCUTF8 szName, + mdToken tkEnclosingType, + mdExportedType *ptkExportedType) + { + HRESULT hr; + ExportedTypeRec *pRec; + ULONG ulCount; + LPCUTF8 szTmp; + mdToken tkImpl; + + _ASSERTE(szName && ptkExportedType); + + // Set NULL namespace to empty string. + if (!szNamespace) + szNamespace = ""; + + // Set output to Nil. + *ptkExportedType = mdTokenNil; + + ulCount = getCountExportedTypes(); + while (ulCount) + { + IfFailRet(GetExportedTypeRecord(ulCount--, &pRec)); + + // Handle the case of nested vs. non-nested classes. + tkImpl = getImplementationOfExportedType(pRec); + if (TypeFromToken(tkImpl) == mdtExportedType && !IsNilToken(tkImpl)) + { + // Current ExportedType being looked at is a nested type, so + // comparing the implementation token. + if (tkImpl != tkEnclosingType) + continue; + } + else if (TypeFromToken(tkEnclosingType) == mdtExportedType && + !IsNilToken(tkEnclosingType)) + { + // ExportedType passed in is nested but the current ExportedType is not. + continue; + } + + // Compare name and namespace. + IfFailRet(getTypeNameOfExportedType(pRec, &szTmp)); + if (strcmp(szTmp, szName)) + continue; + IfFailRet(getTypeNamespaceOfExportedType(pRec, &szTmp)); + if (!strcmp(szTmp, szNamespace)) + { + *ptkExportedType = TokenFromRid(ulCount+1, mdtExportedType); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; + } + + __checkReturn + virtual HRESULT CommonGetExportedTypeProps( + mdToken tkExportedType, + LPCUTF8 *pszNamespace, + LPCUTF8 *pszName, + mdToken *ptkImpl) + { + HRESULT hr = S_OK; + ExportedTypeRec *pRec; + + IfFailRet(GetExportedTypeRecord(RidFromToken(tkExportedType), &pRec)); + + if (pszNamespace != NULL) + { + IfFailRet(getTypeNamespaceOfExportedType(pRec, pszNamespace)); + } + if (pszName != NULL) + { + IfFailRet(getTypeNameOfExportedType(pRec, pszName)); + } + if (ptkImpl) *ptkImpl = getImplementationOfExportedType(pRec); + return hr; + } + + int CommonIsRo() + { + return static_cast<Impl*>(this)->Impl_IsRo(); + } + + + + HRESULT CommonGetCustomAttributeProps( + mdCustomAttribute cv, // [IN] CustomAttribute token. + mdToken *ptkObj, // [OUT, OPTIONAL] Put object token here. + mdToken *ptkType, // [OUT, OPTIONAL] Put AttrType token here. + const void **ppBlob, // [OUT, OPTIONAL] Put pointer to data here. + ULONG *pcbSize) // [OUT, OPTIONAL] Put size of date here. + { + _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable."); + + HRESULT hr; + _ASSERTE(TypeFromToken(cv) == mdtCustomAttribute); + CustomAttributeRec *pRec; // A CustomAttribute record. + const void *pBlob; + ULONG cbSize; + IfFailRet(GetCustomAttributeRecord(RidFromToken(cv), &pRec)); + if (ptkObj) + *ptkObj = getParentOfCustomAttribute(pRec); + if (ptkType) + *ptkType = getTypeOfCustomAttribute(pRec); + + if (!ppBlob) + ppBlob = &pBlob; + if (!pcbSize) + pcbSize = &cbSize; + IfFailRet(getValueOfCustomAttribute(pRec, (const BYTE **)ppBlob, pcbSize)); + return S_OK; + } + + //***************************************************************************** + // Get name, parent and flags of a fieldDef + //***************************************************************************** + HRESULT CommonGetFieldDefProps( + mdFieldDef tkFieldDef, + mdTypeDef *ptkParent, + LPCUTF8 *pszName, + DWORD *pdwFlags + ) + { + _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable."); + _ASSERTE(TypeFromToken(tkFieldDef) == mdtFieldDef); + + HRESULT hr; + + FieldRec *pFieldRec; + IfFailRet(GetFieldRecord(RidFromToken(tkFieldDef), &pFieldRec)); + + if(ptkParent) + { + IfFailRet(FindParentOfField(RidFromToken(tkFieldDef), (RID *) ptkParent)); + RidToToken(*ptkParent, mdtTypeDef); + } + if (pszName) + IfFailRet(getNameOfField(pFieldRec, pszName)); + if (pdwFlags) + *pdwFlags = getFlagsOfField(pFieldRec); + + return S_OK; + } + + __checkReturn + HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd) + { + HRESULT hr; + IfFailRet(FindParentOfMethod(RidFromToken(md), (RID *)ptd)); + RidToToken(*ptd, mdtTypeDef); + return NOERROR; + } + + //***************************************************************************** + // Helper function to lookup and retrieve a CustomAttribute. + //***************************************************************************** + __checkReturn + HRESULT CompareCustomAttribute( + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + ULONG rid) // [IN] the rid of the custom attribute to compare to + { + CustomAttributeRec *pRec; // A CustomAttribute record. + LPCUTF8 szNamespaceTmp = NULL; // Namespace of a CustomAttribute's type. + LPCUTF8 szNameTmp = NULL; // Name of a CustomAttribute's type. + int iLen; // Length of a component name. + HRESULT hr = S_FALSE; + HRESULT hrMatch = S_FALSE; + + if (!_IsValidTokenBase(tkObj)) + return COR_E_BADIMAGEFORMAT; + + // Get the row. + IfFailGo(GetCustomAttributeRecord(rid, &pRec)); + + // Check the parent. In debug, always check. In retail, only when scanning. + mdToken tkParent; + tkParent = getParentOfCustomAttribute(pRec); + if (tkObj != tkParent) + { + goto ErrExit; + } + + hr = CommonGetNameOfCustomAttribute(rid, &szNamespaceTmp, &szNameTmp); + if (hr != S_OK) + goto ErrExit; + + iLen = -1; + if (*szNamespaceTmp) + { + iLen = (int)strlen(szNamespaceTmp); + if (strncmp(szName, szNamespaceTmp, iLen) != 0) + goto ErrExit; + // Namespace separator after the Namespace? + if (szName[iLen] != NAMESPACE_SEPARATOR_CHAR) + goto ErrExit; + } + // Check the type name after the separator. + if (strcmp(szName+iLen+1, szNameTmp) != 0) + goto ErrExit; + + hrMatch = S_OK; + ErrExit: + + if (FAILED(hr)) + return hr; + + return hrMatch; + } // CompareCustomAttribute + + + //***************************************************************************** + // Helper function to lookup the name of a custom attribute + // Note that this function can return S_FALSE to support being called + // by CompareCustomAttribute. See GetTypeDefRefTokenInTypeSpec for + // details on when this will happen. + //***************************************************************************** + __checkReturn + HRESULT CommonGetNameOfCustomAttribute( + ULONG rid, // [IN] the rid of the custom attribute + LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute. + LPCUTF8 *pszName) // [OUT] Name of Custom Attribute. + { + CustomAttributeRec *pRec; // A CustomAttribute record. + mdToken tkTypeTmp; // Type of some CustomAttribute. + RID ridTmp; // Rid of some custom value. + HRESULT hr = S_FALSE; + + // Get the row. + IfFailGo(GetCustomAttributeRecord(rid, &pRec)); + + // Get the type. + tkTypeTmp = getTypeOfCustomAttribute(pRec); + + // If the record is a MemberRef or a MethodDef, we will come back here to check + // the type of the parent. + CheckParentType: + + if (!_IsValidTokenBase(tkTypeTmp)) + return COR_E_BADIMAGEFORMAT; + + ridTmp = RidFromToken(tkTypeTmp); + + // Get the name of the type. + switch (TypeFromToken(tkTypeTmp)) + { + case mdtTypeRef: + { + TypeRefRec *pTR; + IfFailGo(GetTypeRefRecord(ridTmp, &pTR)); + IfFailGo(getNamespaceOfTypeRef(pTR, pszNamespace)); + IfFailGo(getNameOfTypeRef(pTR, pszName)); + } + break; + case mdtTypeDef: + { + TypeDefRec *pTD; + IfFailGo(GetTypeDefRecord(ridTmp, &pTD)); + IfFailGo(getNamespaceOfTypeDef(pTD, pszNamespace)); + IfFailGo(getNameOfTypeDef(pTD, pszName)); + } + break; + case mdtTypeSpec: + { + // If this has an encoded token, we'll take a look. If it contains + // a base type, we'll just return a non-match. + + hr = GetTypeDefRefTokenInTypeSpec(tkTypeTmp, &tkTypeTmp); + IfFailGo(hr); + + if (hr == S_OK) + // Ok, tkTypeTmp should be the type token now. + goto CheckParentType; + + // This doesn't have a coded typedef or typeref. + goto ErrExit; + } + case mdtMethodDef: + { + // Follow the parent. + IfFailGo( FindParentOfMethodHelper(tkTypeTmp, &tkTypeTmp)); + goto CheckParentType; + } + break; + case mdtMemberRef: + { + MemberRefRec *pMember; + IfFailGo(GetMemberRefRecord(ridTmp, &pMember)); + // Follow the parent. + tkTypeTmp = getClassOfMemberRef(pMember); + goto CheckParentType; + } + break; + case mdtString: + default: + if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0)) + _ASSERTE(!"Unexpected token type in FindCustomAttributeByName"); + hr = COR_E_BADIMAGEFORMAT; + goto ErrExit; + } // switch (TypeFromToken(tkTypeTmp)) + + hr = S_OK; + ErrExit: + + return hr; + } // CommonGetNameOfCustomAttribute + + __checkReturn + HRESULT GetTypeDefRefTokenInTypeSpec(mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look + mdToken *tkEnclosedToken) // [OUT] The enclosed type token + { + _ASSERTE(TypeFromToken(tkTypeSpec) == mdtTypeSpec); + if (TypeFromToken(tkTypeSpec) != mdtTypeSpec || !_IsValidTokenBase(tkTypeSpec)) + return COR_E_BADIMAGEFORMAT; + + HRESULT hr; + TypeSpecRec *pTS; + IfFailRet(GetTypeSpecRecord(RidFromToken(tkTypeSpec), &pTS)); + ULONG cbSig = 0; + PCCOR_SIGNATURE pSig; + PCCOR_SIGNATURE pEnd; + ULONG data = 0; + + IfFailRet(getSignatureOfTypeSpec(pTS, &pSig, &cbSig)); + pEnd = pSig + cbSig; + + if (cbSig == 0) + return COR_E_BADIMAGEFORMAT; + + pSig += CorSigUncompressData(pSig, &data); + + while (pSig < pEnd && CorIsModifierElementType((CorElementType) data)) + { + pSig += CorSigUncompressData(pSig, &data); + } + + // See if the signature was bad + if (pSig >= pEnd) + return COR_E_BADIMAGEFORMAT; + + // pSig should point to the element type now. + if (data == ELEMENT_TYPE_VALUETYPE || data == ELEMENT_TYPE_CLASS) + { + // Get the new type token + if (CorSigUncompressToken(pSig, tkEnclosedToken) == 0) + return COR_E_BADIMAGEFORMAT; + + // Ok, tkEnclosedToken should be the type token now + return S_OK; + } + + // The enclosed type is a base type or an array. We don't have a token to hand out + *tkEnclosedToken = mdTokenNil; + + return S_FALSE; + } + + + + //***************************************************************************** + // Given a scope, return the number of tokens in a given table + //***************************************************************************** + ULONG CommonGetRowCount( // return hresult + DWORD tkKind) // [IN] pass in the kind of token. + { + _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable."); + + ULONG ulCount = 0; + + switch (tkKind) + { + case mdtTypeDef: + ulCount = getCountTypeDefs(); + break; + case mdtTypeRef: + ulCount = getCountTypeRefs(); + break; + case mdtMethodDef: + ulCount = getCountMethods(); + break; + case mdtFieldDef: + ulCount = getCountFields(); + break; + case mdtMemberRef: + ulCount = getCountMemberRefs(); + break; + case mdtInterfaceImpl: + ulCount = getCountInterfaceImpls(); + break; + case mdtParamDef: + ulCount = getCountParams(); + break; + case mdtFile: + ulCount = getCountFiles(); + break; + case mdtAssemblyRef: + ulCount = getCountAssemblyRefs(); + break; + case mdtAssembly: + ulCount = getCountAssemblys(); + break; + case mdtCustomAttribute: + ulCount = getCountCustomAttributes(); + break; + case mdtModule: + ulCount = getCountModules(); + break; + case mdtPermission: + ulCount = getCountDeclSecuritys(); + break; + case mdtSignature: + ulCount = getCountStandAloneSigs(); + break; + case mdtEvent: + ulCount = getCountEvents(); + break; + case mdtProperty: + ulCount = getCountPropertys(); + break; + case mdtModuleRef: + ulCount = getCountModuleRefs(); + break; + case mdtTypeSpec: + ulCount = getCountTypeSpecs(); + break; + case mdtExportedType: + ulCount = getCountExportedTypes(); + break; + case mdtManifestResource: + ulCount = getCountManifestResources(); + break; + case mdtGenericParam: + ulCount = getCountGenericParams(); + break; + case mdtMethodSpec: + ulCount = getCountMethodSpecs(); + break; + default: + Debug_ReportError("Invalid token kind (table)"); + ulCount = 0; + break; + } + return ulCount; + } // ULONG CommonGetRowCount() + + + + //***************************************************************************** + // Locate methodimpl token range for a given typeDef + //***************************************************************************** + HRESULT CommonGetMethodImpls( + mdTypeDef tkTypeDef, // [IN] typeDef to scope search + mdToken *ptkMethodImplFirst, // [OUT] returns first methodImpl token + ULONG *pMethodImplCount // [OUT] returns # of methodImpl tokens scoped to type + ) + { + _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable."); + + _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef); + _ASSERTE(ptkMethodImplFirst != NULL); + _ASSERTE(pMethodImplCount != NULL); + + HRESULT hr; + + RID ridEnd; + RID ridStart; + IfFailGo(getMethodImplsForClass(RidFromToken(tkTypeDef), &ridEnd, &ridStart)); + *pMethodImplCount = ridEnd - ridStart; + if (*pMethodImplCount) + { + *ptkMethodImplFirst = TokenFromRid(TBL_MethodImpl << 24, ridStart); + } + hr = S_OK; + + ErrExit: + return hr; + } + + //***************************************************************************** + // Extract row info for methodImpl + //***************************************************************************** + HRESULT CommonGetMethodImplProps( + mdToken tkMethodImpl, // [IN] methodImpl + mdToken *pBody, // [OUT] returns body token + mdToken *pDecl // [OUT] returns decl token + ) + { + _ASSERTE(!IsWritable() && "IMetaModelCommonRO methods cannot be used because this importer is writable."); + + HRESULT hr; + _ASSERTE(TypeFromToken(tkMethodImpl) == (TBL_MethodImpl << 24)); + _ASSERTE(pBody != NULL); + _ASSERTE(pDecl != NULL); + MethodImplRec *pRec; + IfFailGo(GetMethodImplRecord(RidFromToken(tkMethodImpl), &pRec)); + *pBody = getMethodBodyOfMethodImpl(pRec); + *pDecl = getMethodDeclarationOfMethodImpl(pRec); + hr = S_OK; + ErrExit: + return hr; + } + + // IMetaModelCommonRO interface end + + + + +public: +// friend class CLiteWeightStgdb; + + __checkReturn + virtual HRESULT vGetRow(UINT32 nTableIndex, UINT32 nRowIndex, void **ppRow) + { + return getRow(nTableIndex, nRowIndex, ppRow); + } + +public: + + //************************************************************************* + // This group of functions are table-level (one function per table). Functions like + // getting a count of rows. + + // Functions to get the count of rows in a table. Functions like: + // ULONG getCountXyzs() { return m_Schema.m_cRecs[TBL_Xyz];} +#undef MiniMdTable +#define MiniMdTable(tbl) ULONG getCount##tbl##s() { return _TBLCNT(tbl); } + MiniMdTables(); +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + PortablePdbMiniMdTables(); +#endif + // macro misspells some names. + ULONG getCountProperties() {return getCountPropertys();} + ULONG getCountMethodSemantics() {return getCountMethodSemanticss();} + + // Functions for getting a row by rid. Look like: + // HRESULT GetXyzRecord(RID rid, XyzRec **ppRecord) { return m_Tables[TBL_Xyz].GetRecord(rid, ppRecord); } + // e.g.: + // HRESULT GetMethodRecord(RID rid, MethodRec **ppRecord) { return m_Tables[TBL_Method].GetRecord(rid, ppRecord); } + #undef MiniMdTable +#define MiniMdTable(tbl) __checkReturn HRESULT Get##tbl##Record(RID rid, tbl##Rec **ppRecord) { \ + return getRow(TBL_##tbl, rid, reinterpret_cast<void **>(ppRecord)); } + MiniMdTables(); +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + PortablePdbMiniMdTables(); +#endif + + //************************************************************************* + // These are specialized searching functions. Mostly generic (ie, find + // a custom value for any object). + + // Functions to search for a record relating to another record. + // Return RID to Constant table. + __checkReturn + HRESULT FindConstantFor(RID rid, mdToken typ, RID *pFoundRid) + { return doSearchTable(TBL_Constant, _COLPAIR(Constant,Parent), encodeToken(rid,typ,mdtHasConstant,lengthof(mdtHasConstant)), pFoundRid); } + + // Return RID to FieldMarshal table. + __checkReturn + HRESULT FindFieldMarshalFor(RID rid, mdToken typ, RID *pFoundRid) + { return doSearchTable(TBL_FieldMarshal, _COLPAIR(FieldMarshal,Parent), encodeToken(rid,typ,mdtHasFieldMarshal,lengthof(mdtHasFieldMarshal)), pFoundRid); } + + // Return RID to ClassLayout table, given the rid to a TypeDef. + __checkReturn + HRESULT FindClassLayoutFor(RID rid, RID *pFoundRid) + { return doSearchTable(TBL_ClassLayout, _COLPAIR(ClassLayout,Parent), RidFromToken(rid), pFoundRid); } + + // given a rid to the Event table, find an entry in EventMap table that contains the back pointer + // to its typedef parent + __checkReturn + HRESULT FindEventMapParentOfEvent(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_EventMap, _COLDEF(EventMap,EventList), rid, pFoundRid); + } + // return the parent eventmap rid given a event rid + __checkReturn + HRESULT FindParentOfEvent(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_EventMap, _COLDEF(EventMap,EventList), rid, pFoundRid); + } + + // given a rid to the Event table, find an entry in EventMap table that contains the back pointer + // to its typedef parent + __checkReturn + HRESULT FindPropertyMapParentOfProperty(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_PropertyMap, _COLDEF(PropertyMap,PropertyList), rid, pFoundRid); + } + // return the parent propertymap rid given a property rid + __checkReturn + HRESULT FindParentOfProperty(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_PropertyMap, _COLDEF(PropertyMap,PropertyList), rid, pFoundRid); + } + + // Return RID to MethodSemantics table, given the rid to a MethodDef. + __checkReturn + HRESULT FindMethodSemanticsFor(RID rid, RID *pFoundRid) + { return doSearchTable(TBL_MethodSemantics, _COLPAIR(MethodSemantics,Method), RidFromToken(rid), pFoundRid); } + + // return the parent typedef rid given a field def rid + __checkReturn + HRESULT FindParentOfField(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_TypeDef, _COLDEF(TypeDef,FieldList), rid, pFoundRid); + } + + // return the parent typedef rid given a method def rid + __checkReturn + HRESULT FindParentOfMethod(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_TypeDef, _COLDEF(TypeDef,MethodList), rid, pFoundRid); + } + + __checkReturn + HRESULT FindParentOfParam(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_Method, _COLDEF(Method,ParamList), rid, pFoundRid); + } + + // Find a FieldLayout record given the corresponding Field. + __checkReturn + HRESULT FindFieldLayoutFor(RID rid, RID *pFoundRid) + { return doSearchTable(TBL_FieldLayout, _COLPAIR(FieldLayout, Field), rid, pFoundRid); } + + // Return RID to Constant table. + __checkReturn + HRESULT FindImplMapFor(RID rid, mdToken typ, RID *pFoundRid) + { return doSearchTable(TBL_ImplMap, _COLPAIR(ImplMap,MemberForwarded), encodeToken(rid,typ,mdtMemberForwarded,lengthof(mdtMemberForwarded)), pFoundRid); } + + // Return RID to FieldRVA table. + __checkReturn + HRESULT FindFieldRVAFor(RID rid, RID *pFoundRid) + { return doSearchTable(TBL_FieldRVA, _COLPAIR(FieldRVA, Field), rid, pFoundRid); } + + // Find a NestedClass record given the corresponding Field. + __checkReturn + HRESULT FindNestedClassFor(RID rid, RID *pFoundRid) + { return doSearchTable(TBL_NestedClass, _COLPAIR(NestedClass, NestedClass), rid, pFoundRid); } + + //************************************************************************* + // These are table-specific functions. + + // ModuleRec + _GETSTR(Module,Name); + __checkReturn HRESULT _GETGUID(Module,Mvid); + __checkReturn HRESULT _GETGUID(Module,EncId); + __checkReturn HRESULT _GETGUID(Module,EncBaseId); + + // TypeRefRec + mdToken _GETCDTKN(TypeRef, ResolutionScope, mdtResolutionScope); + _GETSTR(TypeRef, Name); + _GETSTR(TypeRef, Namespace); + + // TypeDefRec + ULONG _GETFLD(TypeDef,Flags); // USHORT getFlagsOfTypeDef(TypeDefRec *pRec); + _GETSTR(TypeDef,Name); + _GETSTR(TypeDef,Namespace); + + _GETLIST(TypeDef,FieldList,Field); // RID getFieldListOfTypeDef(TypeDefRec *pRec); + _GETLIST(TypeDef,MethodList,Method); // RID getMethodListOfTypeDef(TypeDefRec *pRec); + mdToken _GETCDTKN(TypeDef,Extends,mdtTypeDefOrRef); // mdToken getExtendsOfTypeDef(TypeDefRec *pRec); + + __checkReturn + HRESULT getGenericParamsForTypeDef(RID rid, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_GenericParam, + _COLDEF(GenericParam,Owner), + encodeToken(rid, mdtTypeDef, mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)), + pEnd, + pFoundRid); + } + __checkReturn + HRESULT getGenericParamsForMethodDef(RID rid, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_GenericParam, + _COLDEF(GenericParam,Owner), + encodeToken(rid, mdtMethodDef, mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)), + pEnd, + pFoundRid); + } + __checkReturn + HRESULT getMethodSpecsForMethodDef(RID rid, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_MethodSpec, + _COLDEF(MethodSpec,Method), + encodeToken(rid, mdtMethodDef, mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)), + pEnd, + pFoundRid); + } + __checkReturn + HRESULT getMethodSpecsForMemberRef(RID rid, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_MethodSpec, + _COLDEF(MethodSpec,Method), + encodeToken(rid, mdtMemberRef, mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)), + pEnd, + pFoundRid); + } + __checkReturn + HRESULT getInterfaceImplsForTypeDef(RID rid, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_InterfaceImpl, + _COLDEF(InterfaceImpl,Class), + rid, + pEnd, + pFoundRid); + } + + // FieldPtr + ULONG _GETRID(FieldPtr,Field); + + // FieldRec + USHORT _GETFLD(Field,Flags); // USHORT getFlagsOfField(FieldRec *pRec); + _GETSTR(Field,Name); // HRESULT getNameOfField(FieldRec *pRec, LPCUTF8 *pszString); + _GETSIGBLOB(Field,Signature); // HRESULT getSignatureOfField(FieldRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize); + + // MethodPtr + ULONG _GETRID(MethodPtr,Method); + + // MethodRec + ULONG _GETFLD(Method,RVA); + USHORT _GETFLD(Method,ImplFlags); + USHORT _GETFLD(Method,Flags); + _GETSTR(Method,Name); // HRESULT getNameOfMethod(MethodRec *pRec, LPCUTF8 *pszString); + _GETSIGBLOB(Method,Signature); // HRESULT getSignatureOfMethod(MethodRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize); + _GETLIST(Method,ParamList,Param); + + // ParamPtr + ULONG _GETRID(ParamPtr,Param); + + // ParamRec + USHORT _GETFLD(Param,Flags); + USHORT _GETFLD(Param,Sequence); + _GETSTR(Param,Name); + + // InterfaceImplRec + mdToken _GETTKN(InterfaceImpl,Class,mdtTypeDef); + mdToken _GETCDTKN(InterfaceImpl,Interface,mdtTypeDefOrRef); + + // MemberRefRec + mdToken _GETCDTKN(MemberRef,Class,mdtMemberRefParent); + _GETSTR(MemberRef,Name); + _GETSIGBLOB(MemberRef,Signature); // HRESULT getSignatureOfMemberRef(MemberRefRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize); + + // ConstantRec + BYTE _GETFLD(Constant,Type); + mdToken _GETCDTKN(Constant,Parent,mdtHasConstant); + _GETBLOB(Constant,Value); + + // CustomAttributeRec + __checkReturn + HRESULT getCustomAttributeForToken(mdToken tk, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_CustomAttribute, + _COLDEF(CustomAttribute,Parent), + encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, lengthof(mdtHasCustomAttribute)), + pEnd, + pFoundRid); + } + + mdToken _GETCDTKN(CustomAttribute,Parent,mdtHasCustomAttribute); + mdToken _GETCDTKN(CustomAttribute,Type,mdtCustomAttributeType); + _GETBLOB(CustomAttribute,Value); + + // FieldMarshalRec + mdToken _GETCDTKN(FieldMarshal,Parent,mdtHasFieldMarshal); + _GETSIGBLOB(FieldMarshal,NativeType); + + // DeclSecurityRec + __checkReturn + HRESULT getDeclSecurityForToken(mdToken tk, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_DeclSecurity, + _COLDEF(DeclSecurity,Parent), + encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, lengthof(mdtHasDeclSecurity)), + pEnd, + pFoundRid); + } + + short _GETFLD(DeclSecurity,Action); + mdToken _GETCDTKN(DeclSecurity,Parent,mdtHasDeclSecurity); + _GETBLOB(DeclSecurity,PermissionSet); + + // ClassLayoutRec + USHORT _GETFLD(ClassLayout,PackingSize); + ULONG _GETFLD(ClassLayout,ClassSize); + ULONG _GETTKN(ClassLayout,Parent, mdtTypeDef); + + // FieldLayout + ULONG _GETFLD(FieldLayout,OffSet); + ULONG _GETTKN(FieldLayout, Field, mdtFieldDef); + + // Event map. + _GETLIST(EventMap,EventList,Event); + ULONG _GETRID(EventMap, Parent); + + // EventPtr + ULONG _GETRID(EventPtr, Event); + + // Event. + USHORT _GETFLD(Event,EventFlags); + _GETSTR(Event,Name); + mdToken _GETCDTKN(Event,EventType,mdtTypeDefOrRef); + + // Property map. + _GETLIST(PropertyMap,PropertyList,Property); + ULONG _GETRID(PropertyMap, Parent); + + // PropertyPtr + ULONG _GETRID(PropertyPtr, Property); + + // Property. + USHORT _GETFLD(Property,PropFlags); + _GETSTR(Property,Name); + _GETSIGBLOB(Property,Type); + + // MethodSemantics. + // Given an event or a property token, return the beginning/ending + // associates. + // + __checkReturn + HRESULT getAssociatesForToken(mdToken tk, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_MethodSemantics, + _COLDEF(MethodSemantics,Association), + encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasSemantic, lengthof(mdtHasSemantic)), + pEnd, + pFoundRid); + } + + USHORT _GETFLD(MethodSemantics,Semantic); + mdToken _GETTKN(MethodSemantics,Method,mdtMethodDef); + mdToken _GETCDTKN(MethodSemantics,Association,mdtHasSemantic); + + // MethodImpl + // Given a class token, return the beginning/ending MethodImpls. + // + __checkReturn + HRESULT getMethodImplsForClass(RID rid, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_MethodImpl, + _COLDEF(MethodImpl, Class), + rid, + pEnd, + pFoundRid); + } + + mdToken _GETTKN(MethodImpl,Class,mdtTypeDef); + mdToken _GETCDTKN(MethodImpl,MethodBody, mdtMethodDefOrRef); + mdToken _GETCDTKN(MethodImpl, MethodDeclaration, mdtMethodDefOrRef); + + // StandAloneSigRec + _GETSIGBLOB(StandAloneSig,Signature); // HRESULT getSignatureOfStandAloneSig(StandAloneSigRec *pRec, PCCOR_SIGNATURE *ppbData, ULONG *pcbSize); + + // TypeSpecRec + // const BYTE* getSignatureOfTypeSpec(TypeSpecRec *pRec, ULONG *pcb); + _GETSIGBLOB(TypeSpec,Signature); + + // ModuleRef + _GETSTR(ModuleRef,Name); + + // ENCLog + ULONG _GETFLD(ENCLog, FuncCode); // ULONG getFuncCodeOfENCLog(ENCLogRec *pRec); + + // ImplMap + USHORT _GETFLD(ImplMap, MappingFlags); // USHORT getMappingFlagsOfImplMap(ImplMapRec *pRec); + mdToken _GETCDTKN(ImplMap, MemberForwarded, mdtMemberForwarded); // mdToken getMemberForwardedOfImplMap(ImplMapRec *pRec); + _GETSTR(ImplMap, ImportName); // HRESULT getImportNameOfImplMap(ImplMapRec *pRec, LPCUTF8 *pszString); + mdToken _GETTKN(ImplMap, ImportScope, mdtModuleRef); // mdToken getImportScopeOfImplMap(ImplMapRec *pRec); + + // FieldRVA + ULONG _GETFLD(FieldRVA, RVA); // ULONG getRVAOfFieldRVA(FieldRVARec *pRec); + mdToken _GETTKN(FieldRVA, Field, mdtFieldDef); // mdToken getFieldOfFieldRVA(FieldRVARec *pRec); + + // Assembly + ULONG _GETFLD(Assembly, HashAlgId); + USHORT _GETFLD(Assembly, MajorVersion); + USHORT _GETFLD(Assembly, MinorVersion); + USHORT _GETFLD(Assembly, BuildNumber); + USHORT _GETFLD(Assembly, RevisionNumber); + ULONG _GETFLD(Assembly, Flags); + _GETBLOB(Assembly, PublicKey); + _GETSTR(Assembly, Name); + _GETSTR(Assembly, Locale); + + // AssemblyRef + USHORT _GETFLD(AssemblyRef, MajorVersion); + USHORT _GETFLD(AssemblyRef, MinorVersion); + USHORT _GETFLD(AssemblyRef, BuildNumber); + USHORT _GETFLD(AssemblyRef, RevisionNumber); + ULONG _GETFLD(AssemblyRef, Flags); + _GETBLOB(AssemblyRef, PublicKeyOrToken); + _GETSTR(AssemblyRef, Name); + _GETSTR(AssemblyRef, Locale); + _GETBLOB(AssemblyRef, HashValue); + + // File + ULONG _GETFLD(File, Flags); + _GETSTR(File, Name); + _GETBLOB(File, HashValue); + + // ExportedType + ULONG _GETFLD(ExportedType, Flags); + ULONG _GETFLD(ExportedType, TypeDefId); + _GETSTR(ExportedType, TypeName); + _GETSTR(ExportedType, TypeNamespace); + mdToken _GETCDTKN(ExportedType, Implementation, mdtImplementation); + + // ManifestResource + ULONG _GETFLD(ManifestResource, Offset); + ULONG _GETFLD(ManifestResource, Flags); + _GETSTR(ManifestResource, Name); + mdToken _GETCDTKN(ManifestResource, Implementation, mdtImplementation); + + // NestedClass + mdToken _GETTKN(NestedClass, NestedClass, mdtTypeDef); + mdToken _GETTKN(NestedClass, EnclosingClass, mdtTypeDef); + + int GetSizeOfMethodNameColumn() + { + return _COLDEF(Method,Name).m_cbColumn; + } + + // GenericParRec + USHORT _GETFLD(GenericParam,Number); + USHORT _GETFLD(GenericParam,Flags); + mdToken _GETCDTKN(GenericParam,Owner,mdtTypeOrMethodDef); + _GETSTR(GenericParam,Name); + + __checkReturn + HRESULT getGenericParamConstraintsForGenericParam(RID rid, RID *pEnd, RID *pFoundRid) + { + return SearchTableForMultipleRows(TBL_GenericParamConstraint, + _COLDEF(GenericParamConstraint,Owner), + rid, + pEnd, + pFoundRid); + } + + // MethodSpecRec + mdToken _GETCDTKN(MethodSpec,Method,mdtMethodDefOrRef); + _GETSIGBLOB(MethodSpec,Instantiation); + + //GenericParamConstraintRec + mdToken _GETTKN(GenericParamConstraint,Owner,mdtGenericParam); + mdToken _GETCDTKN(GenericParamConstraint,Constraint,mdtTypeDefOrRef); + + BOOL SupportsGenerics() + { + // Only 2.0 of the metadata (and 1.1) support generics + return (m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0 || + (m_Schema.m_major == METAMODEL_MAJOR_VER_B1 && m_Schema.m_minor == METAMODEL_MINOR_VER_B1)); + }// SupportGenerics + + protected: + //***************************************************************************** + // Helper: determine if a token is valid or not + //***************************************************************************** + BOOL _IsValidTokenBase( + mdToken tk) + { + BOOL bRet = FALSE; + RID rid = RidFromToken(tk); + + if (rid != 0) + { + switch (TypeFromToken(tk)) + { + case mdtModule: + // can have only one module record + bRet = (rid <= getCountModules()); + break; + case mdtTypeRef: + bRet = (rid <= getCountTypeRefs()); + break; + case mdtTypeDef: + bRet = (rid <= getCountTypeDefs()); + break; + case mdtFieldDef: + bRet = (rid <= getCountFields()); + break; + case mdtMethodDef: + bRet = (rid <= getCountMethods()); + break; + case mdtParamDef: + bRet = (rid <= getCountParams()); + break; + case mdtInterfaceImpl: + bRet = (rid <= getCountInterfaceImpls()); + break; + case mdtMemberRef: + bRet = (rid <= getCountMemberRefs()); + break; + case mdtCustomAttribute: + bRet = (rid <= getCountCustomAttributes()); + break; + case mdtPermission: + bRet = (rid <= getCountDeclSecuritys()); + break; + case mdtSignature: + bRet = (rid <= getCountStandAloneSigs()); + break; + case mdtEvent: + bRet = (rid <= getCountEvents()); + break; + case mdtProperty: + bRet = (rid <= getCountPropertys()); + break; + case mdtModuleRef: + bRet = (rid <= getCountModuleRefs()); + break; + case mdtTypeSpec: + bRet = (rid <= getCountTypeSpecs()); + break; + case mdtAssembly: + bRet = (rid <= getCountAssemblys()); + break; + case mdtAssemblyRef: + bRet = (rid <= getCountAssemblyRefs()); + break; + case mdtFile: + bRet = (rid <= getCountFiles()); + break; + case mdtExportedType: + bRet = (rid <= getCountExportedTypes()); + break; + case mdtManifestResource: + bRet = (rid <= getCountManifestResources()); + break; + case mdtGenericParam: + bRet = (rid <= getCountGenericParams()); + break; + case mdtGenericParamConstraint: + bRet = (rid <= getCountGenericParamConstraints()); + break; + case mdtMethodSpec: + bRet = (rid <= getCountMethodSpecs()); + break; + default: + _ASSERTE(!bRet); + Debug_ReportError("Unknown token kind!"); + } + } + return bRet; + } // _IsValidToken + +#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN + bool IsSafeToDelete() + { + return m_isSafeToDelete; + } + + FORCEINLINE void MarkUnsafeToDelete() { m_isSafeToDelete = false; } + + bool m_isSafeToDelete; // This starts out true, but gets set to FALSE if we detect + // a MiniMd API call that might have given out an internal pointer. +#endif + +}; //class CMiniMdTemplate<Impl> + + +//----------------------------------------------------------------------------------------------------- +// A common interface unifying RegMeta and MDInternalRO, giving the adapter a common interface to +// access the raw metadata. +//----------------------------------------------------------------------------------------------------- + +// {4F8EE8A3-24F8-4241-BC75-C8CAEC0255B5} +EXTERN_GUID(IID_IMDCommon, 0x4f8ee8a3, 0x24f8, 0x4241, 0xbc, 0x75, 0xc8, 0xca, 0xec, 0x2, 0x55, 0xb5); + +#undef INTERFACE +#define INTERFACE IID_IMDCommon +DECLARE_INTERFACE_(IMDCommon, IUnknown) +{ + STDMETHOD_(IMetaModelCommon*, GetMetaModelCommon)() PURE; + STDMETHOD_(IMetaModelCommonRO*, GetMetaModelCommonRO)() PURE; + STDMETHOD(GetVersionString)(LPCSTR *pszVersionString) PURE; +}; + + +#undef SETP +#undef _GETCDTKN +#undef _GETTKN +#undef _GETRID +#undef _GETBLOB +#undef _GETGUID +#undef _GETSTR +#undef SCHEMA + +#endif // _METAMODEL_H_ +// eof ------------------------------------------------------------------------ diff --git a/src/coreclr/md/inc/metamodelro.h b/src/coreclr/md/inc/metamodelro.h new file mode 100644 index 00000000000..1f7b170d218 --- /dev/null +++ b/src/coreclr/md/inc/metamodelro.h @@ -0,0 +1,240 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaModelRO.h -- header file for Read-Only compressed COM+ metadata. +// + +// +// Used by the EE. +// +//***************************************************************************** +#ifndef _METAMODELRO_H_ +#define _METAMODELRO_H_ + +#if _MSC_VER >= 1100 + # pragma once +#endif + +#include "metamodel.h" + +#include "../heaps/export.h" +#include "../tables/export.h" + +//***************************************************************************** +// A read-only MiniMd. This is the fastest and smallest possible MiniMd, +// and as such, is the preferred EE metadata provider. +//***************************************************************************** + +template <class MiniMd> class CLiteWeightStgdb; +class CMiniMdRW; +class MDInternalRO; +class CMiniMd final: public CMiniMdTemplate<CMiniMd> +{ +public: + friend class CLiteWeightStgdb<CMiniMd>; + friend class CMiniMdTemplate<CMiniMd>; + friend class CMiniMdRW; + friend class MDInternalRO; + + __checkReturn + HRESULT InitOnMem(void *pBuf, ULONG ulBufLen); + __checkReturn + HRESULT PostInit(int iLevel); // higher number : more checking + + // Returns TRUE if token (tk) is valid. + // For user strings, consideres 0 as valid token. + BOOL _IsValidToken( + mdToken tk) // [IN] token to be checked + { + if (TypeFromToken(tk) == mdtString) + { + return m_UserStringHeap.IsValidIndex(RidFromToken(tk)); + } + // Base type doesn't know about user string blob (yet) + return _IsValidTokenBase(tk); + } // CMiniMdRO::_IsValidToken + + __checkReturn + FORCEINLINE HRESULT GetUserString(ULONG nIndex, MetaData::DataBlob *pData) + { + MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED(); + return m_UserStringHeap.GetBlob(nIndex, pData); + } + +#ifdef FEATURE_PREJIT + void DisableHotDataUsage() + { + MetaData::HotHeap emptyHotHeap; + // Initialize hot data again with empty heap to disable their usage + m_StringHeap.InitializeHotData(emptyHotHeap); + m_BlobHeap.InitializeHotData(emptyHotHeap); + m_UserStringHeap.InitializeHotData(emptyHotHeap); + m_GuidHeap.InitializeHotData(emptyHotHeap); + // Disable usage of hot table data (throw it away) + m_pHotTablesDirectory = NULL; + } +#endif //FEATURE_PREJIT + +protected: + DAC_ALIGNAS(CMiniMdTemplate<CMiniMd>) // Align the first member to the alignment of the base class + // Table info. + MetaData::TableRO m_Tables[TBL_COUNT]; +#ifdef FEATURE_PREJIT + struct MetaData::HotTablesDirectory * m_pHotTablesDirectory; +#endif //FEATURE_PREJIT + + __checkReturn + HRESULT InitializeTables(MetaData::DataBlob tablesData); + + __checkReturn + virtual HRESULT vSearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid); + __checkReturn + virtual HRESULT vSearchTableNotGreater(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid); + + // Heaps + MetaData::StringHeapRO m_StringHeap; + MetaData::BlobHeapRO m_BlobHeap; + MetaData::BlobHeapRO m_UserStringHeap; + MetaData::GuidHeapRO m_GuidHeap; + +protected: + + //************************************************************************* + // Overridables -- must be provided in derived classes. + __checkReturn + FORCEINLINE HRESULT Impl_GetString(UINT32 nIndex, __out LPCSTR *pszString) + { return m_StringHeap.GetString(nIndex, pszString); } + __checkReturn + HRESULT Impl_GetStringW(ULONG ix, __inout_ecount (cchBuffer) LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer); + __checkReturn + FORCEINLINE HRESULT Impl_GetGuid(UINT32 nIndex, GUID *pTargetGuid) + { + HRESULT hr; + GUID UNALIGNED *pSourceGuid; + IfFailRet(m_GuidHeap.GetGuid( + nIndex, + &pSourceGuid)); + // Add void* casts so that the compiler can't make assumptions about alignment. + CopyMemory((void *)pTargetGuid, (void *)pSourceGuid, sizeof(GUID)); + SwapGuid(pTargetGuid); + return S_OK; + } + __checkReturn + FORCEINLINE HRESULT Impl_GetBlob(UINT32 nIndex, __out MetaData::DataBlob *pData) + { return m_BlobHeap.GetBlob(nIndex, pData); } + + __checkReturn + FORCEINLINE HRESULT Impl_GetRow( + UINT32 nTableIndex, + UINT32 nRowIndex, + __deref_out_opt BYTE **ppRecord) + { + _ASSERTE(nTableIndex < TBL_COUNT); + return m_Tables[nTableIndex].GetRecord( + nRowIndex, + ppRecord, + m_TableDefs[nTableIndex].m_cbRec, + m_Schema.m_cRecs[nTableIndex], +#ifdef FEATURE_PREJIT + m_pHotTablesDirectory, +#endif //FEATURE_PREJIT + nTableIndex); + } + + // Count of rows in tbl2, pointed to by the column in tbl. + __checkReturn + HRESULT Impl_GetEndRidForColumn( + UINT32 nTableIndex, + RID nRowIndex, + CMiniColDef &def, // Column containing the RID into other table. + UINT32 nTargetTableIndex, // The other table. + RID *pEndRid); + + __checkReturn + FORCEINLINE HRESULT Impl_SearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ixCol, ULONG ulTarget, RID *pFoundRid) + { + return vSearchTable(ixTbl, sColumn, ulTarget, pFoundRid); + } + + // given a rid to the Property table, find an entry in PropertyMap table that contains the back pointer + // to its typedef parent + __checkReturn + HRESULT FindPropertyMapParentOfProperty(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_PropertyMap, _COLDEF(PropertyMap,PropertyList), rid, pFoundRid); + } + + __checkReturn + HRESULT FindParentOfPropertyHelper( + mdProperty pr, + mdTypeDef *ptd) + { + HRESULT hr = NOERROR; + + RID ridPropertyMap; + PropertyMapRec *pRec; + + IfFailRet(FindPropertyMapParentOfProperty(RidFromToken(pr), &ridPropertyMap)); + IfFailRet(GetPropertyMapRecord(ridPropertyMap, &pRec)); + *ptd = getParentOfPropertyMap( pRec ); + + RidToToken(*ptd, mdtTypeDef); + + return hr; + } // HRESULT CMiniMdRW::FindParentOfPropertyHelper() + + // given a rid to the Event table, find an entry in EventMap table that contains the back pointer + // to its typedef parent + __checkReturn + HRESULT FindEventMapParentOfEvent(RID rid, RID *pFoundRid) + { + return vSearchTableNotGreater(TBL_EventMap, _COLDEF(EventMap, EventList), rid, pFoundRid); + } + + __checkReturn + HRESULT FindParentOfEventHelper( + mdEvent pr, + mdTypeDef *ptd) + { + HRESULT hr = NOERROR; + + RID ridEventMap; + EventMapRec *pRec; + + IfFailRet(FindEventMapParentOfEvent(RidFromToken(pr), &ridEventMap)); + IfFailRet(GetEventMapRecord(ridEventMap, &pRec)); + *ptd = getParentOfEventMap( pRec ); + + RidToToken(*ptd, mdtTypeDef); + + return hr; + } // HRESULT CMiniMdRW::FindParentOfEventHelper() + + FORCEINLINE int Impl_IsRo() + { return 1; } + //************************************************************************* + + __checkReturn + HRESULT CommonEnumCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + bool fStopAtFirstFind, // [IN] just find the first one + HENUMInternal* phEnum); // enumerator to fill up + + __checkReturn + HRESULT CommonGetCustomAttributeByNameEx( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + mdCustomAttribute *ptkCA, // [OUT] put custom attribute token here + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData); // [OUT] Put size of data here. + + public: + virtual BOOL IsWritable() + { + return FALSE; + } + +}; // class CMiniMd + +#endif // _METAMODELRO_H_ diff --git a/src/coreclr/md/inc/metamodelrw.h b/src/coreclr/md/inc/metamodelrw.h new file mode 100644 index 00000000000..51cb0707c89 --- /dev/null +++ b/src/coreclr/md/inc/metamodelrw.h @@ -0,0 +1,1456 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +//***************************************************************************** +// MetaModelRW.h -- header file for Read/Write compressed COM+ metadata. +// + +// +// Used by Emitters and by E&C. +// +//***************************************************************************** +#ifndef _METAMODELRW_H_ +#define _METAMODELRW_H_ + +#if _MSC_VER >= 1100 + # pragma once +#endif + +#include "metamodel.h" // Base classes for the MetaModel. +#include "metadatahash.h" +#include "rwutil.h" +#include "shash.h" + +#include "../heaps/export.h" +#include "../hotdata/export.h" +#include "../tables/export.h" + +struct HENUMInternal; +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE +struct IMDCustomDataSource; +#endif + +// ENUM for marking bit +enum +{ + InvalidMarkedBit = 0x00000000, + ModuleMarkedBit = 0x00000001, + TypeRefMarkedBit = 0x00000002, + TypeDefMarkedBit = 0x00000004, + FieldMarkedBit = 0x00000008, + MethodMarkedBit = 0x00000010, + ParamMarkedBit = 0x00000020, + MemberRefMarkedBit = 0x00000040, + CustomAttributeMarkedBit = 0x00000080, + DeclSecurityMarkedBit = 0x00000100, + SignatureMarkedBit = 0x00000200, + EventMarkedBit = 0x00000400, + PropertyMarkedBit = 0x00000800, + MethodImplMarkedBit = 0x00001000, + ModuleRefMarkedBit = 0x00002000, + TypeSpecMarkedBit = 0x00004000, + InterfaceImplMarkedBit = 0x00008000, + AssemblyRefMarkedBit = 0x00010000, + MethodSpecMarkedBit = 0x00020000, + +}; + +// entry for marking UserString +struct FilterUserStringEntry +{ + DWORD m_tkString; + bool m_fMarked; +}; + +class FilterTable : public CDynArray<DWORD> +{ +public: + FilterTable() { m_daUserStringMarker = NULL; } + ~FilterTable(); + + __checkReturn FORCEINLINE HRESULT MarkTypeRef(mdToken tk) { return MarkToken(tk, TypeRefMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkTypeDef(mdToken tk) { return MarkToken(tk, TypeDefMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkField(mdToken tk) { return MarkToken(tk, FieldMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkMethod(mdToken tk) { return MarkToken(tk, MethodMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkParam(mdToken tk) { return MarkToken(tk, ParamMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkMemberRef(mdToken tk) { return MarkToken(tk, MemberRefMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkCustomAttribute(mdToken tk) { return MarkToken(tk, CustomAttributeMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkDeclSecurity(mdToken tk) { return MarkToken(tk, DeclSecurityMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkSignature(mdToken tk) { return MarkToken(tk, SignatureMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkEvent(mdToken tk) { return MarkToken(tk, EventMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkProperty(mdToken tk) { return MarkToken(tk, PropertyMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkMethodImpl(RID rid) + { + return MarkToken(TokenFromRid(rid, TBL_MethodImpl << 24), MethodImplMarkedBit); + } + __checkReturn FORCEINLINE HRESULT MarkModuleRef(mdToken tk) { return MarkToken(tk, ModuleRefMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkTypeSpec(mdToken tk) { return MarkToken(tk, TypeSpecMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkInterfaceImpl(mdToken tk) { return MarkToken(tk, InterfaceImplMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkAssemblyRef(mdToken tk) { return MarkToken(tk, AssemblyRefMarkedBit); } + __checkReturn FORCEINLINE HRESULT MarkMethodSpec(mdToken tk) { return MarkToken(tk, MethodSpecMarkedBit); } + + // It may look inconsistent but it is because taht UserString an offset to the heap. + // We don't want to grow the FilterTable to the size of the UserString heap. + // So we use the heap's marking system instead... + // + __checkReturn HRESULT MarkUserString(mdString str); + + __checkReturn HRESULT MarkNewUserString(mdString str); + + FORCEINLINE bool IsTypeRefMarked(mdToken tk) { return IsTokenMarked(tk, TypeRefMarkedBit); } + FORCEINLINE bool IsTypeDefMarked(mdToken tk) { return IsTokenMarked(tk, TypeDefMarkedBit); } + FORCEINLINE bool IsFieldMarked(mdToken tk) { return IsTokenMarked(tk, FieldMarkedBit); } + FORCEINLINE bool IsMethodMarked(mdToken tk) { return IsTokenMarked(tk, MethodMarkedBit); } + FORCEINLINE bool IsParamMarked(mdToken tk) { return IsTokenMarked(tk, ParamMarkedBit); } + FORCEINLINE bool IsMemberRefMarked(mdToken tk) { return IsTokenMarked(tk, MemberRefMarkedBit); } + FORCEINLINE bool IsCustomAttributeMarked(mdToken tk) { return IsTokenMarked(tk, CustomAttributeMarkedBit); } + FORCEINLINE bool IsDeclSecurityMarked(mdToken tk) { return IsTokenMarked(tk, DeclSecurityMarkedBit); } + FORCEINLINE bool IsSignatureMarked(mdToken tk) { return IsTokenMarked(tk, SignatureMarkedBit); } + FORCEINLINE bool IsEventMarked(mdToken tk) { return IsTokenMarked(tk, EventMarkedBit); } + FORCEINLINE bool IsPropertyMarked(mdToken tk) { return IsTokenMarked(tk, PropertyMarkedBit); } + FORCEINLINE bool IsMethodImplMarked(RID rid) + { + return IsTokenMarked(TokenFromRid(rid, TBL_MethodImpl << 24), MethodImplMarkedBit); + } + FORCEINLINE bool IsModuleRefMarked(mdToken tk) { return IsTokenMarked(tk, ModuleRefMarkedBit); } + FORCEINLINE bool IsTypeSpecMarked(mdToken tk) { return IsTokenMarked(tk, TypeSpecMarkedBit); } + FORCEINLINE bool IsInterfaceImplMarked(mdToken tk){ return IsTokenMarked(tk, InterfaceImplMarkedBit); } + FORCEINLINE bool IsAssemblyRefMarked(mdToken tk){ return IsTokenMarked(tk, AssemblyRefMarkedBit); } + FORCEINLINE bool IsMethodSpecMarked(mdToken tk){ return IsTokenMarked(tk, MethodSpecMarkedBit); } + + bool IsUserStringMarked(mdString str); + + __checkReturn HRESULT UnmarkAll(CMiniMdRW *pMiniMd, ULONG ulSize); + __checkReturn HRESULT MarkAll(CMiniMdRW *pMiniMd, ULONG ulSize); + bool IsTokenMarked(mdToken); + + __checkReturn FORCEINLINE HRESULT UnmarkTypeDef(mdToken tk) { return UnmarkToken(tk, TypeDefMarkedBit); } + __checkReturn FORCEINLINE HRESULT UnmarkField(mdToken tk) { return UnmarkToken(tk, FieldMarkedBit); } + __checkReturn FORCEINLINE HRESULT UnmarkMethod(mdToken tk) { return UnmarkToken(tk, MethodMarkedBit); } + __checkReturn FORCEINLINE HRESULT UnmarkCustomAttribute(mdToken tk) { return UnmarkToken(tk, CustomAttributeMarkedBit); } + +private: + CDynArray<FilterUserStringEntry> *m_daUserStringMarker; + bool IsTokenMarked(mdToken tk, DWORD bitMarked); + __checkReturn HRESULT MarkToken(mdToken tk, DWORD bit); + __checkReturn HRESULT UnmarkToken(mdToken tk, DWORD bit); +}; // class FilterTable : public CDynArray<DWORD> + +class CMiniMdRW; + +//***************************************************************************** +// This class is used to keep a list of RID. This list of RID can be sorted +// base on the m_ixCol's value of the m_ixTbl table. +//***************************************************************************** +class VirtualSort +{ +public: + void Init(ULONG ixTbl, ULONG ixCol, CMiniMdRW *pMiniMd); + void Uninit(); + TOKENMAP *m_pMap; // RID for m_ixTbl table. Sorted by on the ixCol + bool m_isMapValid; + ULONG m_ixTbl; // Table this is a sorter for. + ULONG m_ixCol; // Key column in the table. + CMiniMdRW *m_pMiniMd; // The MiniMd with the data. + __checkReturn + HRESULT Sort(); +private: + mdToken m_tkBuf; + __checkReturn + HRESULT SortRange(int iLeft, int iRight); +public: + __checkReturn + HRESULT Compare( + RID iLeft, // First item to compare. + RID iRight, // Second item to compare. + int *pnResult); // -1, 0, or 1 + +private: + FORCEINLINE void Swap( + RID iFirst, + RID iSecond) + { + if ( iFirst == iSecond ) return; + m_tkBuf = *(m_pMap->Get(iFirst)); + *(m_pMap->Get(iFirst)) = *(m_pMap->Get(iSecond)); + *(m_pMap->Get(iSecond)) = m_tkBuf; + } + + +}; // class VirtualSort + +class ReorderData +{ +public: + typedef enum + { + MinReorderBucketType=0, // bucket# shouldn't be less than this value + Undefined=0, // use this for initialization + Duplicate=1, // duplicate string + ProfileData=2, // bucket# for IBC data + PublicData=3, // bucket# for public data + OtherData=4, // bucket# for other data + NonPublicData=5, // bucket# for non-public data + MaxReorderBucketType=255 // bucket# shouldn't exceeed this value + } ReorderBucketType; +}; + +typedef CMetaDataHashBase CMemberRefHash; +typedef CMetaDataHashBase CLookUpHash; + +class MDTOKENMAP; +class MDInternalRW; +class CorProfileData; +class UTSemReadWrite; + +template <class MiniMd> class CLiteWeightStgdb; +//***************************************************************************** +// Read/Write MiniMd. +//***************************************************************************** +class CMiniMdRW : public CMiniMdTemplate<CMiniMdRW> +{ +public: + friend class CLiteWeightStgdb<CMiniMdRW>; + friend class CLiteWeightStgdbRW; + friend class CMiniMdTemplate<CMiniMdRW>; + friend class CQuickSortMiniMdRW; + friend class VirtualSort; + friend class MDInternalRW; + friend class RegMeta; + friend class FilterTable; + friend class ImportHelper; + friend class VerifyLayoutsMD; + + CMiniMdRW(); + ~CMiniMdRW(); + + __checkReturn + HRESULT InitNew(); + __checkReturn + HRESULT InitOnMem(const void *pBuf, ULONG ulBufLen, int bReadOnly); + __checkReturn + HRESULT PostInit(int iLevel); + __checkReturn + HRESULT InitPoolOnMem(int iPool, void *pbData, ULONG cbData, int bReadOnly); + __checkReturn + HRESULT InitOnRO(CMiniMd *pMd, int bReadOnly); +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + __checkReturn + HRESULT InitOnCustomDataSource(IMDCustomDataSource* pDataSouce); +#endif + __checkReturn + HRESULT ConvertToRW(); + + __checkReturn + HRESULT GetSaveSize( + CorSaveSize fSave, + UINT32 *pcbSize, + DWORD *pbCompressed, + MetaDataReorderingOptions reorderingOptions = NoReordering, + CorProfileData *pProfileData = NULL); + int IsPoolEmpty(int iPool); + __checkReturn + HRESULT GetPoolSaveSize(int iPool, UINT32 *pcbSize); + + __checkReturn + HRESULT SaveTablesToStream(IStream *pIStream, MetaDataReorderingOptions reorderingOptions, CorProfileData *pProfileData); + __checkReturn + HRESULT SavePoolToStream(int iPool, IStream *pIStream); + __checkReturn + HRESULT SaveDone(); + + __checkReturn + HRESULT SetHandler(IUnknown *pIUnk); + + __checkReturn + HRESULT SetOption(OptionValue *pOptionValue); + __checkReturn + HRESULT GetOption(OptionValue *pOptionValue); + + static ULONG GetTableForToken(mdToken tkn); + static mdToken GetTokenForTable(ULONG ixTbl); + + FORCEINLINE static ULONG TblFromRecId(ULONG ul) { return (ul >> 24)&0x7f; } + FORCEINLINE static ULONG RidFromRecId(ULONG ul) { return ul & 0xffffff; } + FORCEINLINE static ULONG RecIdFromRid(ULONG rid, ULONG ixTbl) { return rid | ((ixTbl|0x80) << 24); } + FORCEINLINE static int IsRecId(ULONG ul) { return (ul & 0x80000000) != 0;} + + // Place in every API function before doing any allocations. + __checkReturn + FORCEINLINE HRESULT PreUpdate() + { + if (m_eGrow == eg_grow) + { + return ExpandTables(); + } + return S_OK; + } + + __checkReturn + HRESULT AddRecord( + UINT32 nTableIndex, + void **ppRow, + RID *pRid); + + __checkReturn + FORCEINLINE HRESULT PutCol(ULONG ixTbl, ULONG ixCol, void *pRecord, ULONG uVal) + { _ASSERTE(ixTbl < TBL_COUNT); _ASSERTE(ixCol < m_TableDefs[ixTbl].m_cCols); + return PutCol(m_TableDefs[ixTbl].m_pColDefs[ixCol], pRecord, uVal); + } // HRESULT CMiniMdRW::PutCol() + __checkReturn + HRESULT PutString(ULONG ixTbl, ULONG ixCol, void *pRecord, LPCSTR szString); + __checkReturn + HRESULT PutStringW(ULONG ixTbl, ULONG ixCol, void *pRecord, LPCWSTR wszString); + __checkReturn + HRESULT PutGuid(ULONG ixTbl, ULONG ixCol, void *pRecord, REFGUID guid); + __checkReturn + HRESULT ChangeMvid(REFGUID newMvid); + __checkReturn + HRESULT PutToken(ULONG ixTbl, ULONG ixCol, void *pRecord, mdToken tk); + __checkReturn + HRESULT PutBlob(ULONG ixTbl, ULONG ixCol, void *pRecord, const void *pvData, ULONG cbData); + + __checkReturn + HRESULT PutUserString(MetaData::DataBlob data, UINT32 *pnIndex) + { return m_UserStringHeap.AddBlob(data, pnIndex); } + + ULONG GetCol(ULONG ixTbl, ULONG ixCol, void *pRecord); + mdToken GetToken(ULONG ixTbl, ULONG ixCol, void *pRecord); + + // Add a record to a table, and return a typed XXXRec *. +// #undef AddTblRecord + #define AddTblRecord(tbl) \ + __checkReturn HRESULT Add##tbl##Record(tbl##Rec **ppRow, RID *pnRowIndex) \ + { return AddRecord(TBL_##tbl, reinterpret_cast<void **>(ppRow), pnRowIndex); } + + AddTblRecord(Module) + AddTblRecord(TypeRef) + __checkReturn HRESULT AddTypeDefRecord( // Specialized implementation. + TypeDefRec **ppRow, + RID *pnRowIndex); + AddTblRecord(Field) + __checkReturn HRESULT AddMethodRecord( // Specialized implementation. + MethodRec **ppRow, + RID *pnRowIndex); + AddTblRecord(Param) + AddTblRecord(InterfaceImpl) + AddTblRecord(MemberRef) + AddTblRecord(Constant) + AddTblRecord(CustomAttribute) + AddTblRecord(FieldMarshal) + AddTblRecord(DeclSecurity) + AddTblRecord(ClassLayout) + AddTblRecord(FieldLayout) + AddTblRecord(StandAloneSig) + __checkReturn HRESULT AddEventMapRecord( // Specialized implementation. + EventMapRec **ppRow, + RID *pnRowIndex); + AddTblRecord(Event) + __checkReturn HRESULT AddPropertyMapRecord( // Specialized implementation. + PropertyMapRec **ppRow, + RID *pnRowIndex); + AddTblRecord(Property) + AddTblRecord(MethodSemantics) + AddTblRecord(MethodImpl) + AddTblRecord(ModuleRef) + AddTblRecord(FieldPtr) + AddTblRecord(MethodPtr) + AddTblRecord(ParamPtr) + AddTblRecord(PropertyPtr) + AddTblRecord(EventPtr) + + AddTblRecord(ENCLog) + AddTblRecord(TypeSpec) + AddTblRecord(ImplMap) + AddTblRecord(ENCMap) + AddTblRecord(FieldRVA) + + // Assembly Tables. + AddTblRecord(Assembly) + AddTblRecord(AssemblyProcessor) + AddTblRecord(AssemblyOS) + AddTblRecord(AssemblyRef) + AddTblRecord(AssemblyRefProcessor) + AddTblRecord(AssemblyRefOS) + AddTblRecord(File) + AddTblRecord(ExportedType) + AddTblRecord(ManifestResource) + + AddTblRecord(NestedClass) + AddTblRecord(GenericParam) + AddTblRecord(MethodSpec) + AddTblRecord(GenericParamConstraint) + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + AddTblRecord(Document) + AddTblRecord(MethodDebugInformation) + AddTblRecord(LocalScope) + AddTblRecord(LocalVariable) + AddTblRecord(LocalConstant) + AddTblRecord(ImportScope) + // TODO: + // AddTblRecord(StateMachineMethod) + // AddTblRecord(CustomDebugInformation) +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + + // Specialized AddXxxToYyy() functions. + __checkReturn HRESULT AddMethodToTypeDef(RID td, RID md); + __checkReturn HRESULT AddFieldToTypeDef(RID td, RID md); + __checkReturn HRESULT AddParamToMethod(RID md, RID pd); + __checkReturn HRESULT AddPropertyToPropertyMap(RID pmd, RID pd); + __checkReturn HRESULT AddEventToEventMap(ULONG emd, RID ed); + + // does the MiniMdRW has the indirect tables, such as FieldPtr, MethodPtr + FORCEINLINE int HasIndirectTable(ULONG ix) + { if (g_PtrTableIxs[ix].m_ixtbl < TBL_COUNT) return GetCountRecs(g_PtrTableIxs[ix].m_ixtbl); return 0;} + + FORCEINLINE int IsVsMapValid(ULONG ixTbl) + { _ASSERTE(ixTbl<TBL_COUNT); return (m_pVS[ixTbl] && m_pVS[ixTbl]->m_isMapValid); } + + // translate index returned by getMethodListOfTypeDef to a rid into Method table + __checkReturn + FORCEINLINE HRESULT GetMethodRid(ULONG index, ULONG *pRid) + { + HRESULT hr; + if (HasIndirectTable(TBL_Method)) + { + MethodPtrRec *pMethodPtrRecord; + IfFailGo(GetMethodPtrRecord(index, &pMethodPtrRecord)); + *pRid = getMethodOfMethodPtr(pMethodPtrRecord); + } + else + { + *pRid = index; + } + return S_OK; + ErrExit: + *pRid = 0; + return hr; + } + + // translate index returned by getFieldListOfTypeDef to a rid into Field table + __checkReturn + FORCEINLINE HRESULT GetFieldRid(ULONG index, ULONG *pRid) + { + HRESULT hr; + if (HasIndirectTable(TBL_Field)) + { + FieldPtrRec *pFieldPtrRecord; + IfFailGo(GetFieldPtrRecord(index, &pFieldPtrRecord)); + *pRid = getFieldOfFieldPtr(pFieldPtrRecord); + } + else + { + *pRid = index; + } + return S_OK; + ErrExit: + *pRid = 0; + return hr; + } + + // translate index returned by getParamListOfMethod to a rid into Param table + __checkReturn + FORCEINLINE HRESULT GetParamRid(ULONG index, ULONG *pRid) + { + HRESULT hr; + if (HasIndirectTable(TBL_Param)) + { + ParamPtrRec *pParamPtrRecord; + IfFailGo(GetParamPtrRecord(index, &pParamPtrRecord)); + *pRid = getParamOfParamPtr(pParamPtrRecord); + } + else + { + *pRid = index; + } + return S_OK; + ErrExit: + *pRid = 0; + return hr; + } + + // translate index returned by getEventListOfEventMap to a rid into Event table + __checkReturn + FORCEINLINE HRESULT GetEventRid(ULONG index, ULONG *pRid) + { + HRESULT hr; + if (HasIndirectTable(TBL_Event)) + { + EventPtrRec *pEventPtrRecord; + IfFailGo(GetEventPtrRecord(index, &pEventPtrRecord)); + *pRid = getEventOfEventPtr(pEventPtrRecord); + } + else + { + *pRid = index; + } + return S_OK; + ErrExit: + *pRid = 0; + return hr; + } + + // translate index returned by getPropertyListOfPropertyMap to a rid into Property table + __checkReturn + FORCEINLINE HRESULT GetPropertyRid(ULONG index, ULONG *pRid) + { + HRESULT hr; + if (HasIndirectTable(TBL_Property)) + { + PropertyPtrRec *pPropertyPtrRecord; + IfFailGo(GetPropertyPtrRecord(index, &pPropertyPtrRecord)); + *pRid = getPropertyOfPropertyPtr(pPropertyPtrRecord); + } + else + { + *pRid = index; + } + return S_OK; + ErrExit: + *pRid = 0; + return hr; + } + + // Convert a pseudo-RID from a Virtual Sort into a real RID. + FORCEINLINE ULONG GetRidFromVirtualSort(ULONG ixTbl, ULONG index) + { return IsVsMapValid(ixTbl) ? *(m_pVS[ixTbl]->m_pMap->Get(index)) : index; } + + // Index returned by GetInterfaceImplForTypeDef. It could be index to VirtualSort table + // or directly to InterfaceImpl + FORCEINLINE ULONG GetInterfaceImplRid(ULONG index) + { return GetRidFromVirtualSort(TBL_InterfaceImpl, index); } + + // Index returned by GetGenericParamForToken. It could be index to VirtualSort table + // or directly to GenericParam + FORCEINLINE ULONG GetGenericParamRid(ULONG index) + { return GetRidFromVirtualSort(TBL_GenericParam, index); } + + // Index returned by GetGenericParamConstraintForToken. It could be index to VirtualSort table + // or directly to GenericParamConstraint + FORCEINLINE ULONG GetGenericParamConstraintRid(ULONG index) + { return GetRidFromVirtualSort(TBL_GenericParamConstraint, index); } + + // Index returned by GetDeclSecurityForToken. It could be index to VirtualSort table + // or directly to DeclSecurity + FORCEINLINE ULONG GetDeclSecurityRid(ULONG index) + { return GetRidFromVirtualSort(TBL_DeclSecurity, index); } + + // Index returned by GetCustomAttributeForToken. It could be index to VirtualSort table + // or directly to CustomAttribute + FORCEINLINE ULONG GetCustomAttributeRid(ULONG index) + { return GetRidFromVirtualSort(TBL_CustomAttribute, index); } + + // add method, field, property, event, param to the map table + __checkReturn HRESULT AddMethodToLookUpTable(mdMethodDef md, mdTypeDef td); + __checkReturn HRESULT AddFieldToLookUpTable(mdFieldDef fd, mdTypeDef td); + __checkReturn HRESULT AddPropertyToLookUpTable(mdProperty pr, mdTypeDef td); + __checkReturn HRESULT AddEventToLookUpTable(mdEvent ev, mdTypeDef td); + __checkReturn HRESULT AddParamToLookUpTable(mdParamDef pd, mdMethodDef md); + + // look up the parent of method, field, property, event, or param + __checkReturn HRESULT FindParentOfMethodHelper(mdMethodDef md, mdTypeDef *ptd); + __checkReturn HRESULT FindParentOfFieldHelper(mdFieldDef fd, mdTypeDef *ptd); + __checkReturn HRESULT FindParentOfPropertyHelper(mdProperty pr, mdTypeDef *ptd); + __checkReturn HRESULT FindParentOfEventHelper(mdEvent ev, mdTypeDef *ptd); + __checkReturn HRESULT FindParentOfParamHelper(mdParamDef pd, mdMethodDef *pmd); + + bool IsMemberDefHashPresent() { return m_pMemberDefHash != NULL; } + + // Function to reorganize the string pool based on IBC profile data (if available) and static analysis. + // Throws on error. + VOID OrganizeStringPool(CorProfileData *pProfileData); + + // Result of hash search + enum HashSearchResult + { + Found, // Item was found. + NotFound, // Item not found. + NoTable // Table hasn't been built. + }; + + // Create MemberRef hash table. + __checkReturn + HRESULT CreateMemberRefHash(); + + // Add a new MemberRef to the hash table. + __checkReturn + HRESULT AddMemberRefToHash( // Return code. + mdMemberRef mr); // Token of new MemberRef. + + // If the hash is built, search for the item. Ignore token *ptkMemberRef. + HashSearchResult FindMemberRefFromHash( + mdToken tkParent, // Parent token. + LPCUTF8 szName, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob, // Size of signature. + mdMemberRef * ptkMemberRef); // IN: Ignored token. OUT: Return if found. + + //************************************************************************* + // Check a given mr token to see if this one is a match. + //************************************************************************* + __checkReturn + HRESULT CompareMemberRefs( // S_OK match, S_FALSE no match. + mdMemberRef mr, // Token to check. + mdToken tkPar, // Parent token. + LPCUTF8 szNameUtf8, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob); // Size of signature. + + // Add a new MemberDef to the hash table. + __checkReturn + HRESULT AddMemberDefToHash( + mdToken tkMember, // Token of new def. It can be MethodDef or FieldDef + mdToken tkParent); // Parent token. + + // Create MemberDef Hash + __checkReturn + HRESULT CreateMemberDefHash(); + + // If the hash is built, search for the item. Ignore token *ptkMember. + HashSearchResult FindMemberDefFromHash( + mdToken tkParent, // Parent token. + LPCUTF8 szName, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob, // Size of signature. + mdToken * ptkMember); // IN: Ignored token. OUT: Return if found. It can be MethodDef or FieldDef + + //************************************************************************* + // Check a given Method/Field token to see if this one is a match. + //************************************************************************* + __checkReturn + HRESULT CompareMemberDefs( // S_OK match, S_FALSE no match. + mdToken tkMember, // Token to check. It can be MethodDef or FieldDef + mdToken tkParent, // Parent token recorded in the hash entry + mdToken tkPar, // Parent token. + LPCUTF8 szNameUtf8, // Name of item. + PCCOR_SIGNATURE pvSigBlob, // Signature. + ULONG cbSigBlob); // Size of signature. + + //************************************************************************* + // Add a new CustomAttributes to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddCustomAttributesToHash( // Return code. + mdCustomAttribute cv) // Token of new guy. + { return GenericAddToHash(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, RidFromToken(cv)); } + + inline ULONG HashMemberRef(mdToken tkPar, LPCUTF8 szName) + { + ULONG l = HashBytes((const BYTE *) &tkPar, sizeof(mdToken)) + HashStringA(szName); + return (l); + } + + inline ULONG HashMemberDef(mdToken tkPar, LPCUTF8 szName) + { + return HashMemberRef(tkPar, szName); + } + + // helper to calculate the hash value given a token + inline ULONG HashCustomAttribute(mdToken tkObject) + { + return HashToken(tkObject); + } + + DAC_ALIGNAS(CMiniMdTemplate<CMiniMdRW>) // Align the first member to the alignment of the base class + CMemberRefHash *m_pMemberRefHash; + + // Hash table for Methods and Fields + CMemberDefHash *m_pMemberDefHash; + + // helper to calculate the hash value given a pair of tokens + inline ULONG HashToken(mdToken tkObject) + { + ULONG l = HashBytes((const BYTE *) &tkObject, sizeof(mdToken)); + return (l); + } + + + //************************************************************************* + // Add a new FieldMarhsal Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddFieldMarshalToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_FieldMarshal, FieldMarshalRec::COL_Parent, rid); } + + //************************************************************************* + // Add a new Constant Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddConstantToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_Constant, ConstantRec::COL_Parent, rid); } + + //************************************************************************* + // Add a new MethodSemantics Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddMethodSemanticsToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_MethodSemantics, MethodSemanticsRec::COL_Association, rid); } + + //************************************************************************* + // Add a new ClassLayout Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddClassLayoutToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_ClassLayout, ClassLayoutRec::COL_Parent, rid); } + + //************************************************************************* + // Add a new FieldLayout Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddFieldLayoutToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_FieldLayout, FieldLayoutRec::COL_Field, rid); } + + //************************************************************************* + // Add a new ImplMap Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddImplMapToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_ImplMap, ImplMapRec::COL_MemberForwarded, rid); } + + //************************************************************************* + // Add a new FieldRVA Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddFieldRVAToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_FieldRVA, FieldRVARec::COL_Field, rid); } + + //************************************************************************* + // Add a new nested class Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddNestedClassToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_NestedClass, NestedClassRec::COL_NestedClass, rid); } + + //************************************************************************* + // Add a new MethodImpl Rid to the hash table. + //************************************************************************* + __checkReturn + HRESULT AddMethodImplToHash( // Return code. + RID rid) // Token of new guy. + { return GenericAddToHash(TBL_MethodImpl, MethodImplRec::COL_Class, rid); } + + + //************************************************************************* + // Build a hash table for the specified table if the size exceed the thresholds. + //************************************************************************* + __checkReturn + HRESULT GenericBuildHashTable( // Return code. + ULONG ixTbl, // Table with hash + ULONG ixCol); // col that we hash. + + //************************************************************************* + // Add a rid from a table into a hash + //************************************************************************* + __checkReturn + HRESULT GenericAddToHash( // Return code. + ULONG ixTbl, // Table with hash + ULONG ixCol, // col that we hash. + RID rid); // new row of the table. + + //************************************************************************* + // Add a rid from a table into a hash + //************************************************************************* + __checkReturn + HRESULT GenericFindWithHash( // Return code. + ULONG ixTbl, // Table with hash + ULONG ixCol, // col that we hash. + mdToken tkTarget, // token to be find in the hash + RID *pFoundRid); + + + // look up hash table for tokenless tables. + // They are constant, FieldMarshal, MethodSemantics, ClassLayout, FieldLayout, ImplMap, FieldRVA, NestedClass, and MethodImpl + CLookUpHash * m_pLookUpHashs[TBL_COUNT]; + +#if !defined(DACCESS_COMPILE) + MapSHash<UINT32, UINT32> m_StringPoolOffsetHash; +#endif + + //************************************************************************* + // Hash for named items. + //************************************************************************* + __checkReturn + HRESULT AddNamedItemToHash( // Return code. + ULONG ixTbl, // Table with the new item. + mdToken tk, // Token of new guy. + LPCUTF8 szName, // Name of item. + mdToken tkParent); // Token of parent, if any. + + HashSearchResult FindNamedItemFromHash( + ULONG ixTbl, // Table with the item. + LPCUTF8 szName, // Name of item. + mdToken tkParent, // Token of parent, if any. + mdToken * ptk); // Return if found. + + __checkReturn + HRESULT CompareNamedItems( // S_OK match, S_FALSE no match. + ULONG ixTbl, // Table with the item. + mdToken tk, // Token to check. + LPCUTF8 szName, // Name of item. + mdToken tkParent); // Token of parent, if any. + + FORCEINLINE ULONG HashNamedItem(mdToken tkPar, LPCUTF8 szName) + { return HashBytes((const BYTE *) &tkPar, sizeof(mdToken)) + HashStringA(szName); } + + CMetaDataHashBase *m_pNamedItemHash; + + //***************************************************************************** + // IMetaModelCommon - RW specific versions for some of the functions. + //***************************************************************************** + __checkReturn + virtual HRESULT CommonGetEnclosingClassOfTypeDef( + mdTypeDef td, + mdTypeDef *ptkEnclosingTypeDef) + { + _ASSERTE(ptkEnclosingTypeDef != NULL); + + HRESULT hr; + NestedClassRec *pRec; + RID iRec; + + IfFailRet(FindNestedClassHelper(td, &iRec)); + if (iRec == 0) + { + *ptkEnclosingTypeDef = mdTypeDefNil; + return S_OK; + } + + IfFailRet(GetNestedClassRecord(iRec, &pRec)); + *ptkEnclosingTypeDef = getEnclosingClassOfNestedClass(pRec); + return S_OK; + } + + __checkReturn + HRESULT CommonEnumCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + bool fStopAtFirstFind, // [IN] just find the first one + HENUMInternal* phEnum); // enumerator to fill up + + __checkReturn + HRESULT CommonGetCustomAttributeByNameEx( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + mdCustomAttribute *ptkCA, // [OUT] put custom attribute token here + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData); // [OUT] Put size of data here. + + //***************************************************************************** + // Find helper for a constant. + //***************************************************************************** + __checkReturn + HRESULT FindConstantHelper( // return index to the constant table + mdToken tkParent, // Parent token. Can be ParamDef, FieldDef, or Property. + RID *pFoundRid); + + //***************************************************************************** + // Find helper for a FieldMarshal. + //***************************************************************************** + __checkReturn + HRESULT FindFieldMarshalHelper( // return index to the field marshal table + mdToken tkParent, // Parent token. Can be a FieldDef or ParamDef. + RID *pFoundRid); + + //***************************************************************************** + // Find helper for a method semantics. + //***************************************************************************** + __checkReturn + HRESULT FindMethodSemanticsHelper( // return HRESULT + mdToken tkAssociate, // Event or property token + HENUMInternal *phEnum); // fill in the enum + + //***************************************************************************** + // Find helper for a method semantics given a associate and semantics. + // This will look up methodsemantics based on its status! + // Return CLDB_E_RECORD_NOTFOUND if cannot find the matching one + //***************************************************************************** + __checkReturn + HRESULT FindAssociateHelper(// return HRESULT + mdToken tkAssociate, // Event or property token + DWORD dwSemantics, // [IN] given a associate semantics(setter, getter, testdefault, reset) + RID *pRid); // [OUT] return matching row index here + + //***************************************************************************** + // Find helper for a MethodImpl. + //***************************************************************************** + __checkReturn + HRESULT FindMethodImplHelper(// return HRESULT + mdTypeDef td, // TypeDef token for the Class. + HENUMInternal *phEnum); // fill in the enum + + //***************************************************************************** + // Find helper for a GenericParams + //***************************************************************************** + __checkReturn + HRESULT FindGenericParamHelper( // Return HRESULT + mdToken tkOwner, // Token for the GenericParams' owner + HENUMInternal *phEnum); // Fill in the enum. + + //***************************************************************************** + // Find helper for a Generic Constraints + //***************************************************************************** + __checkReturn + HRESULT FindGenericParamConstraintHelper( // Return HRESULT + mdGenericParam tkParam, // Token for the GenericParam + HENUMInternal *phEnum); // Fill in the enum. + + //***************************************************************************** + // Find helper for a ClassLayout. + //***************************************************************************** + __checkReturn + HRESULT FindClassLayoutHelper( // return index to the ClassLayout table + mdTypeDef tkParent, // Parent token. + RID *pFoundRid); + + //***************************************************************************** + // Find helper for a FieldLayout. + //***************************************************************************** + __checkReturn + HRESULT FindFieldLayoutHelper( // return index to the FieldLayout table + mdFieldDef tkField, // Token for the field. + RID *pFoundRid); + + //***************************************************************************** + // Find helper for a ImplMap. + //***************************************************************************** + __checkReturn + HRESULT FindImplMapHelper( // return index to the constant table + mdToken tk, // Member forwarded token. + RID *pFoundRid); + + //***************************************************************************** + // Find helper for a FieldRVA. + //***************************************************************************** + __checkReturn + HRESULT FindFieldRVAHelper( // return index to the FieldRVA table + mdFieldDef tkField, // Token for the field. + RID *pFoundRid); + + //***************************************************************************** + // Find helper for a NestedClass. + //***************************************************************************** + __checkReturn + HRESULT FindNestedClassHelper( // return index to the NestedClass table + mdTypeDef tkClass, // Token for the NestedClass. + RID *pFoundRid); + + //***************************************************************************** + // IMPORTANT!!!!!!!! Use these set of functions if you are dealing with RW rather + // getInterfaceImplsForTypeDef, getDeclSecurityForToken, etc. + // The following functions can deal with these tables when they are not sorted and + // build the VirtualSort tables for quick lookup. + //***************************************************************************** + __checkReturn + HRESULT GetInterfaceImplsForTypeDef(mdTypeDef td, RID *pRidStart, RID *pRidEnd = 0) + { + return LookUpTableByCol( RidFromToken(td), m_pVS[TBL_InterfaceImpl], pRidStart, pRidEnd); + } + + __checkReturn + HRESULT GetGenericParamsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0) + { + return LookUpTableByCol( + encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)), + m_pVS[TBL_GenericParam], pRidStart, pRidEnd); + } + + __checkReturn + HRESULT GetGenericParamConstraintsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0) + { + return LookUpTableByCol( RidFromToken(tk), + m_pVS[TBL_GenericParamConstraint], pRidStart, pRidEnd); + } + + __checkReturn + HRESULT GetMethodSpecsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0) + { + return LookUpTableByCol( + encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)), + m_pVS[TBL_MethodSpec], pRidStart, pRidEnd); + } + + __checkReturn + HRESULT GetDeclSecurityForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0) + { + return LookUpTableByCol( + encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, lengthof(mdtHasDeclSecurity)), + m_pVS[TBL_DeclSecurity], + pRidStart, + pRidEnd); + } + + __checkReturn + HRESULT GetCustomAttributeForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0) + { + return LookUpTableByCol( + encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, lengthof(mdtHasCustomAttribute)), + m_pVS[TBL_CustomAttribute], + pRidStart, + pRidEnd); + } + + __checkReturn + FORCEINLINE HRESULT GetUserString(ULONG nIndex, MetaData::DataBlob *pData) + { return m_UserStringHeap.GetBlob(nIndex, pData); } + // Gets user string (*Data) at index (nIndex) and fills the index (*pnNextIndex) of the next user string + // in the heap. + // Returns S_OK and fills the string (*pData) and the next index (*pnNextIndex). + // Returns S_FALSE if the index (nIndex) is not valid user string index. + // Returns error code otherwise. + // Clears *pData and sets *pnNextIndex to 0 on error or S_FALSE. + __checkReturn + HRESULT GetUserStringAndNextIndex( + UINT32 nIndex, + MetaData::DataBlob *pData, + UINT32 *pnNextIndex); + + FORCEINLINE int IsSorted(ULONG ixTbl) { return m_Schema.IsSorted(ixTbl);} + FORCEINLINE int IsSortable(ULONG ixTbl) { return m_bSortable[ixTbl];} + FORCEINLINE bool HasDelete() { return ((m_Schema.m_heaps & CMiniMdSchema::HAS_DELETE) ? true : false); } + FORCEINLINE int IsPreSaveDone() { return m_bPreSaveDone; } + +protected: + __checkReturn HRESULT PreSave(MetaDataReorderingOptions reorderingOptions=NoReordering, CorProfileData *pProfileData=NULL); + __checkReturn HRESULT PostSave(); + + __checkReturn HRESULT PreSaveFull(); + __checkReturn HRESULT PreSaveEnc(); + + __checkReturn HRESULT GetFullPoolSaveSize(int iPool, UINT32 *pcbSize); + __checkReturn HRESULT GetENCPoolSaveSize(int iPool, UINT32 *pcbSize); + + __checkReturn HRESULT SaveFullPoolToStream(int iPool, IStream *pIStream); + __checkReturn HRESULT SaveENCPoolToStream(int iPool, IStream *pIStream); + + __checkReturn + HRESULT GetHotMetadataTokensSearchAware( + CorProfileData *pProfileData, + ULONG ixTbl, + ULONG *pResultCount, + mdToken *tokenBuffer, + ULONG maxCount); + + __checkReturn + HRESULT GetFullSaveSize( + CorSaveSize fSave, + UINT32 *pcbSize, + DWORD *pbCompressed, + MetaDataReorderingOptions reorderingOptions = NoReordering, + CorProfileData *pProfileData = NULL); + __checkReturn + HRESULT GetENCSaveSize(UINT32 *pcbSize); + __checkReturn + HRESULT GetHotPoolsSaveSize( + UINT32 *pcbSize, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData); + + __checkReturn + HRESULT SaveFullTablesToStream(IStream *pIStream, MetaDataReorderingOptions reorderingOptions=NoReordering, CorProfileData *pProfileData = NULL ); + __checkReturn + HRESULT SaveENCTablesToStream(IStream *pIStream); + __checkReturn + HRESULT SaveHotPoolsToStream( + IStream *pStream, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData, + UINT32 *pnPoolDirSize, + UINT32 *pnSavedPoolsSize); + __checkReturn + HRESULT SaveHotPoolToStream( + IStream *pStream, + CorProfileData *pProfileData, + MetaData::HotHeapWriter *pHotHeapWriter, + UINT32 *pnSavedSize); + + // TO ELIMINATE: + __checkReturn + HRESULT AddGuid(REFGUID pGuid, UINT32 *pnIndex) + { return m_GuidHeap.AddGuid(&pGuid, pnIndex); } + + // Allows putting into tables outside this MiniMd, specifically the temporary + // table used on save. + __checkReturn + HRESULT PutCol(CMiniColDef ColDef, void *pRecord, ULONG uVal); + + // Returns TRUE if token (tk) is valid. + // For user strings, consideres 0 as valid token. + BOOL _IsValidToken( + mdToken tk) // [IN] token to be checked + { + if (TypeFromToken(tk) == mdtString) + { + // need to check the user string heap + return m_UserStringHeap.IsValidIndex(RidFromToken(tk)); + } + // Base type doesn't know about user string blob (yet) + return _IsValidTokenBase(tk); + } // CMiniMdRW::_IsValidToken + +#ifdef _DEBUG + bool CanHaveCustomAttribute(ULONG ixTbl); +#endif + + __checkReturn + HRESULT ExpandTables(); + __checkReturn + HRESULT ExpandTableColumns(CMiniMdSchema &Schema, ULONG ixTbl); + + __checkReturn + HRESULT InitWithLargeTables(); + + void ComputeGrowLimits(int bSmall=TRUE); // Set max, lim, based on param. + ULONG m_maxRid; // Highest RID so far allocated. + ULONG m_limRid; // Limit on RID before growing. + ULONG m_maxIx; // Highest pool index so far. + ULONG m_limIx; // Limit on pool index before growing. + enum {eg_ok, eg_grow, eg_grown} m_eGrow; // Is a grow required? done? + #define AUTO_GROW_CODED_TOKEN_PADDING 5 + + // fix up these tables after PreSave has move the tokens + __checkReturn HRESULT FixUpTable(ULONG ixTbl); + __checkReturn HRESULT FixUpRefToDef(); + + // Table info. + MetaData::TableRW m_Tables[TBL_COUNT]; + VirtualSort *m_pVS[TBL_COUNT]; // Virtual sorters, one per table, but sparse. + + //***************************************************************************** + // look up a table by a col given col value is ulVal. + //***************************************************************************** + __checkReturn + HRESULT LookUpTableByCol( + ULONG ulVal, + VirtualSort *pVSTable, + RID *pRidStart, + RID *pRidEnd); + + __checkReturn + HRESULT Impl_SearchTableRW(ULONG ixTbl, ULONG ixCol, ULONG ulTarget, RID *pFoundRid); + __checkReturn + virtual HRESULT vSearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid); + __checkReturn + virtual HRESULT vSearchTableNotGreater(ULONG ixTbl, CMiniColDef sColumn, ULONG ulTarget, RID *pRid); + + void SetSorted(ULONG ixTbl, int bSorted) + { m_Schema.SetSorted(ixTbl, bSorted); } + + void SetPreSaveDone(int bPreSaveDone) + { m_bPreSaveDone = bPreSaveDone; } + + // Heaps + MetaData::StringHeapRW m_StringHeap; + MetaData::BlobHeapRW m_BlobHeap; + MetaData::BlobHeapRW m_UserStringHeap; + MetaData::GuidHeapRW m_GuidHeap; + + IMapToken *m_pHandler; // Remap handler. + __checkReturn HRESULT MapToken(RID from, RID to, mdToken type); + + ULONG m_cbSaveSize; // Estimate of save size. + + int m_fIsReadOnly : 1; // Is this db read-only? + int m_bPreSaveDone : 1; // Has save optimization been done? + int m_bSaveCompressed : 1; // Can the data be saved as fully compressed? + int m_bPostGSSMod : 1; // true if a change was made post GetSaveSize. + + + //************************************************************************* + // Overridables -- must be provided in derived classes. + __checkReturn + FORCEINLINE HRESULT Impl_GetString(UINT32 nIndex, __out LPCSTR *pszString) + { return m_StringHeap.GetString(nIndex, pszString); } + __checkReturn + HRESULT Impl_GetStringW(ULONG ix, __inout_ecount (cchBuffer) LPWSTR szOut, ULONG cchBuffer, ULONG *pcchBuffer); + __checkReturn + FORCEINLINE HRESULT Impl_GetGuid(UINT32 nIndex, GUID *pTargetGuid) + { + HRESULT hr; + GUID UNALIGNED *pSourceGuid; + IfFailRet(m_GuidHeap.GetGuid( + nIndex, + &pSourceGuid)); + // Add void* casts so that the compiler can't make assumptions about alignment. + CopyMemory((void *)pTargetGuid, (void *)pSourceGuid, sizeof(GUID)); + SwapGuid(pTargetGuid); + return S_OK; + } + + __checkReturn + FORCEINLINE HRESULT Impl_GetBlob(ULONG nIndex, __out MetaData::DataBlob *pData) + { return m_BlobHeap.GetBlob(nIndex, pData); } + + __checkReturn + FORCEINLINE HRESULT Impl_GetRow( + UINT32 nTableIndex, + UINT32 nRowIndex, + __deref_out_opt BYTE **ppRecord) + { + _ASSERTE(nTableIndex < TBL_COUNT); + return m_Tables[nTableIndex].GetRecord(nRowIndex, ppRecord); + } + + // Count of rows in tbl2, pointed to by the column in tbl. + __checkReturn + HRESULT Impl_GetEndRidForColumn( + UINT32 nTableIndex, + RID nRowIndex, + CMiniColDef &def, // Column containing the RID into other table. + UINT32 nTargetTableIndex, // The other table. + RID *pEndRid); + + __checkReturn + FORCEINLINE HRESULT Impl_SearchTable(ULONG ixTbl, CMiniColDef sColumn, ULONG ixCol, ULONG ulTarget, RID *pFoundRid) + { return Impl_SearchTableRW(ixTbl, ixCol, ulTarget, pFoundRid); } + + FORCEINLINE int Impl_IsRo() + { return 0; } + + + //************************************************************************* + enum {END_OF_TABLE = 0}; + FORCEINLINE ULONG NewRecordPointerEndValue(ULONG ixTbl) + { if (HasIndirectTable(ixTbl)) return m_Schema.m_cRecs[ixTbl]+1; else return END_OF_TABLE; } + + __checkReturn HRESULT ConvertMarkerToEndOfTable(ULONG tblParent, ULONG colParent, ULONG ridChild, RID ridParent); + + // Add a child row, adjust pointers in parent rows. + __checkReturn + HRESULT AddChildRowIndirectForParent( + ULONG tblParent, + ULONG colParent, + ULONG tblChild, + RID ridParent, + void **ppRow); + + // Update pointers in the parent table to reflect the addition of a child, if required + // create the indirect table in which case don't update pointers. + __checkReturn + HRESULT AddChildRowDirectForParent(ULONG tblParent, ULONG colParent, ULONG tblChild, RID ridParent); + + // Given a table id, create the corresponding indirect table. + __checkReturn + HRESULT CreateIndirectTable(ULONG ixtbl, BOOL bOneLess = true); + + // If the last param is not added in the right sequence, fix it up. + __checkReturn + HRESULT FixParamSequence(RID md); + + + // these are the map tables to map a method, a field, a property, a event, or a param to its parent + TOKENMAP *m_pMethodMap; + TOKENMAP *m_pFieldMap; + TOKENMAP *m_pPropertyMap; + TOKENMAP *m_pEventMap; + TOKENMAP *m_pParamMap; + + // This table keep tracks tokens that are marked( or filtered) + FilterTable *m_pFilterTable; + IHostFilter *m_pHostFilter; + + // TOKENMAP *m_pTypeRefToTypeDefMap; + TokenRemapManager *m_pTokenRemapManager; + + OptionValue m_OptionValue; + + CMiniMdSchema m_StartupSchema; // Schema at start time. Keep count of records. + BYTE m_bSortable[TBL_COUNT]; // Is a given table sortable? (Can it be reorganized?) +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + ReleaseHolder<IMDCustomDataSource> m_pCustomDataSource; +#endif + +#ifdef _DEBUG + +protected: + UTSemReadWrite * dbg_m_pLock; + +public: + // Checks that MetaData is locked for write operation (if thread-safety is enabled and the lock exists) + void Debug_CheckIsLockedForWrite(); + + void Debug_SetLock(UTSemReadWrite * pLock) + { + dbg_m_pLock = pLock; + } + +#endif //_DEBUG + +public: + + FilterTable *GetFilterTable(); + __checkReturn HRESULT UnmarkAll(); + __checkReturn HRESULT MarkAll(); + + FORCEINLINE IHostFilter *GetHostFilter() { return m_pHostFilter;} + + __checkReturn HRESULT CalculateTypeRefToTypeDefMap(); + + FORCEINLINE TOKENMAP *GetTypeRefToTypeDefMap() + { return m_pTokenRemapManager ? m_pTokenRemapManager->GetTypeRefToTypeDefMap() : NULL; }; + + FORCEINLINE TOKENMAP *GetMemberRefToMemberDefMap() + { return m_pTokenRemapManager ? m_pTokenRemapManager->GetMemberRefToMemberDefMap() : NULL; }; + + FORCEINLINE MDTOKENMAP *GetTokenMovementMap() + { return m_pTokenRemapManager ? m_pTokenRemapManager->GetTokenMovementMap() : NULL; }; + + FORCEINLINE TokenRemapManager *GetTokenRemapManager() { return m_pTokenRemapManager; }; + + __checkReturn HRESULT InitTokenRemapManager(); + + virtual ULONG vGetCol(ULONG ixTbl, ULONG ixCol, void *pRecord) + { return GetCol(ixTbl, ixCol, pRecord);} + +public: + virtual BOOL IsWritable() + { + return !m_fIsReadOnly; + } + + + //************************************************************************* + // Delta MetaData (EditAndContinue) functions. +public: + enum eDeltaFuncs{ + eDeltaFuncDefault = 0, + eDeltaMethodCreate, + eDeltaFieldCreate, + eDeltaParamCreate, + eDeltaPropertyCreate, + eDeltaEventCreate, + }; + + __checkReturn HRESULT ApplyDelta(CMiniMdRW &mdDelta); + +public: + // Functions for updating ENC log tables ENC log. + FORCEINLINE BOOL IsENCOn() + { + return (m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateENC; + } + + __checkReturn + FORCEINLINE HRESULT UpdateENCLog(mdToken tk, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault) + { + if (IsENCOn()) + return UpdateENCLogHelper(tk, funccode); + else + return S_OK; + } + + __checkReturn + FORCEINLINE HRESULT UpdateENCLog2(ULONG ixTbl, ULONG iRid, CMiniMdRW::eDeltaFuncs funccode = CMiniMdRW::eDeltaFuncDefault) + { + if (IsENCOn()) + return UpdateENCLogHelper2(ixTbl, iRid, funccode); + else + return S_OK; + } + + __checkReturn HRESULT ResetENCLog(); + +private: + BOOL m_fMinimalDelta; + + // + // String heap reorganization + // + + // Check to see if it is safe to reorder the string pool. + BOOL IsSafeToReorderStringPool(); + // Function to mark hot strings in the marks array based on the token information in profile data. + VOID MarkHotStrings(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize); + // Function to mark hot strings referenced by hot tables based on token information in profile data. + VOID MarkStringsInHotTables(CorProfileData *pProfileData, BYTE * pMarks, ULONG poolSize); + // Function to mark strings referenced by the different metadata tables. + VOID MarkStringsInTables(BYTE * pMarks, ULONG poolSize); + // Function to mark duplicate strings in the mark array. + // Throws on error. + VOID MarkDuplicateStrings(BYTE * pMarks, ULONG poolSize); + // Function to update the tables with the modified string offsets. + VOID FixStringsInTables(); + // Function to fill the given string pool with strings from the existing string pool using the mark array. + // Throws on error. + VOID CreateReorderedStringPool( + MetaData::StringHeapRW *pStringHeap, + BYTE *pMarks, + ULONG cbHeapSize, + CorProfileData *pProfileData); + +public: + BOOL IsMinimalDelta() + { + return m_fMinimalDelta; + } + + + // Turns on/off the ability to emit delta metadatas + + // Unfortunately, we can't allow this to be set via the SetOption method anymore. In v1.0 and v1.1, this flag + // could be set but would still result in generating full metadatas. We can't automatically start generating + // true deltas for people... it could break them. + void EnableDeltaMetadataGeneration() + { + _ASSERTE(m_OptionValue.m_UpdateMode == MDUpdateENC); + } + void DisableDeltaMetadataGeneration() {m_OptionValue.m_UpdateMode = MDUpdateENC;} + +protected: + // Internal Helper functions for ENC log. + __checkReturn + HRESULT UpdateENCLogHelper(mdToken tk, CMiniMdRW::eDeltaFuncs funccode); + __checkReturn + HRESULT UpdateENCLogHelper2(ULONG ixTbl, ULONG iRid, CMiniMdRW::eDeltaFuncs funccode); + +protected: + static ULONG m_TruncatedEncTables[]; + static ULONG m_SuppressedDeltaColumns[TBL_COUNT]; + + ULONGARRAY *m_rENCRecs; // Array of RIDs affected by ENC. + + __checkReturn + HRESULT ApplyRecordDelta(CMiniMdRW &mdDelta, ULONG ixTbl, void *pDelta, void *pRecord); + __checkReturn + HRESULT ApplyTableDelta(CMiniMdRW &mdDelta, ULONG ixTbl, RID iRid, int fc); + __checkReturn + HRESULT GetDeltaRecord(ULONG ixTbl, ULONG iRid, void **ppRecord); + __checkReturn + HRESULT ApplyHeapDeltas(CMiniMdRW &mdDelta); + __checkReturn + HRESULT ApplyHeapDeltasWithMinimalDelta(CMiniMdRW &mdDelta); + __checkReturn + HRESULT ApplyHeapDeltasWithFullDelta(CMiniMdRW &mdDelta); + __checkReturn + HRESULT StartENCMap(); // Call, on a delta MD, to prepare to access sparse rows. + __checkReturn + HRESULT EndENCMap(); // Call, on a delta MD, when done with sparse rows. + +public: + // Workaround for compiler performance issue VSW 584653 for 2.0 RTM. + // Get the table's VirtualSort validity state. + bool IsTableVirtualSorted(ULONG ixTbl); + // Workaround for compiler performance issue VSW 584653 for 2.0 RTM. + // Validate table's VirtualSort after adding one record into the table. + // Returns new VirtualSort validity state in *pfIsTableVirtualSortValid. + // Assumptions: + // Table's VirtualSort was valid before adding the record to the table. + // The caller must ensure validity of VirtualSort by calling to + // IsTableVirtualSorted or by using the returned state from previous + // call to this method. + __checkReturn + HRESULT ValidateVirtualSortAfterAddRecord( + ULONG ixTbl, + bool * pfIsTableVirtualSortValid); + +}; // class CMiniMdRW : public CMiniMdTemplate<CMiniMdRW> + +#endif // _METAMODELRW_H_ diff --git a/src/coreclr/md/inc/pdbheap.h b/src/coreclr/md/inc/pdbheap.h new file mode 100644 index 00000000000..92cc3b24494 --- /dev/null +++ b/src/coreclr/md/inc/pdbheap.h @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _PDBHEAP_H_ +#define _PDBHEAP_H_ + +#if _MSC_VER >= 1100 +#pragma once +#endif + +#include "metamodel.h" +#include "portablepdbmdds.h" + +/* Simple storage class (similar to StgPool) holding pdbstream data +** for portable PDB metadata. +*/ +class PdbHeap +{ +public: + PdbHeap(); + ~PdbHeap(); + + __checkReturn HRESULT SetData(PORT_PDB_STREAM* data); + __checkReturn HRESULT SaveToStream(IStream* stream); + BOOL IsEmpty(); + ULONG GetSize(); + +private: + BYTE* m_data; + ULONG m_size; +}; + +#endif diff --git a/src/coreclr/md/inc/portablepdbmdds.h b/src/coreclr/md/inc/portablepdbmdds.h new file mode 100644 index 00000000000..10c82d3dba3 --- /dev/null +++ b/src/coreclr/md/inc/portablepdbmdds.h @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/***************************************************************************** + ** ** + ** portablepdbmdds.h - contains data structures and types used for ** + ** portable PDB metadata generation. ** + ** ** + *****************************************************************************/ + +#ifndef _PORTABLEPDBMDDS_H_ +#define _PORTABLEPDBMDDS_H_ + +#if _MSC_VER >= 1100 +#pragma once +#endif + +#include "corhdr.h" + +//------------------------------------- +//--- PDB stream data structure +//------------------------------------- +typedef struct _PDB_ID +{ + GUID pdbGuid; + ULONG pdbTimeStamp; +} PDB_ID; + +typedef struct _PORT_PDB_STREAM +{ + PDB_ID id; + mdMethodDef entryPoint; + ULONG64 referencedTypeSystemTables; + ULONG *typeSystemTableRows; + ULONG typeSystemTableRowsSize; +} PORT_PDB_STREAM; + +//------------------------------------- +//--- Portable PDB table tokens +//------------------------------------- + +// PPdb token definitions +typedef mdToken mdDocument; +typedef mdToken mdMethodDebugInformation; +typedef mdToken mdLocalScope; +typedef mdToken mdLocalVariable; +typedef mdToken mdLocalConstant; +typedef mdToken mdImportScope; +// TODO: +// typedef mdToken mdStateMachineMethod; +// typedef mdToken mdCustomDebugInformation; + +// PPdb token tags +typedef enum PPdbCorTokenType +{ + mdtDocument = 0x30000000, + mdtMethodDebugInformation = 0x31000000, + mdtLocalScope = 0x32000000, + mdtLocalVariable = 0x33000000, + mdtLocalConstant = 0x34000000, + mdtImportScope = 0x35000000, + // TODO: + // mdtStateMachineMethod = 0x36000000, + // mdtCustomDebugInformation = 0x37000000, +} PPdbCorTokenType; + +// PPdb Nil tokens +#define mdDocumentNil ((mdDocument)mdtDocument) +#define mdMethodDebugInformationNil ((mdMethodDebugInformation)mdtMethodDebugInformation) +#define mdLocalScopeNil ((mdLocalScope)mdtLocalScope) +#define mdLocalVariableNil ((mdLocalVariable)mdtLocalVariable) +#define mdLocalConstantNil ((mdLocalConstant)mdtLocalConstant) +#define mdImportScopeNil ((mdImportScope)mdtImportScope) +// TODO: +// #define mdStateMachineMethodNil ((mdStateMachineMethod)mdtStateMachineMethod) +// #define mdCustomDebugInformationNil ((mdCustomDebugInformation)mdtCustomDebugInformation) + +#endif // _PORTABLEPDBMDDS_H_ diff --git a/src/coreclr/md/inc/portablepdbmdi.h b/src/coreclr/md/inc/portablepdbmdi.h new file mode 100644 index 00000000000..d1785bca193 --- /dev/null +++ b/src/coreclr/md/inc/portablepdbmdi.h @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/***************************************************************************** + ** ** + ** portablepdbmdi.h - contains COM interface definitions for portable PDB ** + ** metadata generation. ** + ** ** + *****************************************************************************/ + +#ifndef _PORTABLEPDBMDI_H_ +#define _PORTABLEPDBMDI_H_ + +#if _MSC_VER >= 1100 +#pragma once +#endif + +#include "cor.h" +#include "portablepdbmdds.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------- +//--- IMetaDataEmit3 +//------------------------------------- +// {1a5abcd7-854e-4f07-ace4-3f09e6092939} +EXTERN_GUID(IID_IMetaDataEmit3, 0x1a5abcd7, 0x854e, 0x4f07, 0xac, 0xe4, 0x3f, 0x09, 0xe6, 0x09, 0x29, 0x39); + +//--- +#undef INTERFACE +#define INTERFACE IMetaDataEmit3 +DECLARE_INTERFACE_(IMetaDataEmit3, IMetaDataEmit2) +{ + + STDMETHOD(GetReferencedTypeSysTables)( // S_OK or error. + ULONG64 *refTables, // [OUT] Bit vector of referenced type system metadata tables. + ULONG refTableRows[], // [OUT] Array of number of rows for each referenced type system table. + const ULONG maxTableRowsSize, // [IN] Max size of the rows array. + ULONG *tableRowsSize) PURE; // [OUT] Actual size of the rows array. + + STDMETHOD(DefinePdbStream)( // S_OK or error. + PORT_PDB_STREAM *pdbStream) PURE; // [IN] Portable pdb stream data. + + STDMETHOD(DefineDocument)( // S_OK or error. + char *docName, // [IN] Document name (string will be tokenized). + GUID *hashAlg, // [IN] Hash algorithm GUID. + BYTE *hashVal, // [IN] Hash value. + ULONG hashValSize, // [IN] Hash value size. + GUID *lang, // [IN] Language GUID. + mdDocument *docMdToken) PURE; // [OUT] Token of the defined document. + + STDMETHOD(DefineSequencePoints)( // S_OK or error. + ULONG docRid, // [IN] Document RID. + BYTE *sequencePtsBlob, // [IN] Sequence point blob. + ULONG sequencePtsBlobSize) PURE; // [IN] Sequence point blob size. + + STDMETHOD(DefineLocalScope)( // S_OK or error. + ULONG methodDefRid, // [IN] Method RID. + ULONG importScopeRid, // [IN] Import scope RID. + ULONG firstLocalVarRid, // [IN] First local variable RID (of the continous run). + ULONG firstLocalConstRid, // [IN] First local constant RID (of the continous run). + ULONG startOffset, // [IN] Start offset of the scope. + ULONG length) PURE; // [IN] Scope length. + + STDMETHOD(DefineLocalVariable)( // S_OK or error. + USHORT attribute, // [IN] Variable attribute. + USHORT index, // [IN] Variable index (slot). + char *name, // [IN] Variable name. + mdLocalVariable *locVarToken) PURE; // [OUT] Token of the defined variable. +}; + +//------------------------------------- +//--- IMetaDataDispenserEx2 +//------------------------------------- + +// {23aaef0d-49bf-43f0-9744-1c3e9c56322a} +EXTERN_GUID(IID_IMetaDataDispenserEx2, 0x23aaef0d, 0x49bf, 0x43f0, 0x97, 0x44, 0x1c, 0x3e, 0x9c, 0x56, 0x32, 0x2a); + +#undef INTERFACE +#define INTERFACE IMetaDataDispenserEx2 +DECLARE_INTERFACE_(IMetaDataDispenserEx2, IMetaDataDispenserEx) +{ + STDMETHOD(DefinePortablePdbScope)( // Return code. + REFCLSID rclsid, // [IN] What version to create. + DWORD dwCreateFlags, // [IN] Flags on the create. + REFIID riid, // [IN] The interface desired. + IUnknown * *ppIUnk) PURE; // [OUT] Return interface on success. +}; + +#ifdef __cplusplus +} +#endif + +#endif // _PORTABLEPDBMDI_H_ diff --git a/src/coreclr/md/inc/recordpool.h b/src/coreclr/md/inc/recordpool.h new file mode 100644 index 00000000000..cd907776eeb --- /dev/null +++ b/src/coreclr/md/inc/recordpool.h @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RecordPool.h -- header file for record heaps. +// + +// +//***************************************************************************** +#ifndef _RECORDPOOL_H_ +#define _RECORDPOOL_H_ + +#if _MSC_VER >= 1100 +#pragma once +#endif + +#include <stgpool.h> + +//***************************************************************************** +// This Record pool class collects user Records into a big consecutive heap. +// The list of Records is kept in memory while adding, and +// finally flushed to a stream at the caller's request. +//***************************************************************************** +class RecordPool : public StgPool +{ + friend class VerifyLayoutsMD; + + using StgPool::InitNew; + using StgPool::InitOnMem; + +public: + RecordPool() : + StgPool(1024, 1) + { } + +//***************************************************************************** +// Init the pool for use. This is called for the create empty case. +//***************************************************************************** + __checkReturn + HRESULT InitNew( + UINT32 cbRec, // Record size. + UINT32 cRecsInit); // Initial guess of count of record. + +//***************************************************************************** +// Load a Record heap from persisted memory. If a copy of the data is made +// (so that it may be updated), then a new hash table is generated which can +// be used to elminate duplicates with new Records. +//***************************************************************************** + __checkReturn + HRESULT InitOnMem( + ULONG cbRec, // Record size. + void *pData, // Predefined data. + ULONG iSize, // Size of data. + BOOL fReadOnly); // true if append is forbidden. + +//***************************************************************************** +// Allocate memory if we don't have any, or grow what we have. If successful, +// then at least iRequired bytes will be allocated. +//***************************************************************************** + bool Grow( // true if successful. + ULONG iRequired); // Min required bytes to allocate. + +//***************************************************************************** +// The Record will be added to the pool. The index of the Record in the pool +// is returned in *piIndex. If the Record is already in the pool, then the +// index will be to the existing copy of the Record. +//***************************************************************************** + HRESULT AddRecord( + BYTE **ppRecord, + UINT32 *pnIndex); // Return 1-based index of Record here. + +//***************************************************************************** +// Insert a Record into the pool. The index of the Record before which to +// insert is specified. Shifts all records down. Return a pointer to the +// new record. +//***************************************************************************** + HRESULT InsertRecord( + UINT32 nIndex, // [IN] Insert record before this. + BYTE **ppRecord); + +//***************************************************************************** +// Return a pointer to a Record given an index previously handed out by +// AddRecord or FindRecord. +//***************************************************************************** + __checkReturn + virtual HRESULT GetRecord( + UINT32 nIndex, // 1-based index of Record in pool. + BYTE **ppRecord); + +//***************************************************************************** +// Given a pointer to a record, determine the index corresponding to the +// record. +//***************************************************************************** + virtual ULONG GetIndexForRecord( // 1-based index of Record in pool. + const void *pRecord); // Pointer to Record in pool. + +//***************************************************************************** +// Given a purported pointer to a record, determine if the pointer is valid. +//***************************************************************************** + virtual int IsValidPointerForRecord( // true or false. + const void *pRecord); // Pointer to Record in pool. + +//***************************************************************************** +// How many objects are there in the pool? If the count is 0, you don't need +// to persist anything at all to disk. +//***************************************************************************** + UINT32 Count() + { return GetNextOffset() / m_cbRec; } + +//***************************************************************************** +// Indicate if heap is empty. This has to be based on the size of the data +// we are keeping. If you open in r/o mode on memory, there is no hash +// table. +//***************************************************************************** + virtual int IsEmpty() // true if empty. + { return (GetNextOffset() == 0); } + +//***************************************************************************** +// Is the index valid for the Record? +//***************************************************************************** + virtual int IsValidCookie(ULONG ulCookie) + { return (ulCookie == 0 || IsValidOffset((ulCookie-1) * m_cbRec)); } + +//***************************************************************************** +// Return the size of the heap. +//***************************************************************************** + ULONG GetNextIndex() + { return (GetNextOffset() / m_cbRec); } + +//***************************************************************************** +// Replace the contents of this pool with those from another pool. The other +// pool loses ownership of the memory. +//***************************************************************************** + __checkReturn + HRESULT ReplaceContents( + RecordPool *pOther); // The other record pool. + +//***************************************************************************** +// Return the first record in a pool, and set up a context for fast +// iterating through the pool. Note that this scheme does pretty minimal +// error checking. +//***************************************************************************** + void *GetFirstRecord( // Pointer to Record in pool. + void **pContext); // Store context here. + +//***************************************************************************** +// Given a pointer to a record, return a pointer to the next record. +// Note that this scheme does pretty minimal error checking. In particular, +// this will let the caller walk off of the end of valid data in the last +// segment. +//***************************************************************************** + void *GetNextRecord( // Pointer to Record in pool. + void *pRecord, // Current record. + void **pContext); // Stored context here. + +private: + DAC_ALIGNAS(StgPool) // Align first member to alignment of base class + UINT32 m_cbRec; // How large is each record? + +}; // class RecordPool + +#endif // _RECORDPOOL_H_ diff --git a/src/coreclr/md/inc/rwutil.h b/src/coreclr/md/inc/rwutil.h new file mode 100644 index 00000000000..16e98913a51 --- /dev/null +++ b/src/coreclr/md/inc/rwutil.h @@ -0,0 +1,344 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RWUtil.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __RWUtil__h__ +#define __RWUtil__h__ + +class UTSemReadWrite; + +#define UTF8STR(wszInput, szOutput) \ + do { \ + if ((wszInput) == NULL) \ + { \ + (szOutput) = NULL; \ + } \ + else \ + { \ + int cbBuffer = ((int)wcslen(wszInput) * 3) + 1; \ + (szOutput) = (char *)_alloca(cbBuffer); \ + Unicode2UTF((wszInput), (szOutput), cbBuffer); \ + } \ + } while (0) + +//***************************************************************************** +// Helper methods +//***************************************************************************** +void +Unicode2UTF( + LPCWSTR wszSrc, // The string to convert. + __out_ecount(cbDst) + LPUTF8 szDst, // Buffer for the output UTF8 string. + int cbDst); // Size of the buffer for UTF8 string. + +//********************************************************************* +// The token remap record. +//********************************************************************* +struct TOKENREC +{ + mdToken m_tkFrom; // The imported token + bool m_isDuplicate; // Is record duplicate? This information is recorded during merge + bool m_isDeleted; // This information is recorded during RegMeta::ProcessFilter when we might have deleted a record + bool m_isFoundInImport; // This information is also recorded during RegMeta::ProcessFilter + mdToken m_tkTo; // The new token in the merged scope + + void SetEmpty() {m_tkFrom = m_tkTo = (mdToken) -1;} + BOOL IsEmpty() {return m_tkFrom == (mdToken) -1;} +}; + + +//********************************************************************* +// +// This structure keeps track on token remap for an imported scope. This map is initially sorted by from +// tokens. It can then become sorted by To tokens. This usually happen during PreSave remap lookup. Thus +// we assert if we try to look up or sort by From token. +// +//********************************************************************* +class MDTOKENMAP : public CDynArray<TOKENREC> +{ +public: + + enum SortKind{ + Unsorted = 0, + SortByFromToken = 1, + SortByToToken = 2, + Indexed = 3, // Indexed by table/rid. Implies that strings are sorted by "From". + }; + + MDTOKENMAP() + : m_pNextMap(NULL), + m_pMap(NULL), + m_iCountTotal(0), + m_iCountSorted(0), + m_sortKind(SortByFromToken), + m_iCountIndexed(0) +#if defined(_DEBUG) + ,m_pImport(0) +#endif + { } + ~MDTOKENMAP(); + + HRESULT Init(IUnknown *pImport); + + // find a token in the tokenmap. + bool Find(mdToken tkFrom, TOKENREC **ppRec); + + // remap a token. We assert if we don't find the tkFind in the table + HRESULT Remap(mdToken tkFrom, mdToken *ptkTo); + + // Insert a record. This function will keep the inserted record in a sorted sequence + HRESULT InsertNotFound(mdToken tkFrom, bool fDuplicate, mdToken tkTo, TOKENREC **ppRec); + + // This function will just append the record to the end of the list + HRESULT AppendRecord( + mdToken tkFrom, + bool fDuplicate, + mdToken tkTo, + TOKENREC **ppRec); + + // This is a safe remap. *tpkTo will be tkFind if we cannot find tkFind in the lookup table. + mdToken SafeRemap(mdToken tkFrom); // [IN] the token value to find + + bool FindWithToToken( + mdToken tkFind, // [IN] the token value to find + int *piPosition); // [OUT] return the first from-token that has the matching to-token + + FORCEINLINE void SortTokensByFromToken() + { + _ASSERTE(m_sortKind == SortByFromToken || m_sortKind == Indexed); + // Only sort if there are unsorted records. + if (m_iCountSorted < m_iCountTotal) + { + SortRangeFromToken(m_iCountIndexed, m_iCountIndexed+m_iCountTotal - 1); + m_iCountSorted = m_iCountTotal; + } + } // void MDTOKENMAP::SortTokensByFromToken() + + HRESULT EmptyMap(); + + void SortTokensByToToken(); + + MDTOKENMAP *m_pNextMap; + IMapToken *m_pMap; + +private: + FORCEINLINE int CompareFromToken( // -1, 0, or 1 + int iLeft, // First item to compare. + int iRight) // Second item to compare. + { + if ( Get(iLeft)->m_tkFrom < Get(iRight)->m_tkFrom ) + return -1; + if ( Get(iLeft)->m_tkFrom == Get(iRight)->m_tkFrom ) + return 0; + return 1; + } + + FORCEINLINE int CompareToToken( // -1, 0, or 1 + int iLeft, // First item to compare. + int iRight) // Second item to compare. + { + if ( Get(iLeft)->m_tkTo < Get(iRight)->m_tkTo ) + return -1; + if ( Get(iLeft)->m_tkTo == Get(iRight)->m_tkTo ) + return 0; + return 1; + } + + FORCEINLINE void Swap( + int iFirst, + int iSecond) + { + if ( iFirst == iSecond ) return; + memcpy( &m_buf, Get(iFirst), sizeof(TOKENREC) ); + memcpy( Get(iFirst), Get(iSecond),sizeof(TOKENREC) ); + memcpy( Get(iSecond), &m_buf, sizeof(TOKENREC) ); + } + + void SortRangeFromToken(int iLeft, int iRight); + void SortRangeToToken(int iLeft, int iRight); + + TOKENREC m_buf; + ULONG m_iCountTotal; // total entry in the map + ULONG m_iCountSorted; // number of entries that are sorted + + SortKind m_sortKind; + + ULONG m_TableOffset[TBL_COUNT+1]; // Start of each table in map. + ULONG m_iCountIndexed; // number of entries that are indexed. +#if defined(_DEBUG) + IMetaDataImport *m_pImport; // For data validation. +#endif +}; + + + +//********************************************************************* +// +// This CMapToken class implemented the IMapToken. It is used in RegMeta for +// filter process. This class can track all of the tokens are mapped. It also +// supplies a Find function. +// +//********************************************************************* +class CMapToken : public IMapToken +{ + friend class RegMeta; + +public: + STDMETHODIMP QueryInterface(REFIID riid, PVOID *pp); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP Map(mdToken tkImp, mdToken tkEmit); + bool Find(mdToken tkFrom, TOKENREC **pRecTo); + CMapToken(); + virtual ~CMapToken(); + MDTOKENMAP *m_pTKMap; +private: + LONG m_cRef; + bool m_isSorted; +}; + +typedef CDynArray<mdToken> TOKENMAP; + +//********************************************************************* +// +// This class records all sorts of token movement during optimization phase. +// This including Ref to Def optimization. This also includes token movement +// due to sorting or eleminating the pointer tables. +// +//********************************************************************* +class TokenRemapManager +{ +public: + //********************************************************************* + // + // This function is called when a TypeRef is resolved to a TypeDef. + // + //********************************************************************* + FORCEINLINE void RecordTypeRefToTypeDefOptimization( + mdToken tkFrom, + mdToken tkTo) + { + _ASSERTE( TypeFromToken(tkFrom) == mdtTypeRef ); + _ASSERTE( TypeFromToken(tkTo) == mdtTypeDef ); + + m_TypeRefToTypeDefMap[RidFromToken(tkFrom)] = tkTo; + } // RecordTypeRefToTypeDefOptimization + + + //********************************************************************* + // + // This function is called when a MemberRef is resolved to a MethodDef or FieldDef. + // + //********************************************************************* + FORCEINLINE void RecordMemberRefToMemberDefOptimization( + mdToken tkFrom, + mdToken tkTo) + { + _ASSERTE( TypeFromToken(tkFrom) == mdtMemberRef ); + _ASSERTE( TypeFromToken(tkTo) == mdtMethodDef || TypeFromToken(tkTo) == mdtFieldDef); + + m_MemberRefToMemberDefMap[RidFromToken(tkFrom)] = tkTo; + } // RecordMemberRefToMemberDefOptimization + + //********************************************************************* + // + // This function is called when the token kind does not change but token + // is moved. For example, when we sort CustomAttribute table or when we optimize + // away MethodPtr table. These operation will not change the token type. + // + //********************************************************************* + FORCEINLINE HRESULT RecordTokenMovement( + mdToken tkFrom, + mdToken tkTo) + { + TOKENREC *pTokenRec; + + _ASSERTE( TypeFromToken(tkFrom) == TypeFromToken(tkTo) ); + return m_TKMap.AppendRecord( tkFrom, false, tkTo, &pTokenRec ); + } // RecordTokenMovement + + bool ResolveRefToDef( + mdToken tkRef, // [IN] ref token + mdToken *ptkDef); // [OUT] def token that it resolves to. If it does not resolve to a def + + FORCEINLINE TOKENMAP *GetTypeRefToTypeDefMap() { return &m_TypeRefToTypeDefMap; } + FORCEINLINE TOKENMAP *GetMemberRefToMemberDefMap() { return &m_MemberRefToMemberDefMap; } + FORCEINLINE MDTOKENMAP *GetTokenMovementMap() { return &m_TKMap; } + + ~TokenRemapManager(); + HRESULT ClearAndEnsureCapacity(ULONG cTypeRef, ULONG cMemberRef); +private: + MDTOKENMAP m_TKMap; + TOKENMAP m_TypeRefToTypeDefMap; + TOKENMAP m_MemberRefToMemberDefMap; +}; // class TokenRemapManager + +// value that can be set by SetOption APIs +struct OptionValue +{ + CorCheckDuplicatesFor m_DupCheck; // Bit Map for checking duplicates during emit. + CorRefToDefCheck m_RefToDefCheck; // Bit Map for specifying whether to do a ref to def optimization. + CorNotificationForTokenMovement m_NotifyRemap; // Bit Map for token remap notification. + ULONG m_UpdateMode; // (CorSetENC) Specifies whether ENC or Extension mode is on. + CorErrorIfEmitOutOfOrder m_ErrorIfEmitOutOfOrder; // Do not generate pointer tables + CorThreadSafetyOptions m_ThreadSafetyOptions; // specify if thread safety is turn on or not. + CorImportOptions m_ImportOption; // import options such as to skip over deleted items or not + CorLinkerOptions m_LinkerOption; // Linker option. Currently only used in UnmarkAll + BOOL m_GenerateTCEAdapters; // Do not generate the TCE adapters for COM CPC. + LPSTR m_RuntimeVersion; // CLR Version stamp + MetadataVersion m_MetadataVersion; // Version of the metadata to emit + MergeFlags m_MergeOptions; // Options to pass to the merger + UINT32 m_InitialSize; // Initial size of MetaData with values: code:CorMetaDataInitialSize. + CorLocalRefPreservation m_LocalRefPreservation; // Preserve module-local refs instead of optimizing them to defs +}; // struct OptionValue + +//********************************************************************* +// +// Helper class to ensure calling UTSemReadWrite correctly. +// The destructor will call the correct UnlockRead or UnlockWrite depends what lock it is holding. +// User should use macro defined in below instead of calling functions on this class directly. +// They are LOCKREAD(), LOCKWRITE(), and CONVERT_READ_TO_WRITE_LOCK. +// +//********************************************************************* +class CMDSemReadWrite +{ +public: + CMDSemReadWrite(UTSemReadWrite *pSem); + ~CMDSemReadWrite(); + HRESULT LockRead(); + HRESULT LockWrite(); + void UnlockWrite(); + HRESULT ConvertReadLockToWriteLock(); +private: + bool m_fLockedForRead; + bool m_fLockedForWrite; + UTSemReadWrite *m_pSem; +}; + + +#define LOCKREADIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailRet(cSem.LockRead()); +#define LOCKWRITEIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailRet(cSem.LockWrite()); + +#define LOCKREADNORET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + hr = cSem.LockRead(); +#define LOCKWRITENORET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + hr = cSem.LockWrite(); + +#define LOCKREAD() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailGo(cSem.LockRead()); +#define LOCKWRITE() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailGo(cSem.LockWrite()); + +#define UNLOCKWRITE() cSem.UnlockWrite(); +#define CONVERT_READ_TO_WRITE_LOCK() IfFailGo(cSem.ConvertReadLockToWriteLock()); + + +#endif // __RWUtil__h__ diff --git a/src/coreclr/md/inc/stgio.h b/src/coreclr/md/inc/stgio.h new file mode 100644 index 00000000000..cb66cb5594d --- /dev/null +++ b/src/coreclr/md/inc/stgio.h @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// StgIO.h +// + +// +// This module handles disk/memory i/o for a generic set of storage solutions, +// including: +// * File system handle (HFILE) +// * IStream +// * User supplied memory buffer (non-movable) +// +// The Read, Write, Seek, ... functions are all directed to the corresponding +// method for each type of file, allowing the consumer to use one set of api's. +// +// File system data can be paged fully into memory in two scenarios: +// read: Normal memory mapped file is created to manage paging. +// write: A custom paging system provides storage for pages as required. This +// data is invalidated when you call Rewrite on the file. +// +// Transactions and backups are handled in the existing file case only. The +// Rewrite function can make a backup of the current contents, and the Restore +// function can be used to recover the data into the current scope. The backup +// file is flushed to disk (which is slower but safer) after the copy. The +// Restore also flushed the recovered changes to disk. Worst case scenario you +// get a crash after calling Rewrite but before Restore, in which case you will +// have a foo.clb.txn file in the same directory as the source file, foo.clb in +// this example. +//<TODO> +// @FUTURE: issues, +// 1. For reading a .clb in an image, it would be great to memory map +// only the portion of the file with the .clb in it. +//</TODO> +//***************************************************************************** +#ifndef __STGIO_H_ +#define __STGIO_H_ + +#define MAXSHMEM 32 + +#define STGIO_READ 0x1 +#define STGIO_WRITE 0x2 + +enum DBPROPMODE + { DBPROP_TMODEF_READ = 0x1, + DBPROP_TMODEF_WRITE = 0x2, + DBPROP_TMODEF_EXCLUSIVE = 0x4, + // Shared memory uses ole32.dll - we cannot depend on it in the standalone WinRT Read-Only DLL + DBPROP_TMODEF_SHAREDMEM = 0x8, + DBPROP_TMODEF_CREATE = 0x10, + DBPROP_TMODEF_FAILIFTHERE = 0x20, + DBPROP_TMODEF_SLOWSAVE = 0x100, + // Means it is OK to use LoadLibrary to map the file. Used by code:ofTrustedImage. + // We prefer that because it is shared with loader's image loading. + DBPROP_TMODEF_TRYLOADLIBRARY = 0x400, +#if 0 // dead code + DBPROP_TMODEF_NOTXNBACKUPFILE = 0x200, + DBPROP_TMODEF_COMPLUS = 0x1000, + DBPROP_TMODEF_SMEMCREATE = 0x2000, + DBPROP_TMODEF_SMEMOPEN = 0x4000, + DBPROP_TMODEF_ALIGNBLOBS = 0x10000 + DBPROP_TMODEF_RESERVED = 0x80000000, +#endif + DBPROP_TMODEF_DFTWRITEMASK = 0x113, + DBPROP_TMODEF_DFTREADWRITEMASK = 0x103, + }; + + +// Types of IO we can handle. +enum STGIOTYPE +{ + STGIO_NODATA = 0, // Currently not open. + STGIO_HFILE = 1, // File handle contains data. + STGIO_HMODULE = 2, // The file was loaded via LoadLibrary as module. + STGIO_STREAM = 3, // Stream pointer has data. + STGIO_MEM = 4, // In memory pointer has data. + // Shared memory uses ole32.dll - we cannot depend on it in the standalone WinRT Read-Only DLL + STGIO_SHAREDMEM = 5, // Shared memory handle. + STGIO_HFILEMEM = 6 // Handle open, but memory allocated. +}; + +class StgIO +{ + friend class CLiteWeightStgdbRW; // for low-level access to data for metainfo and such. + friend class TiggerStorage; +public: + StgIO( + bool bAutoMap=true); // Memory map for read on open? + + ~StgIO(); + +//***************************************************************************** +// Open the base file on top of: (a) file, (b) memory buffer, or (c) stream. +// If create flag is specified, then this will create a new file with the +// name supplied. No data is read from an opened file. You must call +// MapFileToMem before doing direct pointer access to the contents. +//***************************************************************************** + HRESULT Open( // Return code. + LPCWSTR szName, // Name of the storage. + int fFlags, // How to open the file. + const void *pbBuff, // Optional buffer for memory. + ULONG cbBuff, // Size of buffer. + IStream *pIStream, // Stream for input. + LPSECURITY_ATTRIBUTES pAttributes); // Security token. + +//***************************************************************************** +// Shut down the file handles and allocated objects. +//***************************************************************************** + void Close(); + +//***************************************************************************** +// Read data from the storage source. This will handle all types of backing +// storage from mmf, streams, and file handles. No read ahead or MRU +// caching is done. +//***************************************************************************** + HRESULT Read( // Return code. + void *pbBuff, // Write buffer here. + ULONG cbBuff, // How much to read. + ULONG *pcbRead); // How much read. + +//***************************************************************************** +// Write to disk. This function will cache up to a page of data in a buffer +// and peridocially flush it on overflow and explicit request. This makes it +// safe to do lots of small writes without too much performance overhead. +//***************************************************************************** + HRESULT Write( // Return code. + const void *pbBuff, // Buffer to write. + ULONG cbWrite, // How much. + ULONG *pcbWritten); // Return how much written. + +//***************************************************************************** +// Moves the file pointer to the new location. This handles the different +// types of storage systems. +//***************************************************************************** + HRESULT Seek( // New offset. + int lVal, // How much to move. + ULONG fMoveType); // Direction, use Win32 FILE_xxxx. + +//***************************************************************************** +// Retrieves the current offset for the storage being used. This value is +// tracked based on Read, Write, and Seek operations. +//***************************************************************************** + ULONG GetCurrentOffset(); // Current offset. + +//***************************************************************************** +// Map the file contents to a memory mapped file and return a pointer to the +// data. For read/write with a backing store, map the file using an internal +// paging system. +//***************************************************************************** + HRESULT MapFileToMem( // Return code. + void *&ptr, // Return pointer to file data. + ULONG *pcbSize, // Return size of data. + LPSECURITY_ATTRIBUTES pAttributes=0); // Security token. + +//***************************************************************************** +// Free the mapping object for shared memory but keep the rest of the internal +// state intact. +//***************************************************************************** + HRESULT ReleaseMappingObject(); // Return code. + +//***************************************************************************** +// Resets the logical base address and size to the value given. This is for +// cases like finding a section embedded in another format, like the .clb inside +// of an image. GetPtrForMem, Read, and Seek will then behave as though only +// data from pbStart to cbSize is valid. +//***************************************************************************** + HRESULT SetBaseRange( // Return code. + void *pbStart, // Start of file data. + ULONG cbSize); // How big is the range. + +//***************************************************************************** +// For read/write case, get a pointer to a chunk of the file at cbStart for +// size cbSize. Return the pointer. This will page in parts of the file from +// disk if not already loaded. +//***************************************************************************** + HRESULT GetPtrForMem( // Return code. + ULONG cbStart, // Offset from beginning to load. + ULONG cbSize, // How much, rounded to page. + void *&ptr); // Return pointer on success. + +//***************************************************************************** +// For cached writes, flush the cache to the data store. +//***************************************************************************** + HRESULT FlushCache(); + +//***************************************************************************** +// Tells the file system to flush any cached data it may have. This is +// expensive, but if successful guarantees you won't lose writes short of +// a disk failure. +//***************************************************************************** + HRESULT FlushFileBuffers(); + +//***************************************************************************** +// Called after a successful rewrite of an existing file. The in memory +// backing store is no longer valid because all new data is in memory and +// on disk. This is essentially the same state as created, so free up some +// working set and remember this state. +//***************************************************************************** + HRESULT ResetBackingStore(); // Return code. + + FILETYPE GetFileType() + { return m_FileType; } + + int IsReadOnly() + { return ((m_fFlags & STGIO_WRITE) == 0); } + + ULONG GetFlags() + { return (m_fFlags); } + + ULONG SetFlags(ULONG fFlags) + { m_fFlags = fFlags; + return (m_fFlags); } + + ULONG GetDataSize() + { return (m_cbData); } + + LONG AddRef() + { + return (++m_cRef); + } + + LONG Release() + { + LONG cRef = --m_cRef; + if (cRef == 0) + delete this; + return (cRef); + } + + int IsAlignedPtr(ULONG_PTR Value, int iAlignment); + MAPPINGTYPE GetMemoryMappedType() + { return m_mtMappedType;} + + +//***************************************************************************** +// Called to read the data into allocated memory and release the backing store. +// Only available on read-only data. +//***************************************************************************** + HRESULT LoadFileToMemory(); + + +private: + int IsBackingStore() + { return (m_rgPageMap != 0); } + int IsMemoryMapped() + { return ((m_hMapping != NULL) || (m_hModule != NULL)); } + + void CtorInit(); + HRESULT WriteToDisk(const void *pbBuff, ULONG cbWrite, ULONG *pcbWritten); + HRESULT ReadFromDisk(void *pbBuff, ULONG cbBuff, ULONG *pcbRead); + HRESULT CopyFileInternal(LPCWSTR szTo, int bFailIfThere, int bWriteThrough); + void FreePageMap(); + +private: + + // Flags and state data. + FILETYPE m_FileType; // Cached type of the file (based on extension). + LONG m_cRef; // Ref count on this object. + bool m_bWriteThrough : 1; // true for write through mode. + bool m_bRewrite : 1; // State check for rewrite mode. + bool m_bAutoMap : 1; // true to automatically memory map file. + bool m_bFreeMem : 1; // true to free allocated memory. + + // Handles. + IStream * m_pIStream; // For save to stream instead of file. + HANDLE m_hFile; // The actual file with contents. + HANDLE m_hMapping; // Mapping handle. + HMODULE m_hModule; // If we load with LoadLibrary, this is the module (otherwise NULL). + void * m_pBaseData; // Base address for memory mapped file. + void * m_pData; // For memory mapped file read. + ULONG m_cbData; // Size of in memory data. + int m_fFlags; // Flags for open/create mode. + STGIOTYPE m_iType; // Where is the data. + MAPPINGTYPE m_mtMappedType; // How the file was memory mapped + + // File cache information. + BYTE * m_rgBuff; // Cache buffer for writing. + ULONG m_cbBuff; // Current cache size. + ULONG m_cbOffset; // Current offset in file. + + // Buffer read management. + static int m_iPageSize; // Size of an OS page. + static int m_iCacheSize; // How big a write back cache to use. + BYTE * m_rgPageMap; // Track loaded pages on read/write. + +}; // class StgIO + +#endif // __STGIO_H_ diff --git a/src/coreclr/md/inc/stgtiggerstorage.h b/src/coreclr/md/inc/stgtiggerstorage.h new file mode 100644 index 00000000000..d445d5ef300 --- /dev/null +++ b/src/coreclr/md/inc/stgtiggerstorage.h @@ -0,0 +1,327 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// StgTiggerStorage.h +// + +// +// TiggerStorage is a stripped down version of compound doc files. Doc files +// have some very useful and complex features to them, unfortunately nothing +// comes for free. Given the incredibly tuned format of existing .tlb files, +// every single byte counts and 10% added by doc files is just too exspensive. +// +// The storage itself is made up of a bunch of streams (each aligned to a 4 byte +// value), followed at the end of the file with the header. The header is +// put at the end so that you can continue to write as many streams as you +// like without thrashing the disk. +// +-------------------+ +// | Signature | +// +-------------------+ +// | Stream 1, 2, [] | +// +-------------------+ +// | STORAGEHEADER | +// | Extra data | +// | STORAGESTREAM[] | +// +-------------------+ +// | offset | +// +-------------------+ +// +// The STORAGEHEADER contains flags describing the rest of the file, including +// the ability to have extra data stored in the header. If there is extra +// data, then immediately after the STORAGEHEADER struct is a 4 byte size of +// that data, followed immediately by the extra data. The length must be +// 4 byte aligned so that the first STORAGESTREAM starts on an aligned +// boundary. The contents of the extra data is caller defined. +// +// This code handles the signature at the start of the file, and the list of +// streams at the end (kept in the header). The data in each stream is, of +// course, caller specific. +// +// This code requires the StgIO code to handle the input and output from the +// backing storage, whatever scheme that may be. There are no consistency +// checks on the data (for example crc's) due to the expense in computation +// required. There is a signature at the front of the file and in the header. +// +//***************************************************************************** +#ifndef __StgTiggerStorage_h__ +#define __StgTiggerStorage_h__ + +//#include "utilcode.h" // Helpers. + +#include "mdfileformat.h" + +typedef CDynArray<STORAGESTREAM> STORAGESTREAMLST; + + +// Forwards. +class TiggerStream; +class StgIO; + + + +class TiggerStorage : + public IStorage +{ +friend class TiggerStream; +public: + TiggerStorage(); + virtual ~TiggerStorage(); + +// IUnknown so you can ref count this thing. + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *pp) + { return (BadError(E_NOTIMPL)); } + virtual ULONG STDMETHODCALLTYPE AddRef() + { return (InterlockedIncrement(&m_cRef)); } + virtual ULONG STDMETHODCALLTYPE Release() + { + SUPPORTS_DAC_HOST_ONLY; + ULONG cRef; + if ((cRef = InterlockedDecrement(&m_cRef)) == 0) + delete this; + return (cRef); + } + + +//***************************************************************************** +// Init this storage object on top of the given storage unit. +//***************************************************************************** + HRESULT Init( // Return code. + StgIO *pStgIO, // The I/O subsystem. + __in __in_z LPSTR pVersion); // Compiler-supplied CLR version + +//***************************************************************************** +// Retrieves a the size and a pointer to the extra data that can optionally be +// written in the header of the storage system. This data is not required to +// be in the file, in which case *pcbExtra will come back as 0 and pbData will +// be set to null. You must have initialized the storage using Init() before +// calling this function. +//***************************************************************************** + HRESULT GetExtraData( // Return code. + ULONG *pcbExtra, // Return size of extra data. + BYTE *&pbData); // Return a pointer to extra data. + +//***************************************************************************** +// Flushes the header to disk. +//***************************************************************************** + HRESULT WriteHeader( // Return code. + STORAGESTREAMLST *pList, // List of streams. + ULONG cbExtraData, // Size of extra data, may be 0. + BYTE *pbExtraData); // Pointer to extra data for header. + +//***************************************************************************** +// Called when all data has been written. Forces cached data to be flushed +// and stream lists to be validated. +//***************************************************************************** + HRESULT WriteFinished( // Return code. + STORAGESTREAMLST *pList, // List of streams. + ULONG *pcbSaveSize, // Return size of total data. + BOOL fDeltaSave); // Was this a delta + +//***************************************************************************** +// Called after a successful rewrite of an existing file. The in memory +// backing store is no longer valid because all new data is in memory and +// on disk. This is essentially the same state as created, so free up some +// working set and remember this state. +//***************************************************************************** + HRESULT ResetBackingStore(); // Return code. + +//***************************************************************************** +// Called to restore the original file. If this operation is successful, then +// the backup file is deleted as requested. The restore of the file is done +// in write through mode to the disk help ensure the contents are not lost. +// This is not good enough to fulfill ACID props, but it ain't that bad. +//***************************************************************************** + HRESULT Restore( // Return code. + __in __in_z LPWSTR szBackup, // If non-0, backup the file. + int bDeleteOnSuccess); // Delete backup file if successful. + +//***************************************************************************** +// Given the name of a stream that will be persisted into a stream in this +// storage type, figure out how big that stream would be including the user's +// stream data and the header overhead the file format incurs. The name is +// stored in ANSI and the header struct is aligned to 4 bytes. +//***************************************************************************** + static HRESULT GetStreamSaveSize( // Return code. + LPCWSTR szStreamName, // Name of stream. + UINT32 cbDataSize, // Size of data to go into stream. + UINT32 *pcbSaveSize); // Return data size plus stream overhead. + +//***************************************************************************** +// Return the fixed size overhead for the storage implementation. This includes +// the signature and fixed header overhead. The overhead in the header for each +// stream is calculated as part of GetStreamSaveSize because these structs are +// variable sized on the name. +//***************************************************************************** + static HRESULT GetStorageSaveSize( // Return code. + ULONG *pcbSaveSize, // [in] current size, [out] plus overhead. + ULONG cbExtra, // How much extra data to store in header. + LPCSTR pRuntimeVersion); // The version string as it's length is part of the total size. + +//***************************************************************************** +// Adjust the offset in each known stream to match where it will wind up after +// a save operation. +//***************************************************************************** + static HRESULT CalcOffsets( // Return code. + STORAGESTREAMLST *pStreamList, // List of streams for header. + ULONG cbExtra, // Size of variable extra data in header. + LPCSTR pRuntimeVersion); // The version string as it's length is part of the total size. + + + +//***************************************************************************** +// Returns the size of the signature plus the verion information +//***************************************************************************** + static HRESULT SizeOfStorageSignature( + LPCSTR pRuntimeVersion, // The version string as it's length is part of the total size. + ULONG *pcbSignatureSize); + +// IStorage + virtual HRESULT STDMETHODCALLTYPE CreateStream( + const OLECHAR *pwcsName, + DWORD grfMode, + DWORD reserved1, + DWORD reserved2, + IStream **ppstm); + + virtual HRESULT STDMETHODCALLTYPE CreateStream( + LPCSTR szName, + DWORD grfMode, + DWORD reserved1, + DWORD reserved2, + IStream **ppstm) + DAC_UNEXPECTED(); + + virtual HRESULT STDMETHODCALLTYPE OpenStream( + const OLECHAR *pwcsName, + void *reserved1, + DWORD grfMode, + DWORD reserved2, + IStream **ppstm); + + virtual HRESULT STDMETHODCALLTYPE CreateStorage( + const OLECHAR *pwcsName, + DWORD grfMode, + DWORD dwStgFmt, + DWORD reserved2, + IStorage **ppstg); + + virtual HRESULT STDMETHODCALLTYPE OpenStorage( + const OLECHAR * wcsName, + IStorage * pStgPriority, + DWORD dwMode, + __in + SNB snbExclude, + DWORD reserved, + IStorage ** ppStg); + + virtual HRESULT STDMETHODCALLTYPE CopyTo( + DWORD cIidExclude, + const IID * rgIidExclude, + __in + SNB snbExclude, + IStorage * pStgDest); + + virtual HRESULT STDMETHODCALLTYPE MoveElementTo( + const OLECHAR *pwcsName, + IStorage *pstgDest, + const OLECHAR *pwcsNewName, + DWORD grfFlags); + + virtual HRESULT STDMETHODCALLTYPE Commit( + DWORD grfCommitFlags); + + virtual HRESULT STDMETHODCALLTYPE Revert(); + + virtual HRESULT STDMETHODCALLTYPE EnumElements( + DWORD reserved1, + void *reserved2, + DWORD reserved3, + IEnumSTATSTG **ppenum); + + virtual HRESULT STDMETHODCALLTYPE DestroyElement( + const OLECHAR *pwcsName); + + virtual HRESULT STDMETHODCALLTYPE RenameElement( + const OLECHAR *pwcsOldName, + const OLECHAR *pwcsNewName); + + virtual HRESULT STDMETHODCALLTYPE SetElementTimes( + const OLECHAR *pwcsName, + const FILETIME *pctime, + const FILETIME *patime, + const FILETIME *pmtime); + + virtual HRESULT STDMETHODCALLTYPE SetClass( + REFCLSID clsid); + + virtual HRESULT STDMETHODCALLTYPE SetStateBits( + DWORD grfStateBits, + DWORD grfMask); + + virtual HRESULT STDMETHODCALLTYPE Stat( + STATSTG *pstatstg, + DWORD grfStatFlag); + + virtual HRESULT STDMETHODCALLTYPE OpenStream( + LPCWSTR szStream, + ULONG *pcbData, + void **ppAddress); + + // Access storage object. + StgIO *GetStgIO() + { return (m_pStgIO); } + +#if defined(_DEBUG) + ULONG PrintSizeInfo( // Size of streams. + bool verbose); // Be verbose? +#endif + +protected: + HRESULT Write( // Return code. + LPCSTR szName, // Name of stream we're writing. + const void *pData, // Data to write. + ULONG cbData, // Size of data. + ULONG *pcbWritten); // How much did we write. + +private: + HRESULT FindStream(LPCSTR szName, __out PSTORAGESTREAM *stream); + HRESULT WriteSignature(LPCSTR pVersion); + HRESULT VerifySignature(PSTORAGESIGNATURE pSig); + HRESULT ReadHeader(); + HRESULT VerifyHeader(); + + static HRESULT GetDefaultVersion(LPCSTR* ppVersion); + +public: + // This function is a workaround to allow access to the "version requested" string. + HRESULT GetHeaderPointer(const void **ppv, ULONG *pcb); + +private: + // State data. + StgIO *m_pStgIO; // Storage subsystem. + LONG m_cRef; // Ref count for COM. + + // Header data. + STORAGEHEADER m_StgHdr; // Header for storage. + STORAGESTREAMLST m_Streams; // List of streams in the storage. + PSTORAGESTREAM m_pStreamList; // For read mode. + void *m_pbExtra; // Pointer to extra data if on disk. +}; + + +//***************************************************************************** +// Debugging helpers. #define __SAVESIZE_TRACE__ to enable. +//***************************************************************************** + +// #define __SAVESIZE_TRACE__ +#ifdef __SAVESIZE_TRACE__ +#define SAVETRACE(func) DEBUG_STMT(func) +#else +#define SAVETRACE(func) +#endif // __SAVESIZE_TRACE__ + +#endif // StgTiggerStorage + + + +// EOF diff --git a/src/coreclr/md/inc/stgtiggerstream.h b/src/coreclr/md/inc/stgtiggerstream.h new file mode 100644 index 00000000000..b30ade61d81 --- /dev/null +++ b/src/coreclr/md/inc/stgtiggerstream.h @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// StgTiggerStream.h +// + +// +// TiggerStream is the companion to the TiggerStorage CoClass. It handles the +// streams managed inside of the storage and does the direct file i/o. +// +//***************************************************************************** +#ifndef __StgTiggerStream_h__ +#define __StgTiggerStream_h__ + + + +#include "stgtiggerstorage.h" // Data definitions. + +enum +{ + STREAM_DATA_NAME +}; + + +class TiggerStorage; + + +class TiggerStream : + public IStream +{ +public: + TiggerStream() : + m_pStorage(0), + m_cRef(1) + {} + + virtual ~TiggerStream() {} + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *pp) + { return (BadError(E_NOTIMPL)); } + virtual ULONG STDMETHODCALLTYPE AddRef() + { return InterlockedIncrement(&m_cRef); } + virtual ULONG STDMETHODCALLTYPE Release() + { + ULONG cRef; + if ((cRef = InterlockedDecrement(&m_cRef)) == 0) + delete this; + return (cRef); + } + +// IStream + virtual HRESULT STDMETHODCALLTYPE Read( + void *pv, + ULONG cb, + ULONG *pcbRead); + + virtual HRESULT STDMETHODCALLTYPE Write( + const void *pv, + ULONG cb, + ULONG *pcbWritten); + + virtual HRESULT STDMETHODCALLTYPE Seek( + LARGE_INTEGER dlibMove, + DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition); + + virtual HRESULT STDMETHODCALLTYPE SetSize( + ULARGE_INTEGER libNewSize); + + virtual HRESULT STDMETHODCALLTYPE CopyTo( + IStream *pstm, + ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, + ULARGE_INTEGER *pcbWritten); + + virtual HRESULT STDMETHODCALLTYPE Commit( + DWORD grfCommitFlags); + + virtual HRESULT STDMETHODCALLTYPE Revert( void); + + virtual HRESULT STDMETHODCALLTYPE LockRegion( + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType); + + virtual HRESULT STDMETHODCALLTYPE UnlockRegion( + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType); + + virtual HRESULT STDMETHODCALLTYPE Stat( + STATSTG *pstatstg, + DWORD grfStatFlag); + + virtual HRESULT STDMETHODCALLTYPE Clone( + IStream **ppstm); + + + HRESULT Init( // Return code. + TiggerStorage *pStorage, // Parent storage. + LPCSTR szStream); // Stream name. + + ULONG GetStreamSize(); + +private: + TiggerStorage *m_pStorage; // Our parent storage. + char m_rcStream[MAXSTREAMNAME]; // Name of the stream. + LONG m_cRef; // Ref count. +}; + +#endif // __StgTiggerStream_h__ diff --git a/src/coreclr/md/inc/streamutil.h b/src/coreclr/md/inc/streamutil.h new file mode 100644 index 00000000000..0836e1bd91b --- /dev/null +++ b/src/coreclr/md/inc/streamutil.h @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// + +#if !defined( __STREAMUTIL_H__ ) +#define __STREAMUTIL_H__ + +namespace StreamUtil +{ + +// Write data to stream and advance the totalBytes counter +// +inline +HRESULT WriteToStream( IStream * strm, void const * data, UINT32 sizeInBytes, UINT32 * totalBytes = NULL ) +{ + HRESULT hr = strm->Write( data, sizeInBytes, NULL ); + if ( SUCCEEDED( hr ) && totalBytes != NULL ) + *totalBytes += sizeInBytes; + return hr; +} + + +// Write a POD to stream +// +template < typename T > +HRESULT WritePODToStream( IStream * strm, T val, UINT32 * totalBytes ) +{ + return WriteToStream( strm, & val, sizeof( val ), totalBytes ); +} + + +// Write concrete data types to stream +// Add additional overloads as needed +// + +inline +HRESULT WriteToStream( IStream * strm, int val, UINT32 * totalBytes = NULL ) +{ + return WritePODToStream( strm, val, totalBytes ); +} + + +inline +HRESULT WriteToStream( IStream * strm, DWORD val, UINT32 * totalBytes = NULL ) +{ + return WritePODToStream( strm, val, totalBytes ); +} + + +inline +HRESULT WriteToStream( IStream * strm, WORD val, UINT32 * totalBytes = NULL ) +{ + return WritePODToStream( strm, val, totalBytes ); +} + + +inline +HRESULT WriteToStream( IStream * strm, BYTE val, UINT32 * totalBytes = NULL ) +{ + return WritePODToStream( strm, val, totalBytes ); +} + + +// Align to DWORD boundary +// +inline +HRESULT AlignDWORD( IStream * strm, UINT32 * totalBytes ) +{ + HRESULT hr = S_OK; + + UINT32 aligned = (*totalBytes + 3) & ~3; + if (aligned > *totalBytes) + { // The *totalBytes were not aligned to DWORD, we need to add padding + DWORD data = 0; + hr = WriteToStream( strm, & data, aligned - *totalBytes, totalBytes ); + } + else if (aligned < *totalBytes) + { // We got an integer overflow in 'aligned' expression above + hr = COR_E_OVERFLOW; + } + + return hr; +} + + +// Get stream position +// +inline +HRESULT GetPos( IStream * strm, UINT32 * pos ) +{ + LARGE_INTEGER temp = { {0} }; + ULARGE_INTEGER ul_pos = { {0} }; + HRESULT hr = strm->Seek( temp, STREAM_SEEK_CUR, & ul_pos ); + * pos = ul_pos.u.LowPart; + return hr; +} + + +class NullStream : public IStream +{ +public: + NullStream() + : m_pos( 0 ) + {} + + ULONG STDMETHODCALLTYPE AddRef() + { + _ASSERTE( false ); + return 0; + } + + ULONG STDMETHODCALLTYPE Release() + { + SUPPORTS_DAC_HOST_ONLY; + _ASSERTE( false ); + return 0; + } + + HRESULT STDMETHODCALLTYPE QueryInterface( REFIID, PVOID* ) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten) + { + m_pos += cb; + if (pcbWritten != NULL) + { + *pcbWritten = cb; + } + return S_OK; + } + + HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove,DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) + { + if ( dwOrigin != STREAM_SEEK_CUR || dlibMove.QuadPart != 0 || plibNewPosition == NULL ) + return E_NOTIMPL; + + plibNewPosition->u.HighPart = 0; + plibNewPosition->u.LowPart = m_pos; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE CopyTo( + IStream *pstm, + ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, + ULARGE_INTEGER *pcbWritten) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE Commit( + DWORD grfCommitFlags) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE Revert() + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE LockRegion( + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE UnlockRegion( + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE Stat( + STATSTG *pstatstg, + DWORD grfStatFlag) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE Clone( + IStream **ppstm) + { + _ASSERTE( false ); + return E_NOTIMPL; + } + +private: + UINT32 m_pos; +}; // class NullStream + +}; // namespace StreamUtil + +#endif diff --git a/src/coreclr/md/inc/verifylayouts.h b/src/coreclr/md/inc/verifylayouts.h new file mode 100644 index 00000000000..efe3acee471 --- /dev/null +++ b/src/coreclr/md/inc/verifylayouts.h @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// VerifyLayouts.h +// + +// +// Make sure that layouts of MD data strucutres doesn't change accidentally +// +//***************************************************************************** + +// The code in MD\DataSource\TargetTypes.* takes a direct dependency on +// the layouts of types in MD. This is used by the debugger to read metadata +// from a seperate process by deserializing the memory for these datastructures. +// +// You are probably reading this comment because you changed a layout and +// one of the static_asserts failed during build. This is what you should +// do to fix it: +// +// a) Go to clr\src\Debug\EE\Debugger.cpp and increment the global version counter +// m_mdDataStructureVersion set in Debugger::Debugger() +// Please comment the change there with a new entry in the version table +// +// b) If a define is conditionally changing the layout: +// i) add, if needed, an entry to the list of define bits in +// clr\src\Debug\EE\debugger.h Debugger::_Target_Defines +// ii) add code like this that sets the bit in the Debugger::_defines static +// variable +// #ifdef MY_DEFINE +// | DEFINE_MY_DEFINE +// #endif +// +// c) Update the code in MD\DataSource\TargetTypes.h/cpp to deserialize your +// new layout correctly. The code needs to work for any version of the layouts +// with or without defines set. Your reader code can access the current version +// and defines by calling: +// reader.GetMDStructuresVersion() +// reader.IsDefined(Define_XYZ) +// +// d) If your changes affect what a debugger should be reading in order to fetch +// metadata then you probably need to change other parts of the debugger +// implementation as well. In general the debugger cares about the schema, +// TableDefs, storage signature, table records, and storage pools. +// +// e) AFTER you have fixed up the debugger stuff above, now its time to update +// layout definitions so the static asserts will quiet down. Check out +// the comments in VerifyLayouts.inc for how to do that. +// +// Thanks for helping us keep the debugger working :) +// + + + + +//------------------------------------------------------------------------------- +// Type layout verification +// +// +// These macros includes VerifyLayouts.inc a few times with different definitions to build up +// the source. The final result should look something like this: +// (don't assume specific type names/fields/offsets/sizes are accurate in this example) +// +// +// class VerifyLayoutsMD +// { +// +// static const int expected_offset_of_first_field_in_CMiniMdRW = 208; +// static const int actual_offset_of_first_field_in_CMiniMdRW = +// 208; +// static const int offset_of_field_after_CMiniMdRW_m_Schema = +// 312; +// static const int offset_of_field_after_CMiniMdRW_m_Tables = +// 316; +// ... many more lines like this covering all fields in all marked up types ... +// +// +// static const int alignment_of_first_field_in_CMiniMdRW = +// 4; +// static const int alignment_of_field_after_CMiniMdRW_m_Schema = +// 8; +// static const int alignment_of_field_after_CMiniMdRW_m_Tables = +// 8; +// ... many more lines like this cover all fields in all marked up types ... +// +// +// static_assert_no_msg(expected_offset_of_first_field_in_CMiniMdRW == actual_offset_of_first_field_in_CMiniMdRW); +// static_assert_no_msg(offset_of_field_after_CMiniMdRW_m_Schema == +// ALIGN_UP(offsetof(CMiniMdRW, m_Schema) + 104, alignment_of_field_after_CMiniMdRW_m_Schema)); +// static_assert_no_msg(offset_of_field_after_CMiniMdRW_m_Tables == +// ALIGN_UP(offsetof(CMiniMdRW, m_Tables) + 4, alignment_of_field_after_CMiniMdRW_m_Tables)); +// ... many more lines like this cover all fields in all marked up types ... +// +// }; +// +// +// +// + +#ifdef FEATURE_METADATA_VERIFY_LAYOUTS + +#include <stddef.h> // offsetof +#include "static_assert.h" +#include "metamodel.h" +#include "mdinternalrw.h" + +// other types provide friend access to this type so that the +// offsetof macro can access their private fields +class VerifyLayoutsMD +{ + // we have a bunch of arrays with this fixed size, make sure it doesn't change + static_assert_no_msg(TBL_COUNT == 45); + +#define USING_ALIAS(typeName, ...) using typeName = __VA_ARGS__; + +#define FIELD(typeName, fieldName, fieldSize) ALIGN_FIELD(typeName, fieldName, fieldSize, fieldSize) + +#define BEGIN_TYPE(typeName, initialFieldOffset) \ + static const int expected_offset_of_first_field_in_##typeName = initialFieldOffset; \ + static const int actual_offset_of_first_field_in_##typeName = + +#define ALIGN_FIELD(typeName, fieldName, fieldSize, fieldAlign) \ + offsetof(typeName, fieldName); \ + static const int offset_of_field_after_##typeName##_##fieldName = + +#define BITFIELD(typeName, fieldName, fieldOffset, fieldSize) \ + fieldOffset; \ + static const int offset_of_field_after_##typeName##_##fieldName = + +#define END_TYPE(typeName, typeAlignentSize) \ + sizeof(typeName); + +#include "VerifyLayouts.inc" + +// Only declare using once +#undef USING_ALIAS +#define USING_ALIAS(a, ...) + +#undef BEGIN_TYPE +#undef ALIGN_FIELD +#undef END_TYPE +#undef BITFIELD + + +#define BEGIN_TYPE(typeName, initialFieldOffset) \ + static const int alignment_of_first_field_in_##typeName = +#define ALIGN_FIELD(typeName, fieldName, fieldSize, fieldAlign) \ + fieldAlign; \ + static const int alignment_of_field_after_##typeName##_##fieldName = +#define BITFIELD(typeName, fieldName, fieldOffset, fieldSize) \ + fieldSize; \ + static const int alignment_of_field_after_##typeName##_##fieldName = +#define END_TYPE(typeName, typeAlignmentSize) \ + typeAlignmentSize; + +#include "VerifyLayouts.inc" + +#undef BEGIN_TYPE +#undef ALIGN_FIELD +#undef END_TYPE +#undef BITFIELD + + +#define BEGIN_TYPE(typeName, initialFieldOffset) \ + static_assert_no_msg(expected_offset_of_first_field_in_##typeName == actual_offset_of_first_field_in_##typeName); + + +#define ALIGN_UP(value, alignment) (((value) + (alignment) - 1)&~((alignment) - 1)) +#define ALIGN_FIELD(typeName, fieldName, fieldSize, fieldAlign) \ + static_assert_no_msg(offset_of_field_after_##typeName##_##fieldName == \ + ALIGN_UP(offsetof(typeName, fieldName) + fieldSize, alignment_of_field_after_##typeName##_##fieldName)); +#define BITFIELD(typeName, fieldName, fieldOffset, fieldSize) \ + static_assert_no_msg(offset_of_field_after_##typeName##_##fieldName == \ + ALIGN_UP(fieldOffset + fieldSize, alignment_of_field_after_##typeName##_##fieldName)); + +#define END_TYPE(typeName, typeAlignmentSize) +#include "VerifyLayouts.inc" + +}; + + + + +#endif //FEATURE_METADATA_VERIFY_LAYOUTS diff --git a/src/coreclr/md/runtime/CMakeLists.txt b/src/coreclr/md/runtime/CMakeLists.txt new file mode 100644 index 00000000000..6ff49d3e803 --- /dev/null +++ b/src/coreclr/md/runtime/CMakeLists.txt @@ -0,0 +1,68 @@ +add_definitions(-DNO_COR) + +set(MDRUNTIME_SOURCES + mdcolumndescriptors.cpp + liteweightstgdb.cpp + mdfileformat.cpp + metamodel.cpp + metamodelro.cpp + recordpool.cpp + mdinternaldisp.cpp + mdinternalro.cpp + strongnameinternal.cpp +) + +set(MDRUNTIME_HEADERS + ../../inc/caparser.h + ../../inc/cor.h + ../../inc/corhlpr.h + ../../inc/corpriv.h + ../../inc/mdcommon.h + ../../inc/mdfileformat.h + ../../inc/metadatatracker.h + ../../inc/pedecoder.h + ../../inc/posterror.h + ../compiler/regmeta.h + ../hotdata/export.h + ../inc/assemblymdinternaldisp.h + ../inc/liteweightstgdb.h + ../inc/mdcolumndescriptors.h + ../inc/metamodel.h + ../inc/metamodelro.h + ../inc/pdbheap.h + ../inc/portablepdbmdds.h + ../inc/portablepdbmdi.h + ../inc/recordpool.h + metamodelcolumndefs.h + mdinternaldisp.h + mdinternalro.h +) + +convert_to_absolute_path(MDRUNTIME_HEADERS ${MDRUNTIME_HEADERS}) +convert_to_absolute_path(MDRUNTIME_SOURCES ${MDRUNTIME_SOURCES}) + +if (CLR_CMAKE_TARGET_WIN32) + list(APPEND MDRUNTIME_SOURCES ${MDRUNTIME_HEADERS}) +endif (CLR_CMAKE_TARGET_WIN32) + +add_library_clr(mdruntime_dac ${MDRUNTIME_SOURCES}) +set_target_properties(mdruntime_dac PROPERTIES DAC_COMPONENT TRUE) +target_precompile_headers(mdruntime_dac PRIVATE stdafx.h) + +add_library_clr(mdruntime_wks_obj OBJECT ${MDRUNTIME_SOURCES}) +target_compile_definitions(mdruntime_wks_obj PRIVATE FEATURE_METADATA_EMIT_ALL) +target_precompile_headers(mdruntime_wks_obj PRIVATE stdafx.h) +add_library(mdruntime_wks INTERFACE) +target_sources(mdruntime_wks INTERFACE $<TARGET_OBJECTS:mdruntime_wks_obj>) + +add_library_clr(mdruntime-dbi ${MDRUNTIME_SOURCES}) +set_target_properties(mdruntime-dbi PROPERTIES DBI_COMPONENT TRUE) +target_precompile_headers(mdruntime-dbi PRIVATE stdafx.h) + +add_library_clr(mdruntime_crossgen ${MDRUNTIME_SOURCES}) +set_target_properties(mdruntime_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) +target_precompile_headers(mdruntime_crossgen PRIVATE stdafx.h) + +add_library_clr(mdruntime_ppdb ${MDRUNTIME_SOURCES}) +target_compile_definitions(mdruntime_ppdb PRIVATE FEATURE_METADATA_EMIT_ALL FEATURE_METADATA_EMIT_PORTABLE_PDB) +target_precompile_headers(mdruntime_ppdb PRIVATE stdafx.h) diff --git a/src/coreclr/md/runtime/liteweightstgdb.cpp b/src/coreclr/md/runtime/liteweightstgdb.cpp new file mode 100644 index 00000000000..5e110ddde6b --- /dev/null +++ b/src/coreclr/md/runtime/liteweightstgdb.cpp @@ -0,0 +1,261 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// LiteWeightStgdb.cpp +// + +// +// This contains definition of class CLiteWeightStgDB. This is light weight +// read-only implementation for accessing compressed meta data format. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header. +#include "mdfileformat.h" +#include "metamodelro.h" +#include "liteweightstgdb.h" +#include "metadatatracker.h" + +#include "../hotdata/export.h" + +__checkReturn +HRESULT _CallInitOnMemHelper(CLiteWeightStgdb<CMiniMd> *pStgdb, ULONG cbData, LPCVOID pData) +{ + return pStgdb->InitOnMem(cbData,pData); +} + +//***************************************************************************** +// Open an in-memory metadata section for read +//***************************************************************************** +template <class MiniMd> +__checkReturn +HRESULT +CLiteWeightStgdb<MiniMd>::InitOnMem( + ULONG cbData, // count of bytes in pData + LPCVOID pData) // points to meta data section in memory +{ + STORAGEHEADER sHdr; // Header for the storage. + PSTORAGESTREAM pStream; // Pointer to each stream. + int bFoundMd = false; // true when compressed data found. + int i; // Loop control. + HRESULT hr = S_OK; + ULONG cbStreamBuffer; + + // Don't double open. + _ASSERTE((m_pvMd == NULL) && (m_cbMd == 0)); + + // Validate the signature of the format, or it isn't ours. + IfFailGo(MDFormat::VerifySignature((PSTORAGESIGNATURE)pData, cbData)); + +#ifdef FEATURE_PREJIT + m_MiniMd.m_pHotTablesDirectory = NULL; +#endif //FEATURE_PREJIT + + // Remaining buffer size behind the stream header (pStream). + cbStreamBuffer = cbData; + // Get back the first stream. + pStream = MDFormat::GetFirstStream_Verify(&sHdr, pData, &cbStreamBuffer); + if (pStream == NULL) + { + Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Loop through each stream and pick off the ones we need. + for (i = 0; i < sHdr.GetiStreams(); i++) + { + // Do we have enough buffer to read stream header? + if (cbStreamBuffer < sizeof(*pStream)) + { + Debug_ReportError("Stream header is not within MetaData block."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + // Pick off the location and size of the data. + if (pStream->GetOffset() >= cbData) + { // Stream data are not in the buffer. Stream header is corrupted. + Debug_ReportError("Stream data are not within MetaData block."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + void *pvCurrentData = (void *)((BYTE *)pData + pStream->GetOffset()); + ULONG cbCurrentData = pStream->GetSize(); + + // Get next stream. + PSTORAGESTREAM pNext = pStream->NextStream_Verify(); + if (pNext == NULL) + { // Stream header is corrupted. + Debug_ReportError("Invalid stream header - cannot get next stream header."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Range check + if ((LPBYTE)pNext > ((LPBYTE)pData + cbData)) + { + Debug_ReportError("Stream header is not within MetaData block."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Stream end must fit into the buffer and we have to check integer overflow (stream start is already checked) + if ((((LPBYTE)pvCurrentData + cbCurrentData) < (LPBYTE)pvCurrentData) || + (((LPBYTE)pvCurrentData + cbCurrentData) > ((LPBYTE)pData + cbData))) + { + Debug_ReportError("Stream data are not within MetaData block."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // String pool. + if (strcmp(pStream->GetName(), STRING_POOL_STREAM_A) == 0) + { + METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolStrings, pvCurrentData, cbCurrentData, 1)); + // String pool has to end with a null-terminator, therefore we don't have to check string pool content on access. + // Shrink size of the pool to the last null-terminator found. + while (cbCurrentData != 0) + { + if (((LPBYTE)pvCurrentData)[cbCurrentData - 1] == 0) + { // We have found last null terminator + break; + } + // Shrink size of the pool + cbCurrentData--; + Debug_ReportError("String heap/pool does not end with null-terminator ... shrinking the heap."); + } + // Initialize string heap with null-terminated block of data + IfFailGo(m_MiniMd.m_StringHeap.Initialize( + MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), + FALSE)); // fCopyData + } + + // Literal String Blob pool. + else if (strcmp(pStream->GetName(), US_BLOB_POOL_STREAM_A) == 0) + { + METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolUSBlobs, pvCurrentData, cbCurrentData, 1)); + // Initialize user string heap with block of data + IfFailGo(m_MiniMd.m_UserStringHeap.Initialize( + MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), + FALSE)); // fCopyData + } + + // GUID pool. + else if (strcmp(pStream->GetName(), GUID_POOL_STREAM_A) == 0) + { + METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolGuids, pvCurrentData, cbCurrentData, 1)); + // Initialize guid heap with block of data + IfFailGo(m_MiniMd.m_GuidHeap.Initialize( + MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), + FALSE)); // fCopyData + } + + // Blob pool. + else if (strcmp(pStream->GetName(), BLOB_POOL_STREAM_A) == 0) + { + METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolBlobs, pvCurrentData, cbCurrentData, 1)); + // Initialize blob heap with block of data + IfFailGo(m_MiniMd.m_BlobHeap.Initialize( + MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), + FALSE)); // fCopyData + } + + // Found the compressed meta data stream. + else if (strcmp(pStream->GetName(), COMPRESSED_MODEL_STREAM_A) == 0) + { + IfFailGo( m_MiniMd.InitOnMem(pvCurrentData, cbCurrentData) ); + bFoundMd = true; + } + + // Found the hot meta data stream + else if (strcmp(pStream->GetName(), HOT_MODEL_STREAM_A) == 0) + { +#ifdef FEATURE_PREJIT + BYTE * hotStreamEnd = reinterpret_cast< BYTE * >( pvCurrentData ) + cbCurrentData; + ULONG * hotMetadataDir = reinterpret_cast< ULONG * >( hotStreamEnd ) - 2; + ULONG hotPoolsSize = *hotMetadataDir; + + m_MiniMd.m_pHotTablesDirectory = (struct MetaData::HotTablesDirectory *) + (reinterpret_cast<BYTE *>(hotMetadataDir) - hotPoolsSize - sizeof(struct MetaData::HotTablesDirectory)); + MetaData::HotTable::CheckTables(m_MiniMd.m_pHotTablesDirectory); + + DataBuffer hotMetaData( + reinterpret_cast<BYTE *>(pvCurrentData), + cbCurrentData); + IfFailGo(InitHotPools(hotMetaData)); +#else //!FEATURE_PREJIT + Debug_ReportError("MetaData hot stream is peresent, but ngen is not supported."); + // Ignore the stream +#endif //!FEATURE_PREJIT + } + // Pick off the next stream if there is one. + pStream = pNext; + cbStreamBuffer = (ULONG)((LPBYTE)pData + cbData - (LPBYTE)pNext); + } + + // If the meta data wasn't found, we can't handle this file. + if (!bFoundMd) + { + Debug_ReportError("MetaData compressed model stream #~ not found."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + else + { // Validate sensible heaps. + IfFailGo(m_MiniMd.PostInit(0)); + } + + // Save off the location. + m_pvMd = pData; + m_cbMd = cbData; + +ErrExit: + return hr; +} // CLiteWeightStgdb<MiniMd>::InitOnMem + + +template <class MiniMd> +__checkReturn +HRESULT +CLiteWeightStgdb<MiniMd>::InitHotPools( + DataBuffer hotMetaDataBuffer) +{ + HRESULT hr; + MetaData::HotMetaData hotMetaData; + MetaData::HotHeapsDirectoryIterator heapsIterator; + + IfFailRet(hotMetaData.Initialize(hotMetaDataBuffer)); + + IfFailRet(hotMetaData.GetHeapsDirectoryIterator(&heapsIterator)); + + for (;;) + { + MetaData::HotHeap hotHeap; + MetaData::HeapIndex hotHeapIndex; + + hr = heapsIterator.GetNext(&hotHeap, &hotHeapIndex); + if (hr == S_FALSE) + { // End of iteration + return S_OK; + } + + switch (hotHeapIndex.Get()) + { + case MetaData::HeapIndex::StringHeapIndex: + { + m_MiniMd.m_StringHeap.InitializeHotData(hotHeap); + break; + } + case MetaData::HeapIndex::GuidHeapIndex: + { + m_MiniMd.m_GuidHeap.InitializeHotData(hotHeap); + break; + } + case MetaData::HeapIndex::UserStringHeapIndex: + { + m_MiniMd.m_UserStringHeap.InitializeHotData(hotHeap); + break; + } + case MetaData::HeapIndex::BlobHeapIndex: + { + m_MiniMd.m_BlobHeap.InitializeHotData(hotHeap); + break; + } + default: + Debug_ReportInternalError("There's a bug in HotHeapsDirectoryIterator - it should verify the heap index."); + IfFailRet(METADATA_E_INTERNAL_ERROR); + } + } +} // CLiteWeightStgdb<MiniMd>::InitHotPools diff --git a/src/coreclr/md/runtime/mdcolumndescriptors.cpp b/src/coreclr/md/runtime/mdcolumndescriptors.cpp new file mode 100644 index 00000000000..3b795df8097 --- /dev/null +++ b/src/coreclr/md/runtime/mdcolumndescriptors.cpp @@ -0,0 +1,263 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// + +#include "stdafx.h" + +const BYTE CMiniMdBase::s_ModuleCol[] = {2, + 97,0,2, 101,2,2, 102,4,2, 102,6,2, 102,8,2, + 97,0,2, 101,2,4, 102,6,2, 102,8,2, 102,10,2, +}; +const BYTE CMiniMdBase::s_TypeRefCol[] = {2, + 75,0,2, 101,2,2, 101,4,2, + 75,0,2, 101,2,4, 101,6,4, +}; +const BYTE CMiniMdBase::s_TypeDefCol[] = {2, + 99,0,4, 101,4,2, 101,6,2, 64,8,2, 4,10,2, 6,12,2, + 99,0,4, 101,4,4, 101,8,4, 64,12,2, 4,14,2, 6,16,2, +}; +const BYTE CMiniMdBase::s_FieldPtrCol[] = {1, + 4,0,2, +}; +const BYTE CMiniMdBase::s_FieldCol[] = {3, + 97,0,2, 101,2,2, 103,4,2, + 97,0,2, 101,2,4, 103,6,4, + 97,0,2, 101,2,4, 103,6,2, +}; +const BYTE CMiniMdBase::s_MethodPtrCol[] = {1, + 6,0,2, +}; +const BYTE CMiniMdBase::s_MethodCol[] = {3, + 99,0,4, 97,4,2, 97,6,2, 101,8,2, 103,10,2, 8,12,2, + 99,0,4, 97,4,2, 97,6,2, 101,8,4, 103,12,4, 8,16,2, + 99,0,4, 97,4,2, 97,6,2, 101,8,4, 103,12,2, 8,14,2, +}; +const BYTE CMiniMdBase::s_ParamPtrCol[] = {1, + 8,0,2, +}; +const BYTE CMiniMdBase::s_ParamCol[] = {2, + 97,0,2, 97,2,2, 101,4,2, + 97,0,2, 97,2,2, 101,4,4, +}; +const BYTE CMiniMdBase::s_InterfaceImplCol[] = {1, + 2,0,2, 64,2,2, +}; +const BYTE CMiniMdBase::s_MemberRefCol[] = {3, + 69,0,2, 101,2,2, 103,4,2, + 69,0,4, 101,4,4, 103,8,4, + 69,0,2, 101,2,4, 103,6,2, +}; +const BYTE CMiniMdBase::s_ConstantCol[] = {3, + 100,0,1, 65,2,2, 103,4,2, + 100,0,1, 65,2,4, 103,6,4, + 100,0,1, 65,2,2, 103,4,4, +}; +const BYTE CMiniMdBase::s_CustomAttributeCol[] = {3, + 66,0,2, 74,2,2, 103,4,2, + 66,0,4, 74,4,4, 103,8,4, + 66,0,4, 74,4,2, 103,6,2, +}; +const BYTE CMiniMdBase::s_FieldMarshalCol[] = {2, + 67,0,2, 103,2,2, + 67,0,2, 103,2,4, +}; +const BYTE CMiniMdBase::s_DeclSecurityCol[] = {3, + 96,0,2, 68,2,2, 103,4,2, + 96,0,2, 68,2,4, 103,6,4, + 96,0,2, 68,2,2, 103,4,4, +}; +const BYTE CMiniMdBase::s_ClassLayoutCol[] = {1, + 97,0,2, 99,2,4, 2,6,2, +}; +const BYTE CMiniMdBase::s_FieldLayoutCol[] = {1, + 99,0,4, 4,4,2, +}; +const BYTE CMiniMdBase::s_StandAloneSigCol[] = {2, + 103,0,2, + 103,0,4, +}; +const BYTE CMiniMdBase::s_EventMapCol[] = {1, + 2,0,2, 20,2,2, +}; +const BYTE CMiniMdBase::s_EventPtrCol[] = {1, + 20,0,2, +}; +const BYTE CMiniMdBase::s_EventCol[] = {2, + 97,0,2, 101,2,2, 64,4,2, + 97,0,2, 101,2,4, 64,6,2, +}; +const BYTE CMiniMdBase::s_PropertyMapCol[] = {1, + 2,0,2, 23,2,2, +}; +const BYTE CMiniMdBase::s_PropertyPtrCol[] = {1, + 23,0,2, +}; +const BYTE* CMiniMdBase::s_PropertyCol = s_FieldCol; +const BYTE CMiniMdBase::s_MethodSemanticsCol[] = {1, + 97,0,2, 6,2,2, 70,4,2, +}; +const BYTE CMiniMdBase::s_MethodImplCol[] = {1, + 2,0,2, 71,2,2, 71,4,2, +}; +const BYTE CMiniMdBase::s_ModuleRefCol[] = {2, + 101,0,2, + 101,0,4, +}; +const BYTE* CMiniMdBase::s_TypeSpecCol = s_StandAloneSigCol; +const BYTE CMiniMdBase::s_ImplMapCol[] = {2, + 97,0,2, 72,2,2, 101,4,2, 26,6,2, + 97,0,2, 72,2,2, 101,4,4, 26,8,2, +}; +const BYTE* CMiniMdBase::s_FieldRVACol = s_FieldLayoutCol; +const BYTE CMiniMdBase::s_ENCLogCol[] = {1, + 99,0,4, 99,4,4, +}; +const BYTE CMiniMdBase::s_ENCMapCol[] = {1, + 99,0,4, +}; +const BYTE CMiniMdBase::s_AssemblyCol[] = {3, + 99,0,4, 97,4,2, 97,6,2, 97,8,2, 97,10,2, 99,12,4, 103,16,2, 101,18,2, 101,20,2, + 99,0,4, 97,4,2, 97,6,2, 97,8,2, 97,10,2, 99,12,4, 103,16,4, 101,20,4, 101,24,4, + 99,0,4, 97,4,2, 97,6,2, 97,8,2, 97,10,2, 99,12,4, 103,16,2, 101,18,4, 101,22,4, +}; +const BYTE* CMiniMdBase::s_AssemblyProcessorCol = s_ENCMapCol; +const BYTE CMiniMdBase::s_AssemblyOSCol[] = {1, + 99,0,4, 99,4,4, 99,8,4, +}; +const BYTE CMiniMdBase::s_AssemblyRefCol[] = {3, + 97,0,2, 97,2,2, 97,4,2, 97,6,2, 99,8,4, 103,12,2, 101,14,2, 101,16,2, 103,18,2, + 97,0,2, 97,2,2, 97,4,2, 97,6,2, 99,8,4, 103,12,4, 101,16,4, 101,20,4, 103,24,4, + 97,0,2, 97,2,2, 97,4,2, 97,6,2, 99,8,4, 103,12,2, 101,14,4, 101,18,4, 103,22,2, +}; +const BYTE CMiniMdBase::s_AssemblyRefProcessorCol[] = {1, + 99,0,4, 35,4,2, +}; +const BYTE CMiniMdBase::s_AssemblyRefOSCol[] = {1, + 99,0,4, 99,4,4, 99,8,4, 35,12,2, +}; +const BYTE CMiniMdBase::s_FileCol[] = {3, + 99,0,4, 101,4,2, 103,6,2, + 99,0,4, 101,4,4, 103,8,4, + 99,0,4, 101,4,4, 103,8,2, +}; +const BYTE CMiniMdBase::s_ExportedTypeCol[] = {2, + 99,0,4, 99,4,4, 101,8,2, 101,10,2, 73,12,2, + 99,0,4, 99,4,4, 101,8,4, 101,12,4, 73,16,2, +}; +const BYTE CMiniMdBase::s_ManifestResourceCol[] = {2, + 99,0,4, 99,4,4, 101,8,2, 73,10,2, + 99,0,4, 99,4,4, 101,8,4, 73,12,2, +}; +const BYTE CMiniMdBase::s_NestedClassCol[] = {1, + 2,0,2, 2,2,2, +}; +const BYTE CMiniMdBase::s_GenericParamCol[] = {2, + 97,0,2, 97,2,2, 76,4,2, 101,6,2, 64,8,2, 64,10,2, + 97,0,2, 97,2,2, 76,4,2, 101,6,4, 64,10,2, 64,12,2, +}; +const BYTE CMiniMdBase::s_MethodSpecCol[] = {2, + 71,0,2, 103,2,2, + 71,0,2, 103,2,4, +}; +const BYTE CMiniMdBase::s_GenericParamConstraintCol[] = {1, + 42,0,2, 64,2,2, +}; +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +// Dummy descriptors to fill the gap to 0x30 +const BYTE CMiniMdBase::s_Dummy1Col[] = { NULL }; +const BYTE CMiniMdBase::s_Dummy2Col[] = { NULL }; +const BYTE CMiniMdBase::s_Dummy3Col[] = { NULL }; +// Actual portable PDB tables descriptors +const BYTE CMiniMdBase::s_DocumentCol[] = { 2, + 103,0,2, 102,2,2, 103,4,2, 102,6,2, + 103,0,4, 102,4,2, 103,6,2, 102,8,4, +}; +const BYTE CMiniMdBase::s_MethodDebugInformationCol[] = { 2, + 48,0,2, 103,2,2, + 48,0,2, 103,2,4, +}; +const BYTE CMiniMdBase::s_LocalScopeCol[] = { 1, + 6,0,2, 53,2,2, 51,4,2, 52,6,2, 99,8,4, 99,12,4 +}; +const BYTE CMiniMdBase::s_LocalVariableCol[] = { 2, + 97,0,2, 97,2,2, 101,4,2, + 97,0,2, 97,2,2, 101,4,4 +}; +const BYTE CMiniMdBase::s_LocalConstantCol[] = { 3, + 101,0,2, 103,2,2, + 101,0,4, 103,4,4, + 101,0,4, 103,4,2, +}; +const BYTE CMiniMdBase::s_ImportScopeCol[] = { 2, + 53,0,2, 103,2,2, + 53,0,2, 103,2,4 +}; +// TODO: +// const BYTE CMiniMdBase::s_StateMachineMethodCol[] = {}; +// const BYTE CMiniMdBase::s_CustomDebugInformationCol[] = {}; +#endif // #ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + +const BYTE* const CMiniMdBase::s_TableColumnDescriptors[] = { +s_ModuleCol, +s_TypeRefCol, +s_TypeDefCol, +s_FieldPtrCol, +s_FieldCol, +s_MethodPtrCol, +s_MethodCol, +s_ParamPtrCol, +s_ParamCol, +s_InterfaceImplCol, +s_MemberRefCol, +s_ConstantCol, +s_CustomAttributeCol, +s_FieldMarshalCol, +s_DeclSecurityCol, +s_ClassLayoutCol, +s_FieldLayoutCol, +s_StandAloneSigCol, +s_EventMapCol, +s_EventPtrCol, +s_EventCol, +s_PropertyMapCol, +s_PropertyPtrCol, +s_FieldCol, +s_MethodSemanticsCol, +s_MethodImplCol, +s_ModuleRefCol, +s_StandAloneSigCol, +s_ImplMapCol, +s_FieldLayoutCol, +s_ENCLogCol, +s_ENCMapCol, +s_AssemblyCol, +s_ENCMapCol, +s_AssemblyOSCol, +s_AssemblyRefCol, +s_AssemblyRefProcessorCol, +s_AssemblyRefOSCol, +s_FileCol, +s_ExportedTypeCol, +s_ManifestResourceCol, +s_NestedClassCol, +s_GenericParamCol, +s_MethodSpecCol, +s_GenericParamConstraintCol, +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +// Dummy descriptors to fill the gap to 0x30 +s_Dummy1Col, +s_Dummy2Col, +s_Dummy3Col, +// Actual portable PDB tables descriptors +s_DocumentCol, +s_MethodDebugInformationCol, +s_LocalScopeCol, +s_LocalVariableCol, +s_LocalConstantCol, +s_ImportScopeCol, +// TODO: +// s_StateMachineMethodCol, +// s_CustomDebugInformationCol, +#endif // #ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +}; diff --git a/src/coreclr/md/runtime/mdfileformat.cpp b/src/coreclr/md/runtime/mdfileformat.cpp new file mode 100644 index 00000000000..9b1c5f6e64b --- /dev/null +++ b/src/coreclr/md/runtime/mdfileformat.cpp @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDFileFormat.cpp +// + +// +// This file contains a set of helpers to verify and read the file format. +// This code does not handle the paging of the data, or different types of +// I/O. See the StgTiggerStorage and StgIO code for this level of support. +// +//***************************************************************************** + +#include "stdafx.h" // Standard header file. +#include "mdfileformat.h" // The format helpers. +#include "posterror.h" // Error handling code. + +//***************************************************************************** +// Verify the signature at the front of the file to see what type it is. +//***************************************************************************** +#define STORAGE_MAGIC_OLD_SIG 0x2B4D4F43 // +MOC (old version of BSJB signature code:STORAGE_MAGIC_SIG) +HRESULT +MDFormat::VerifySignature( + PSTORAGESIGNATURE pSig, // The signature to check. + ULONG cbData) +{ + HRESULT hr = S_OK; + + // If signature didn't match, you shouldn't be here. + ULONG dwSignature = pSig->GetSignature(); + if (dwSignature == STORAGE_MAGIC_OLD_SIG) + { + Debug_ReportError("Invalid MetaData storage signature - old magic signature +MOC."); + return PostError(CLDB_E_FILE_OLDVER, 1, 0); + } + if (dwSignature != STORAGE_MAGIC_SIG) + { + Debug_ReportError("Invalid MetaData storage signature - unrecognized magic signature, should be BSJB."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // Check for overflow + ULONG lVersionString = pSig->GetVersionStringLength(); + ULONG sum = sizeof(STORAGESIGNATURE) + lVersionString; + if ((sum < sizeof(STORAGESIGNATURE)) || (sum < lVersionString)) + { + Debug_ReportError("Invalid MetaData storage signature - version string too long, integer overflow."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // Check for invalid version string size + if ((sizeof(STORAGESIGNATURE) + lVersionString) > cbData) + { + Debug_ReportError("Invalid MetaData storage signature - version string too long."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // Check that the version string is null terminated. This string + // is ANSI, so no double-null checks need to be made. + { + BYTE *pStart = &pSig->pVersion[0]; + BYTE *pEnd = pStart + lVersionString + 1; // Account for terminating NULL + BYTE *pCur; + + for (pCur = pStart; pCur < pEnd; pCur++) + { + if (*pCur == 0) + break; + } + + // If we got to the end without hitting a NULL, we have a bad version string + if (pCur == pEnd) + { + Debug_ReportError("Invalid MetaData storage signature - version string has not null-terminator."); + return PostError(CLDB_E_FILE_CORRUPT); + } + } + + // Only a specific version of the 0.x format is supported by this code + // in order to support the NT 5 beta clients which used this format. + if (pSig->GetMajorVer() == FILE_VER_MAJOR_v0) + { + if (pSig->GetMinorVer() < FILE_VER_MINOR_v0) + { + Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1."); + hr = CLDB_E_FILE_OLDVER; + } + } + else + // There is currently no code to migrate an old format of the 1.x. This + // would be added only under special circumstances. + if ((pSig->GetMajorVer() != FILE_VER_MAJOR) || (pSig->GetMinorVer() != FILE_VER_MINOR)) + { + Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1."); + hr = CLDB_E_FILE_OLDVER; + } + + if (FAILED(hr)) + hr = PostError(hr, (int)pSig->GetMajorVer(), (int)pSig->GetMinorVer()); + return hr; +} // MDFormat::VerifySignature + +//***************************************************************************** +// Skip over the header and find the actual stream data. +// It doesn't perform any checks for buffer overflow - use GetFirstStream_Verify +// instead. +//***************************************************************************** +PSTORAGESTREAM +MDFormat::GetFirstStream( + PSTORAGEHEADER pHeader, // Return copy of header struct. + const void *pvMd) // Pointer to the full file. +{ + const BYTE *pbMd; + + // Header data starts after signature. + pbMd = (const BYTE *) pvMd; + pbMd += sizeof(STORAGESIGNATURE); + pbMd += ((STORAGESIGNATURE*)pvMd)->GetVersionStringLength(); + PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd; + *pHeader = *pHdr; + pbMd += sizeof(STORAGEHEADER); + + // ECMA specifies that the flags field is "reserved, must be 0". + if (pHdr->GetFlags() != 0) + return NULL; + + // The pointer is now at the first stream in the list. + return ((PSTORAGESTREAM) pbMd); +} // MDFormat::GetFirstStream + +//***************************************************************************** +// Skip over the header and find the actual stream data. Secure version of +// GetFirstStream method. +// The header is supposed to be verified by VerifySignature. +// +// Returns pointer to the first stream (behind storage header) and the size of +// the remaining buffer in *pcbMd (could be 0). +// Returns NULL if there is not enough buffer for reading the headers. The *pcbMd +// could be changed if NULL returned. +// +// Caller has to check available buffer size before using the first stream. +//***************************************************************************** +PSTORAGESTREAM +MDFormat::GetFirstStream_Verify( + PSTORAGEHEADER pHeader, // Return copy of header struct. + const void *pvMd, // Pointer to the full file. + ULONG *pcbMd) // [in, out] Size of pvMd buffer (we don't want to read behind it) +{ + const BYTE *pbMd; + + // Header data starts after signature. + pbMd = (const BYTE *)pvMd; + // Check read buffer overflow + if (*pcbMd < sizeof(STORAGESIGNATURE)) + { + Debug_ReportError("Invalid MetaData - Storage signature doesn't fit."); + return NULL; + } + pbMd += sizeof(STORAGESIGNATURE); + *pcbMd -= sizeof(STORAGESIGNATURE); + + ULONG cbVersionString = ((STORAGESIGNATURE *)pvMd)->GetVersionStringLength(); + // Check read buffer overflow + if (*pcbMd < cbVersionString) + { + Debug_ReportError("Invalid MetaData storage signature - Version string doesn't fit."); + return NULL; + } + pbMd += cbVersionString; + *pcbMd -= cbVersionString; + + // Is there enough space for storage header? + if (*pcbMd < sizeof(STORAGEHEADER)) + { + Debug_ReportError("Invalid MetaData storage header - Storage header doesn't fit."); + return NULL; + } + PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd; + *pHeader = *pHdr; + pbMd += sizeof(STORAGEHEADER); + *pcbMd -= sizeof(STORAGEHEADER); + + // ECMA specifies that the flags field is "reserved, must be 0". + if (pHdr->GetFlags() != 0) + { + Debug_ReportError("Invalid MetaData storage header - Flags are not 0."); + return NULL; + } + + // The pointer is now at the first stream in the list. + return (PSTORAGESTREAM)pbMd; +} // MDFormat::GetFirstStream diff --git a/src/coreclr/md/runtime/mdinternaldisp.cpp b/src/coreclr/md/runtime/mdinternaldisp.cpp new file mode 100644 index 00000000000..49ceb265242 --- /dev/null +++ b/src/coreclr/md/runtime/mdinternaldisp.cpp @@ -0,0 +1,209 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// =========================================================================== +// File: MDInternalDisp.CPP +// + +// Notes: +// +// +// =========================================================================== +#include "stdafx.h" +#include "mdinternaldisp.h" +#include "mdinternalro.h" +#include "posterror.h" +#include "corpriv.h" +#include "assemblymdinternaldisp.h" +#include "pedecoder.h" +#include "metamodel.h" + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +// forward declaration +HRESULT GetInternalWithRWFormat( + LPVOID pData, + ULONG cbData, + DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC + REFIID riid, // [in] The interface desired. + void **ppIUnk); // [out] Return interface on success. + +//***************************************************************************** +// CheckFileFormat +// This function will determine if the in-memory image is a readonly, readwrite, +// or ICR format. +//***************************************************************************** +HRESULT +CheckFileFormat( + LPVOID pData, + ULONG cbData, + MDFileFormat *pFormat) // [OUT] the file format +{ + HRESULT hr = NOERROR; + STORAGEHEADER sHdr; // Header for the storage. + PSTORAGESTREAM pStream; // Pointer to each stream. + int i; + ULONG cbStreamBuffer; + + _ASSERTE(pFormat != NULL); + + *pFormat = MDFormat_Invalid; + + // Validate the signature of the format, or it isn't ours. + if (FAILED(hr = MDFormat::VerifySignature((PSTORAGESIGNATURE) pData, cbData))) + goto ErrExit; + + // Remaining buffer size behind the stream header (pStream). + cbStreamBuffer = cbData; + // Get back the first stream. + pStream = MDFormat::GetFirstStream_Verify(&sHdr, pData, &cbStreamBuffer); + if (pStream == NULL) + { + Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Loop through each stream and pick off the ones we need. + for (i = 0; i < sHdr.GetiStreams(); i++) + { + // Do we have enough buffer to read stream header? + if (cbStreamBuffer < sizeof(*pStream)) + { + Debug_ReportError("Stream header is not within MetaData block."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + // Get next stream. + PSTORAGESTREAM pNext = pStream->NextStream_Verify(); + if (pNext == NULL) + { // Stream header is corrupted. + Debug_ReportError("Invalid stream header - cannot get next stream header."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + // Check that stream header is within the buffer. + if (((LPBYTE)pStream >= ((LPBYTE)pData + cbData)) || + ((LPBYTE)pNext > ((LPBYTE)pData + cbData))) + { + Debug_ReportError("Stream header is not within MetaData block."); + hr = CLDB_E_FILE_CORRUPT; + goto ErrExit; + } + + // Check that the stream data starts and fits within the buffer. + // need two checks on size because of wraparound. + if ((pStream->GetOffset() > cbData) || + (pStream->GetSize() > cbData) || + ((pStream->GetSize() + pStream->GetOffset()) < pStream->GetOffset()) || + ((pStream->GetSize() + pStream->GetOffset()) > cbData)) + { + Debug_ReportError("Stream data are not within MetaData block."); + hr = CLDB_E_FILE_CORRUPT; + goto ErrExit; + } + + // Pick off the location and size of the data. + + if (strcmp(pStream->GetName(), COMPRESSED_MODEL_STREAM_A) == 0) + { + // Validate that only one of compressed/uncompressed is present. + if (*pFormat != MDFormat_Invalid) + { // Already found a good stream. + Debug_ReportError("Compressed model stream #~ is second important stream."); + hr = CLDB_E_FILE_CORRUPT; + goto ErrExit; + } + // Found the compressed meta data stream. + *pFormat = MDFormat_ReadOnly; + } + else if (strcmp(pStream->GetName(), ENC_MODEL_STREAM_A) == 0) + { + // Validate that only one of compressed/uncompressed is present. + if (*pFormat != MDFormat_Invalid) + { // Already found a good stream. + Debug_ReportError("ENC model stream #- is second important stream."); + hr = CLDB_E_FILE_CORRUPT; + goto ErrExit; + } + // Found the ENC meta data stream. + *pFormat = MDFormat_ReadWrite; + } + else if (strcmp(pStream->GetName(), SCHEMA_STREAM_A) == 0) + { + // Found the uncompressed format + *pFormat = MDFormat_ICR; + + // keep going. We may find the compressed format later. + // If so, we want to use the compressed format. + } + + // Pick off the next stream if there is one. + pStream = pNext; + cbStreamBuffer = (ULONG)((LPBYTE)pData + cbData - (LPBYTE)pNext); + } + + if (*pFormat == MDFormat_Invalid) + { // Didn't find a good stream. + Debug_ReportError("Cannot find MetaData stream."); + hr = CLDB_E_FILE_CORRUPT; + } + +ErrExit: + return hr; +} // CheckFileFormat + + + +//***************************************************************************** +// GetMDInternalInterface. +// This function will check the metadata section and determine if it should +// return an interface which implements ReadOnly or ReadWrite. +//***************************************************************************** +STDAPI GetMDInternalInterface( + LPVOID pData, + ULONG cbData, + DWORD flags, // [IN] ofRead or ofWrite. + REFIID riid, // [in] The interface desired. + void **ppIUnk) // [out] Return interface on success. +{ + HRESULT hr = NOERROR; + MDInternalRO *pInternalRO = NULL; + IMDCommon *pInternalROMDCommon = NULL; + MDFileFormat format; + + if (ppIUnk == NULL) + IfFailGo(E_INVALIDARG); + + // Determine the file format we're trying to read. + IfFailGo( CheckFileFormat(pData, cbData, &format) ); + + // Found a fully-compressed, read-only format. + if ( format == MDFormat_ReadOnly ) + { + pInternalRO = new (nothrow) MDInternalRO; + IfNullGo( pInternalRO ); + + IfFailGo( pInternalRO->Init(const_cast<void*>(pData), cbData) ); + + IfFailGo(pInternalRO->QueryInterface(riid, ppIUnk)); + + } + else + { + // Found a not-fully-compressed, ENC format. + _ASSERTE( format == MDFormat_ReadWrite ); + IfFailGo( GetInternalWithRWFormat( pData, cbData, flags, riid, ppIUnk ) ); + } + +ErrExit: + + // clean up + if ( pInternalRO ) + pInternalRO->Release(); + if ( pInternalROMDCommon ) + pInternalROMDCommon->Release(); + + return hr; +} // GetMDInternalInterface + + + +#endif //FEATURE_METADATA_INTERNAL_APIS diff --git a/src/coreclr/md/runtime/mdinternaldisp.h b/src/coreclr/md/runtime/mdinternaldisp.h new file mode 100644 index 00000000000..3da8f31eba5 --- /dev/null +++ b/src/coreclr/md/runtime/mdinternaldisp.h @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDInternalDispenser.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __MDInternalDispenser__h__ +#define __MDInternalDispenser__h__ + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +#include "mdinternalro.h" + +enum MDFileFormat +{ + MDFormat_ReadOnly = 0, + MDFormat_ReadWrite = 1, + MDFormat_ICR = 2, + MDFormat_Invalid = 3 +}; + +STDAPI GetMDInternalInterface( + LPVOID pData, // [IN] Buffer with the metadata. + ULONG cbData, // [IN] Size of the data in the buffer. + DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC + REFIID riid, // [in] The interface desired. + void **ppIUnk); // [out] Return interface on success. + +#endif //FEATURE_METADATA_INTERNAL_APIS + +#endif // __MDInternalDispenser__h__ diff --git a/src/coreclr/md/runtime/mdinternalro.cpp b/src/coreclr/md/runtime/mdinternalro.cpp new file mode 100644 index 00000000000..327eb8648a7 --- /dev/null +++ b/src/coreclr/md/runtime/mdinternalro.cpp @@ -0,0 +1,3481 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// =========================================================================== +// File: MDInternalRO.CPP +// + +// Notes: +// +// +// =========================================================================== +#include "stdafx.h" +#include "mdinternalro.h" +#include "metamodelro.h" +#include "liteweightstgdb.h" +#include "corhlpr.h" +#include "../compiler/regmeta.h" +#include "caparser.h" + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +__checkReturn +HRESULT _FillMDDefaultValue( + BYTE bType, + void const *pValue, + ULONG cbValue, + MDDefaultValue *pMDDefaultValue); + +#ifndef DACCESS_COMPILE +__checkReturn +HRESULT TranslateSigHelper( // S_OK or error. + IMDInternalImport *pImport, // [IN] import scope. + IMDInternalImport *pAssemImport, // [IN] import assembly scope. + const void *pbHashValue, // [IN] hash value for the import assembly. + ULONG cbHashValue, // [IN] count of bytes in the hash value. + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit *pAssemEmit, // [IN] assembly emit scope. + IMetaDataEmit *emit, // [IN] emit interface + CQuickBytes *pqkSigEmit, // [OUT] buffer to hold translated signature + ULONG *pcbSig); // [OUT] count of bytes in the translated signature +#endif //!DACCESS_COMPILE + +__checkReturn +HRESULT GetInternalWithRWFormat( + LPVOID pData, + ULONG cbData, + DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC + REFIID riid, // [in] The interface desired. + void **ppIUnk); // [out] Return interface on success. + +// forward declaration +__checkReturn +HRESULT MDApplyEditAndContinue( // S_OK or error. + IMDInternalImport **ppIMD, // [in, out] The metadata to be updated. + IMDInternalImportENC *pDeltaMD); // [in] The delta metadata. + + +//***************************************************************************** +// Constructor +//***************************************************************************** +MDInternalRO::MDInternalRO() + : m_pMethodSemanticsMap(0), + m_cRefs(1) +{ +} // MDInternalRO::MDInternalRO + + + +//***************************************************************************** +// Destructor +//***************************************************************************** +MDInternalRO::~MDInternalRO() +{ + m_LiteWeightStgdb.Uninit(); + if (m_pMethodSemanticsMap) + delete[] m_pMethodSemanticsMap; + m_pMethodSemanticsMap = 0; +} // MDInternalRO::~MDInternalRO + +//***************************************************************************** +// IUnknown +//***************************************************************************** +ULONG MDInternalRO::AddRef() +{ + return InterlockedIncrement(&m_cRefs); +} // MDInternalRO::AddRef + +ULONG MDInternalRO::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRefs); + if (cRef == 0) + delete this; + return cRef; +} // MDInternalRO::Release + +__checkReturn +HRESULT MDInternalRO::QueryInterface(REFIID riid, void **ppUnk) +{ + *ppUnk = 0; + + if (riid == IID_IUnknown) + *ppUnk = this; // ! QI for IID_IUnknown must return MDInternalRO. ConvertRO2RW() has dependency on this. + else if (riid == IID_IMDInternalImport) + *ppUnk = (IMDInternalImport *)this; + else if (riid == IID_IMDCommon) + *ppUnk = (IMDCommon *)this; + else + return E_NOINTERFACE; + AddRef(); + return S_OK; +} // MDInternalRO::QueryInterface + + +//***************************************************************************** +// Initialize +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::Init( + LPVOID pData, // points to meta data section in memory + ULONG cbData) // count of bytes in pData +{ + m_tdModule = COR_GLOBAL_PARENT_TOKEN; + + extern HRESULT _CallInitOnMemHelper(CLiteWeightStgdb<CMiniMd> *pStgdb, ULONG cbData, LPCVOID pData); + + return _CallInitOnMemHelper(&m_LiteWeightStgdb, cbData, (BYTE*) pData); +} // MDInternalRO::Init + +#ifndef DACCESS_COMPILE +//***************************************************************************** +// Given a scope, determine whether imported from a typelib. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::TranslateSigWithScope( + IMDInternalImport* pAssemImport, // [IN] import assembly scope. + const void* pbHashValue, // [IN] hash value for the import assembly. + ULONG cbHashValue, // [IN] count of bytes in the hash value. + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit* pAssemEmit, // [IN] assembly emit scope. + IMetaDataEmit* emit, // [IN] emit interface + CQuickBytes* pqkSigEmit, // [OUT] buffer to hold translated signature + ULONG* pcbSig) // [OUT] count of bytes in the translated signature +{ + return TranslateSigHelper( + this, + pAssemImport, + pbHashValue, + cbHashValue, + pbSigBlob, + cbSigBlob, + pAssemEmit, + emit, + pqkSigEmit, + pcbSig); +} // MDInternalRO::TranslateSigWithScope +#endif // DACCESS_COMPILE + +__checkReturn +HRESULT MDInternalRO::GetTypeDefRefTokenInTypeSpec(// return S_FALSE if enclosing type does not have a token + mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at + mdToken *tkEnclosedToken) // [OUT] The enclosed type token +{ + return m_LiteWeightStgdb.m_MiniMd.GetTypeDefRefTokenInTypeSpec(tkTypeSpec, tkEnclosedToken); +} // MDInternalRO::GetTypeDefRefTokenInTypeSpec + +#ifndef DACCESS_COMPILE +//***************************************************************************** +// Given a scope, return the number of tokens in a given table +//***************************************************************************** +ULONG MDInternalRO::GetCountWithTokenKind( // return hresult + DWORD tkKind) // [IN] pass in the kind of token. +{ + ULONG ulCount = m_LiteWeightStgdb.m_MiniMd.CommonGetRowCount(tkKind); + if (tkKind == mdtTypeDef) + { + // Remove global typedef from the count of typedefs (and handle the case where there is no global typedef) + if (ulCount > 0) + ulCount--; + } + return ulCount; +} // MDInternalRO::GetCountWithTokenKind +#endif //!DACCESS_COMPILE + +//******************************************************************************* +// Enumerator helpers +//******************************************************************************* + + +//***************************************************************************** +// enumerator init for typedef +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::EnumTypeDefInit( // return hresult + HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data +{ + HRESULT hr = NOERROR; + + _ASSERTE(phEnum); + + HENUMInternal::ZeroEnum(phEnum); + phEnum->m_tkKind = mdtTypeDef; + phEnum->m_EnumType = MDSimpleEnum; + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountTypeDefs(); + + // Skip over the global model typedef + // + // phEnum->u.m_ulCur : the current rid that is not yet enumerated + // phEnum->u.m_ulStart : the first rid that will be returned by enumerator + // phEnum->u.m_ulEnd : the last rid that will be returned by enumerator + phEnum->u.m_ulStart = phEnum->u.m_ulCur = 2; + phEnum->u.m_ulEnd = phEnum->m_ulCount + 1; + if (phEnum->m_ulCount > 0) + phEnum->m_ulCount --; + + return hr; +} // MDInternalRO::EnumTypeDefInit + +//***************************************************************************** +// Enumerator init for MethodImpl. The second HENUMInternal* parameter is +// only used for the R/W version of the MetaData. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::EnumMethodImplInit( // return hresult + mdTypeDef td, // [IN] TypeDef over which to scope the enumeration. + HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens. + HENUMInternal *phEnumDecl) // [OUT] buffer to fill for enumerator data for MethodDecl tokens. +{ + return EnumInit(TBL_MethodImpl << 24, td, phEnumBody); +} // MDInternalRO::EnumMethodImplInit + +//***************************************************************************** +// get the number of MethodImpls in a scope +//***************************************************************************** +ULONG MDInternalRO::EnumMethodImplGetCount( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. +{ + _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl)); + return phEnumBody->m_ulCount; +} // MDInternalRO::EnumMethodImplGetCount + + +//***************************************************************************** +// enumerator for MethodImpl. +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::EnumMethodImplNext( // return hresult + HENUMInternal *phEnumBody, // [IN] input enum for MethodBody + HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl + mdToken *ptkBody, // [OUT] return token for MethodBody + mdToken *ptkDecl) // [OUT] return token for MethodDecl +{ + HRESULT hr; + MethodImplRec *pRecord; + + _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl)); + _ASSERTE(ptkBody && ptkDecl); + + if (phEnumBody->u.m_ulCur >= phEnumBody->u.m_ulEnd) + { + return S_FALSE; + } + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodImplRecord(phEnumBody->u.m_ulCur, &pRecord)); + *ptkBody = m_LiteWeightStgdb.m_MiniMd.getMethodBodyOfMethodImpl(pRecord); + *ptkDecl = m_LiteWeightStgdb.m_MiniMd.getMethodDeclarationOfMethodImpl(pRecord); + phEnumBody->u.m_ulCur++; + + return S_OK; +} // MDInternalRO::EnumMethodImplNext + +//***************************************** +// Reset the enumerator to the beginning. +//***************************************** +void MDInternalRO::EnumMethodImplReset( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. +{ + _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl)); + _ASSERTE(phEnumBody->m_EnumType == MDSimpleEnum); + + phEnumBody->u.m_ulCur = phEnumBody->u.m_ulStart; +} // MDInternalRO::EnumMethodImplReset + + +//***************************************** +// Close the enumerator. +//***************************************** +void MDInternalRO::EnumMethodImplClose( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator. +{ + _ASSERTE(phEnumBody && ((phEnumBody->m_tkKind >> 24) == TBL_MethodImpl)); + _ASSERTE(phEnumBody->m_EnumType == MDSimpleEnum); +} // MDInternalRO::EnumMethodImplClose + + +//****************************************************************************** +// enumerator for global functions +//****************************************************************************** +__checkReturn +HRESULT MDInternalRO::EnumGlobalFunctionsInit( // return hresult + HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data +{ + return EnumInit(mdtMethodDef, m_tdModule, phEnum); +} + + +//****************************************************************************** +// enumerator for global Fields +//****************************************************************************** +__checkReturn +HRESULT MDInternalRO::EnumGlobalFieldsInit( // return hresult + HENUMInternal *phEnum) // [OUT] buffer to fill for enumerator data +{ + return EnumInit(mdtFieldDef, m_tdModule, phEnum); +} + + +//***************************************** +// Enumerator initializer +//***************************************** +__checkReturn +HRESULT MDInternalRO::EnumInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + mdToken tkParent, // [IN] token to scope the search + HENUMInternal *phEnum) // [OUT] the enumerator to fill +{ + HRESULT hr = S_OK; + ULONG ulMax = 0; + + // Vars for query. + _ASSERTE(phEnum); + HENUMInternal::ZeroEnum(phEnum); + + // cache the tkKind and the scope + phEnum->m_tkKind = TypeFromToken(tkKind); + + TypeDefRec *pRec; + + phEnum->m_EnumType = MDSimpleEnum; + + switch (TypeFromToken(tkKind)) + { + case mdtFieldDef: + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec)); + phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getFieldListOfTypeDef(pRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(tkParent), &(phEnum->u.m_ulEnd))); + break; + + case mdtMethodDef: + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(tkParent), &pRec)); + phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getMethodListOfTypeDef(pRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(tkParent), &(phEnum->u.m_ulEnd))); + break; + + case mdtGenericParam: + _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef || TypeFromToken(tkParent) == mdtMethodDef); + + if (TypeFromToken(tkParent) != mdtTypeDef && TypeFromToken(tkParent) != mdtMethodDef) + IfFailGo(CLDB_E_FILE_CORRUPT); + + if (TypeFromToken(tkParent) == mdtTypeDef) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getGenericParamsForTypeDef( + RidFromToken(tkParent), + &phEnum->u.m_ulEnd, + &(phEnum->u.m_ulStart))); + } + else + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getGenericParamsForMethodDef( + RidFromToken(tkParent), + &phEnum->u.m_ulEnd, + &(phEnum->u.m_ulStart))); + } + break; + + case mdtGenericParamConstraint: + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getGenericParamConstraintsForGenericParam( + RidFromToken(tkParent), + &phEnum->u.m_ulEnd, + &phEnum->u.m_ulStart)); + break; + + case mdtInterfaceImpl: + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getInterfaceImplsForTypeDef(RidFromToken(tkParent), &phEnum->u.m_ulEnd, &phEnum->u.m_ulStart)); + break; + + case mdtProperty: + RID ridPropertyMap; + PropertyMapRec *pPropertyMapRec; + + // get the starting/ending rid of properties of this typedef + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindPropertyMapFor(RidFromToken(tkParent), &ridPropertyMap)); + if (!InvalidRid(ridPropertyMap)) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &(phEnum->u.m_ulEnd))); + ulMax = m_LiteWeightStgdb.m_MiniMd.getCountPropertys() + 1; + if(phEnum->u.m_ulStart == 0) phEnum->u.m_ulStart = 1; + if(phEnum->u.m_ulEnd > ulMax) phEnum->u.m_ulEnd = ulMax; + if(phEnum->u.m_ulStart > phEnum->u.m_ulEnd) phEnum->u.m_ulStart = phEnum->u.m_ulEnd; + } + break; + + case mdtEvent: + RID ridEventMap; + EventMapRec *pEventMapRec; + + // get the starting/ending rid of events of this typedef + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindEventMapFor(RidFromToken(tkParent), &ridEventMap)); + if (!InvalidRid(ridEventMap)) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetEventMapRecord(ridEventMap, &pEventMapRec)); + phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getEventListOfEventMap(pEventMapRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndEventListOfEventMap(ridEventMap, &(phEnum->u.m_ulEnd))); + ulMax = m_LiteWeightStgdb.m_MiniMd.getCountEvents() + 1; + if(phEnum->u.m_ulStart == 0) phEnum->u.m_ulStart = 1; + if(phEnum->u.m_ulEnd > ulMax) phEnum->u.m_ulEnd = ulMax; + if(phEnum->u.m_ulStart > phEnum->u.m_ulEnd) phEnum->u.m_ulStart = phEnum->u.m_ulEnd; + } + break; + + case mdtParamDef: + _ASSERTE(TypeFromToken(tkParent) == mdtMethodDef); + + MethodRec *pMethodRec; + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(tkParent), &pMethodRec)); + + // figure out the start rid and end rid of the parameter list of this methoddef + phEnum->u.m_ulStart = m_LiteWeightStgdb.m_MiniMd.getParamListOfMethod(pMethodRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndParamListOfMethod(RidFromToken(tkParent), &(phEnum->u.m_ulEnd))); + break; + case mdtCustomAttribute: + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getCustomAttributeForToken(tkParent, &phEnum->u.m_ulEnd, &phEnum->u.m_ulStart)); + break; + case mdtAssemblyRef: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountAssemblyRefs() + 1; + break; + case mdtFile: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountFiles() + 1; + break; + case mdtExportedType: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountExportedTypes() + 1; + break; + case mdtManifestResource: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountManifestResources() + 1; + break; + case mdtModuleRef: + _ASSERTE(IsNilToken(tkParent)); + phEnum->u.m_ulStart = 1; + phEnum->u.m_ulEnd = m_LiteWeightStgdb.m_MiniMd.getCountModuleRefs() + 1; + break; + case (TBL_MethodImpl << 24): + _ASSERTE(! IsNilToken(tkParent)); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getMethodImplsForClass( + RidFromToken(tkParent), + &phEnum->u.m_ulEnd, + &phEnum->u.m_ulStart)); + break; + default: + _ASSERTE(!"ENUM INIT not implemented for the compressed format!"); + IfFailGo(E_NOTIMPL); + break; + } + + // If the count is negative, the metadata is corrupted somehow. + if (phEnum->u.m_ulEnd < phEnum->u.m_ulStart) + IfFailGo(CLDB_E_FILE_CORRUPT); + + phEnum->m_ulCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart; + phEnum->u.m_ulCur = phEnum->u.m_ulStart; + +ErrExit: + // we are done + return hr; +} + + +//***************************************** +// Enumerator initializer +//***************************************** +__checkReturn +HRESULT MDInternalRO::EnumAllInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + HENUMInternal *phEnum) // [OUT] the enumerator to fill +{ + HRESULT hr = S_OK; + + // Vars for query. + _ASSERTE(phEnum); + HENUMInternal::ZeroEnum(phEnum); + + // cache the tkKind and the scope + phEnum->m_tkKind = TypeFromToken(tkKind); + phEnum->m_EnumType = MDSimpleEnum; + + switch (TypeFromToken(tkKind)) + { + case mdtTypeRef: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountTypeRefs(); + break; + + case mdtMemberRef: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountMemberRefs(); + break; + + case mdtSignature: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountStandAloneSigs(); + break; + + case mdtMethodDef: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountMethods(); + break; + + case mdtMethodSpec: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountMethodSpecs(); + break; + + case mdtFieldDef: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountFields(); + break; + + case mdtTypeSpec: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountTypeSpecs(); + break; + + case mdtAssemblyRef: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountAssemblyRefs(); + break; + + case mdtModuleRef: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountModuleRefs(); + break; + + case mdtTypeDef: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountTypeDefs(); + break; + + case mdtFile: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountFiles(); + break; + + case mdtCustomAttribute: + phEnum->m_ulCount = m_LiteWeightStgdb.m_MiniMd.getCountCustomAttributes(); + break; + + default: + _ASSERTE(!"Bad token kind!"); + break; + } + phEnum->u.m_ulStart = phEnum->u.m_ulCur = 1; + phEnum->u.m_ulEnd = phEnum->m_ulCount + 1; + + // we are done + return hr; +} // MDInternalRO::EnumAllInit + +//***************************************** +// Enumerator initializer for CustomAttributes +//***************************************** +__checkReturn +HRESULT MDInternalRO::EnumCustomAttributeByNameInit(// return S_FALSE if record not found + mdToken tkParent, // [IN] token to scope the search + LPCSTR szName, // [IN] CustomAttribute's name to scope the search + HENUMInternal *phEnum) // [OUT] the enumerator to fill +{ + return m_LiteWeightStgdb.m_MiniMd.CommonEnumCustomAttributeByName(tkParent, szName, false, phEnum); +} // MDInternalRO::EnumCustomAttributeByNameInit + +//***************************************** +// Nagivator helper to navigate back to the parent token given a token. +// For example, given a memberdef token, it will return the containing typedef. +// +// the mapping is as following: +// ---given child type---------parent type +// mdMethodDef mdTypeDef +// mdFieldDef mdTypeDef +// mdInterfaceImpl mdTypeDef +// mdParam mdMethodDef +// mdProperty mdTypeDef +// mdEvent mdTypeDef +// +//***************************************** +__checkReturn +HRESULT MDInternalRO::GetParentToken( + mdToken tkChild, // [IN] given child token + mdToken *ptkParent) // [OUT] returning parent +{ + HRESULT hr = NOERROR; + + _ASSERTE(ptkParent); + + switch (TypeFromToken(tkChild)) + { + case mdtTypeDef: + hr = GetNestedClassProps(tkChild, ptkParent); + // If not found, the *ptkParent has to be left unchanged! (callers depend on that) + if (hr == CLDB_E_RECORD_NOTFOUND) + { + hr = S_OK; + } + break; + + case mdtMethodDef: + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindParentOfMethod(RidFromToken(tkChild), (RID *)ptkParent)); + RidToToken(*ptkParent, mdtTypeDef); + break; + + case mdtMethodSpec: + { + MethodSpecRec *pRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSpecRecord(RidFromToken(tkChild), &pRec)); + *ptkParent = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSpec(pRec); + break; + } + + case mdtFieldDef: + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindParentOfField(RidFromToken(tkChild), (RID *)ptkParent)); + RidToToken(*ptkParent, mdtTypeDef); + break; + + case mdtParamDef: + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindParentOfParam(RidFromToken(tkChild), (RID *)ptkParent)); + RidToToken(*ptkParent, mdtMethodDef); + break; + + case mdtMemberRef: + { + MemberRefRec *pRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMemberRefRecord(RidFromToken(tkChild), &pRec)); + *ptkParent = m_LiteWeightStgdb.m_MiniMd.getClassOfMemberRef(pRec); + break; + } + + case mdtCustomAttribute: + { + CustomAttributeRec *pRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetCustomAttributeRecord(RidFromToken(tkChild), &pRec)); + *ptkParent = m_LiteWeightStgdb.m_MiniMd.getParentOfCustomAttribute(pRec); + break; + } + + case mdtEvent: + hr = m_LiteWeightStgdb.m_MiniMd.FindParentOfEventHelper(tkChild, ptkParent); + break; + + case mdtProperty: + hr = m_LiteWeightStgdb.m_MiniMd.FindParentOfPropertyHelper(tkChild, ptkParent); + break; + + default: + _ASSERTE(!"NYI: for compressed format!"); + break; + } + return hr; +} // MDInternalRO::GetParentToken + + + +//***************************************************************************** +// Get information about a CustomAttribute. +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetCustomAttributeProps( // S_OK or error. + mdCustomAttribute at, // The attribute. + mdToken *ptkType) // Put attribute type here. +{ + HRESULT hr; + _ASSERTE(TypeFromToken(at) == mdtCustomAttribute); + + // Do a linear search on compressed version as we do not want to + // depends on ICR. + // + CustomAttributeRec *pCustomAttributeRec; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetCustomAttributeRecord(RidFromToken(at), &pCustomAttributeRec)); + *ptkType = m_LiteWeightStgdb.m_MiniMd.getTypeOfCustomAttribute(pCustomAttributeRec); + return S_OK; +} // MDInternalRO::GetCustomAttributeProps + +//***************************************************************************** +// return custom value +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetCustomAttributeAsBlob( + mdCustomAttribute cv, // [IN] given custom attribute token + void const **ppBlob, // [OUT] return the pointer to internal blob + ULONG *pcbSize) // [OUT] return the size of the blob +{ + HRESULT hr; + _ASSERTE(ppBlob && pcbSize && TypeFromToken(cv) == mdtCustomAttribute); + + CustomAttributeRec *pCustomAttributeRec; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetCustomAttributeRecord(RidFromToken(cv), &pCustomAttributeRec)); + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getValueOfCustomAttribute(pCustomAttributeRec, (const BYTE **)ppBlob, pcbSize)); + return S_OK; +} // MDInternalRO::GetCustomAttributeAsBlob + +//***************************************************************************** +// Helper function to lookup and retrieve a CustomAttribute. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + __deref_out_bcount(*pcbData) const void **ppData, // [OUT] Put pointer to data here. + __out ULONG *pcbData) // [OUT] Put size of data here. +{ + return m_LiteWeightStgdb.m_MiniMd.CommonGetCustomAttributeByNameEx(tkObj, szName, NULL, ppData, pcbData); +} // MDInternalRO::GetCustomAttributeByName + + +//***************************************************************************** +// return the name of a custom attribute +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetNameOfCustomAttribute( // S_OK or error. + mdCustomAttribute mdAttribute, // [IN] The Custom Attribute + LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute. + LPCUTF8 *pszName) // [OUT] Name of Custom Attribute. +{ + _ASSERTE(TypeFromToken(mdAttribute) == mdtCustomAttribute); + + HRESULT hr = m_LiteWeightStgdb.m_MiniMd.CommonGetNameOfCustomAttribute(RidFromToken(mdAttribute), pszNamespace, pszName); + return (hr == S_FALSE) ? E_FAIL : hr; +} // MDInternalRO::GetNameOfCustomAttribute + +//***************************************************************************** +// return scope properties +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetScopeProps( + LPCSTR *pszName, // [OUT] scope name + GUID *pmvid) // [OUT] version id +{ + HRESULT hr; + + ModuleRec *pModuleRec; + + // there is only one module record + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetModuleRecord(1, &pModuleRec)); + + if (pmvid != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getMvidOfModule(pModuleRec, pmvid)); + } + if (pszName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfModule(pModuleRec, pszName)); + } + + return S_OK; +} // MDInternalRO::GetScopeProps + + +//***************************************************************************** +// Compare two signatures from the same scope. Varags signatures need to be +// preprocessed so they only contain the fixed part. +//***************************************************************************** +BOOL MDInternalRO::CompareSignatures(PCCOR_SIGNATURE pvFirstSigBlob, // First signature + DWORD cbFirstSigBlob, // + PCCOR_SIGNATURE pvSecondSigBlob, // Second signature + DWORD cbSecondSigBlob, // + void * SigArguments) // No additional arguments required +{ + if (cbFirstSigBlob != cbSecondSigBlob || memcmp(pvFirstSigBlob, pvSecondSigBlob, cbSecondSigBlob)) + return FALSE; + else + return TRUE; +} + +//***************************************************************************** +// Find a given member in a TypeDef (typically a class). +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::FindMethodDef( // S_OK or error. + mdTypeDef classdef, // The owning class of the member. + LPCSTR szName, // Name of the member in utf8. + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodDef *pmethoddef) // Put MemberDef token here. +{ + + return FindMethodDefUsingCompare(classdef, + szName, + pvSigBlob, + cbSigBlob, + CompareSignatures, + NULL, + pmethoddef); +} + +//***************************************************************************** +// Find a given member in a TypeDef (typically a class). +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::FindMethodDefUsingCompare( // S_OK or error. + mdTypeDef classdef, // The owning class of the member. + LPCSTR szName, // Name of the member in utf8. + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + PSIGCOMPARE SigCompare, // [IN] Signature comparison routine + void* pSigArgs, // [IN] Additional arguments passed to signature compare + mdMethodDef *pmethoddef) // Put MemberDef token here. +{ + HRESULT hr = NOERROR; + PCCOR_SIGNATURE pvSigTemp = pvSigBlob; + CQuickBytes qbSig; + + _ASSERTE(szName && pmethoddef); + + // initialize the output parameter + *pmethoddef = mdMethodDefNil; + + // check to see if this is a vararg signature + if ( isCallConv(CorSigUncompressCallingConv(pvSigTemp), IMAGE_CEE_CS_CALLCONV_VARARG) ) + { + // Get the fix part of VARARG signature + IfFailGo( _GetFixedSigOfVarArg(pvSigBlob, cbSigBlob, &qbSig, &cbSigBlob) ); + pvSigBlob = (PCCOR_SIGNATURE) qbSig.Ptr(); + } + + // Do a linear search on compressed version + // + RID ridMax; + MethodRec *pMethodRec; + LPCUTF8 szCurMethodName; + void const *pvCurMethodSig; + ULONG cbSig; + TypeDefRec *pRec; + RID ridStart; + + // get the typedef record + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(classdef), &pRec)); + + // get the range of methoddef rids given the classdef + ridStart = m_LiteWeightStgdb.m_MiniMd.getMethodListOfTypeDef(pRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndMethodListOfTypeDef(RidFromToken(classdef), &ridMax)); + + // loop through each methoddef + for (; ridStart < ridMax; ridStart++) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(ridStart, &pMethodRec)); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfMethod(pMethodRec, &szCurMethodName)); + if (strcmp(szCurMethodName, szName) == 0) + { + // name match, now check the signature if specified. + if (cbSigBlob && SigCompare) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMethod(pMethodRec, (PCCOR_SIGNATURE *)&pvCurMethodSig, &cbSig)); + // Signature comparison is required + // Note that if pvSigBlob is vararg, we already preprocess it so that + // it only contains the fix part. Therefore, it still should be an exact + // match!!!. + // + if(SigCompare((PCCOR_SIGNATURE) pvCurMethodSig, cbSig, pvSigBlob, cbSigBlob, pSigArgs) == FALSE) + continue; + } + // Ignore PrivateScope methods. + if (IsMdPrivateScope(m_LiteWeightStgdb.m_MiniMd.getFlagsOfMethod(pMethodRec))) + continue; + // found the match + *pmethoddef = TokenFromRid(ridStart, mdtMethodDef); + goto ErrExit; + } + } + hr = CLDB_E_RECORD_NOTFOUND; + +ErrExit: + return hr; +} + +//***************************************************************************** +// Find a given param of a Method. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::FindParamOfMethod(// S_OK or error. + mdMethodDef md, // [IN] The owning method of the param. + ULONG iSeq, // [IN] The sequence # of the param. + mdParamDef *pparamdef) // [OUT] Put ParamDef token here. +{ + HRESULT hr; + ParamRec *pParamRec; + RID ridStart, ridEnd; + + _ASSERTE(TypeFromToken(md) == mdtMethodDef && pparamdef); + + // get the methoddef record + MethodRec *pMethodRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + + // figure out the start rid and end rid of the parameter list of this methoddef + ridStart = m_LiteWeightStgdb.m_MiniMd.getParamListOfMethod(pMethodRec); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getEndParamListOfMethod(RidFromToken(md), &ridEnd)); + + // Ensure that the paramList is valid. If the count is negative, the metadata + // is corrupted somehow. Thus, return CLDB_E_FILE_CORRUPT. + if (ridEnd < ridStart) + return CLDB_E_FILE_CORRUPT; + + // loop through each param + //<TODO>@consider: parameters are sorted by sequence. Maybe a binary search? + //</TODO> + for (; ridStart < ridEnd; ridStart++) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetParamRecord(ridStart, &pParamRec)); + if (iSeq == m_LiteWeightStgdb.m_MiniMd.getSequenceOfParam(pParamRec)) + { + // parameter has the sequence number matches what we are looking for + *pparamdef = TokenFromRid(ridStart, mdtParamDef); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} + + + +//***************************************************************************** +// return a pointer which points to meta data's internal string +// return the the type name in utf8 +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetNameOfTypeDef( // return hresult + mdTypeDef classdef, // given typedef + LPCSTR* pszname, // pointer to an internal UTF8 string + LPCSTR* psznamespace) // pointer to the namespace. +{ + HRESULT hr; + + if (pszname != NULL) + { + *pszname = NULL; + } + if (psznamespace != NULL) + { + *psznamespace = NULL; + } + + if (TypeFromToken(classdef) == mdtTypeDef) + { + TypeDefRec *pTypeDefRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(classdef), &pTypeDefRec)); + + if (pszname != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeDef(pTypeDefRec, pszname)); + } + + if (psznamespace != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeDef(pTypeDefRec, psznamespace)); + } + return S_OK; + } + + _ASSERTE(!"Invalid argument(s) of GetNameOfTypeDef"); + return CLDB_E_INTERNALERROR; +} // MDInternalRO::GetNameOfTypeDef + + +__checkReturn +HRESULT MDInternalRO::GetIsDualOfTypeDef(// return hresult + mdTypeDef classdef, // given classdef + ULONG *pDual) // [OUT] return dual flag here. +{ + ULONG iFace=0; // Iface type. + HRESULT hr; // A result. + + hr = GetIfaceTypeOfTypeDef(classdef, &iFace); + if (hr == S_OK) + *pDual = (iFace == ifDual); + else + *pDual = 1; + + return hr; +} // MDInternalRO::GetIsDualOfTypeDef + +__checkReturn +HRESULT MDInternalRO::GetIfaceTypeOfTypeDef( + mdTypeDef classdef, // [IN] given classdef. + ULONG *pIface) // [OUT] 0=dual, 1=vtable, 2=dispinterface +{ + HRESULT hr; // A result. + const BYTE *pVal; // The custom value. + ULONG cbVal; // Size of the custom value. + ULONG ItfType = DEFAULT_COM_INTERFACE_TYPE; // Set the interface type to the default. + + // If the value is not present, the class is assumed dual. + hr = GetCustomAttributeByName(classdef, INTEROP_INTERFACETYPE_TYPE, (const void**)&pVal, &cbVal); + if (hr == S_OK) + { + CustomAttributeParser cap(pVal, cbVal); + BYTE u1; + if (SUCCEEDED(cap.SkipProlog()) && + SUCCEEDED(cap.GetU1(&u1))) + { + ItfType = u1; + } + if (ItfType >= ifLast) + ItfType = DEFAULT_COM_INTERFACE_TYPE; + } + + // Set the return value. + *pIface = ItfType; + + return hr; +} // MDInternalRO::GetIfaceTypeOfTypeDef + +//***************************************************************************** +// Given a methoddef, return a pointer to methoddef's name +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetNameOfMethodDef( + mdMethodDef md, + LPCSTR *pszMethodName) +{ + HRESULT hr; + MethodRec *pMethodRec; + *pszMethodName = NULL; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfMethod(pMethodRec, pszMethodName)); + return S_OK; +} // MDInternalRO::GetNameOfMethodDef + +//***************************************************************************** +// Given a methoddef, return a pointer to methoddef's signature and methoddef's name +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetNameAndSigOfMethodDef( + mdMethodDef methoddef, // [IN] given memberdef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszMethodName) +{ + HRESULT hr; + // Output parameter should not be NULL + _ASSERTE(ppvSigBlob && pcbSigBlob); + _ASSERTE(TypeFromToken(methoddef) == mdtMethodDef); + + MethodRec *pMethodRec; + *pszMethodName = NULL; + *ppvSigBlob = NULL; + *pcbSigBlob = 0; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMethod(pMethodRec, (PCCOR_SIGNATURE *)ppvSigBlob, pcbSigBlob)); + + return GetNameOfMethodDef(methoddef, pszMethodName); +} // MDInternalRO::GetNameAndSigOfMethodDef + +//***************************************************************************** +// Given a FieldDef, return a pointer to FieldDef's name in UTF8 +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetNameOfFieldDef( // return hresult + mdFieldDef fd, // given field + LPCSTR *pszFieldName) +{ + HRESULT hr; + FieldRec *pFieldRec; + *pszFieldName = NULL; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfField(pFieldRec, pszFieldName)); + return S_OK; +} // MDInternalRO::GetNameOfFieldDef + + +//***************************************************************************** +// Given a classdef, return the name and namespace of the typeref +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetNameOfTypeRef( // return TypeDef's name + mdTypeRef classref, // [IN] given typeref + LPCSTR *psznamespace, // [OUT] return typeref name + LPCSTR *pszname) // [OUT] return typeref namespace + +{ + _ASSERTE(TypeFromToken(classref) == mdtTypeRef); + + HRESULT hr; + TypeRefRec *pTypeRefRec; + + *psznamespace = NULL; + *pszname = NULL; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, psznamespace)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeRef(pTypeRefRec, pszname)); + return S_OK; +} + +//***************************************************************************** +// return the resolutionscope of typeref +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetResolutionScopeOfTypeRef( + mdTypeRef classref, // given classref + mdToken *ptkResolutionScope) +{ + _ASSERTE(TypeFromToken(classref) == mdtTypeRef && RidFromToken(classref)); + HRESULT hr; + + TypeRefRec *pTypeRefRec; + + *ptkResolutionScope = mdTokenNil; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(RidFromToken(classref), &pTypeRefRec)); + *ptkResolutionScope = m_LiteWeightStgdb.m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec); + return S_OK; +} // MDInternalRO::GetResolutionScopeOfTypeRef + +//***************************************************************************** +// Given a name, find the corresponding TypeRef. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::FindTypeRefByName( // S_OK or error. + LPCSTR szNamespace, // [IN] Namespace for the TypeRef. + LPCSTR szName, // [IN] Name of the TypeRef. + mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef. + mdTypeRef *ptk) // [OUT] TypeRef token returned. +{ + HRESULT hr = NOERROR; + + _ASSERTE(ptk); + + // initialize the output parameter + *ptk = mdTypeRefNil; + + // Treat no namespace as empty string. + if (!szNamespace) + szNamespace = ""; + + // Do a linear search on compressed version as we do not want to + // depends on ICR. + // + ULONG cTypeRefRecs = m_LiteWeightStgdb.m_MiniMd.getCountTypeRefs(); + TypeRefRec *pTypeRefRec; + LPCUTF8 szNamespaceTmp; + LPCUTF8 szNameTmp; + mdToken tkRes; + + for (ULONG i = 1; i <= cTypeRefRecs; i++) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(i, &pTypeRefRec)); + tkRes = m_LiteWeightStgdb.m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec); + + if (IsNilToken(tkRes)) + { + if (!IsNilToken(tkResolutionScope)) + continue; + } + else if (tkRes != tkResolutionScope) + continue; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp)); + if (strcmp(szNamespace, szNamespaceTmp)) + continue; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeRef(pTypeRefRec, &szNameTmp)); + if (!strcmp(szNameTmp, szName)) + { + *ptk = TokenFromRid(i, mdtTypeRef); + goto ErrExit; + } + } + + // cannot find the typedef + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; +} + +//***************************************************************************** +// return flags for a given class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetTypeDefProps( + mdTypeDef td, // given classdef + DWORD *pdwAttr, // return flags on class + mdToken *ptkExtends) // [OUT] Put base class TypeDef/TypeRef here. +{ + HRESULT hr; + TypeDefRec *pTypeDefRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + + if (ptkExtends) + { + *ptkExtends = m_LiteWeightStgdb.m_MiniMd.getExtendsOfTypeDef(pTypeDefRec); + } + if (pdwAttr) + { + *pdwAttr = m_LiteWeightStgdb.m_MiniMd.getFlagsOfTypeDef(pTypeDefRec); + } + + return S_OK; +} + + +//***************************************************************************** +// return guid pointer to MetaData internal guid pool given a given class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetItemGuid( // return hresult + mdToken tkObj, // given item + CLSID *pGuid) +{ + + HRESULT hr; // A result. + const BYTE *pBlob = NULL; // Blob with dispid. + ULONG cbBlob; // Length of blob. + int ix; // Loop control. + + // Get the GUID, if any. + hr = GetCustomAttributeByName(tkObj, INTEROP_GUID_TYPE, (const void**)&pBlob, &cbBlob); + if (hr != S_FALSE) + { + // Should be in format. Total length == 41 + // <0x0001><0x24>01234567-0123-0123-0123-001122334455<0x0000> + if ((cbBlob != 41) || (GET_UNALIGNED_VAL16(pBlob) != 1)) + IfFailGo(E_INVALIDARG); + + WCHAR wzBlob[40]; // Wide char format of guid. + for (ix=1; ix<=36; ++ix) + wzBlob[ix] = pBlob[ix+2]; + wzBlob[0] = '{'; + wzBlob[37] = '}'; + wzBlob[38] = 0; + hr = IIDFromString(wzBlob, pGuid); + } + else + *pGuid = GUID_NULL; + +ErrExit: + return hr; +} // MDInternalRO::GetItemGuid + + +//***************************************************************************** +// // get enclosing class of NestedClass +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetNestedClassProps( // S_OK or error + mdTypeDef tkNestedClass, // [IN] NestedClass token. + mdTypeDef *ptkEnclosingClass) // [OUT] EnclosingClass token. +{ + HRESULT hr; + _ASSERTE(TypeFromToken(tkNestedClass) == mdtTypeDef && ptkEnclosingClass); + + RID rid; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindNestedClassFor(RidFromToken(tkNestedClass), &rid)); + + if (InvalidRid(rid)) + { + return CLDB_E_RECORD_NOTFOUND; + } + else + { + NestedClassRec *pRecord; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(rid, &pRecord)); + *ptkEnclosingClass = m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pRecord); + return S_OK; + } +} + +//******************************************************************************* +// Get count of Nested classes given the enclosing class. +//******************************************************************************* +__checkReturn +HRESULT +MDInternalRO::GetCountNestedClasses( // return count of Nested classes. + mdTypeDef tkEnclosingClass, // [IN]Enclosing class. + ULONG *pcNestedClassesCount) +{ + HRESULT hr; + ULONG ulCount; + ULONG ulRetCount = 0; + NestedClassRec *pRecord; + + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef && !IsNilToken(tkEnclosingClass)); + + *pcNestedClassesCount = 0; + + ulCount = m_LiteWeightStgdb.m_MiniMd.getCountNestedClasss(); + + for (ULONG i = 1; i <= ulCount; i++) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(i, &pRecord)); + if (tkEnclosingClass == m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pRecord)) + ulRetCount++; + } + *pcNestedClassesCount = ulRetCount; + return S_OK; +} // MDInternalRO::GetCountNestedClasses + +//******************************************************************************* +// Return array of Nested classes given the enclosing class. +//******************************************************************************* +__checkReturn +HRESULT +MDInternalRO::GetNestedClasses( // Return actual count. + mdTypeDef tkEnclosingClass, // [IN] Enclosing class. + mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens. + ULONG ulNestedClasses, // [IN] Size of array. + ULONG *pcNestedClasses) +{ + HRESULT hr; + ULONG ulCount; + ULONG ulRetCount = 0; + NestedClassRec *pRecord; + + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef && + !IsNilToken(tkEnclosingClass)); + + *pcNestedClasses = 0; + + ulCount = m_LiteWeightStgdb.m_MiniMd.getCountNestedClasss(); + + for (ULONG i = 1; i <= ulCount; i++) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(i, &pRecord)); + if (tkEnclosingClass == m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pRecord)) + { + if (ovadd_le(ulRetCount, 1, ulNestedClasses)) // ulRetCount is 0 based. + rNestedClasses[ulRetCount] = m_LiteWeightStgdb.m_MiniMd.getNestedClassOfNestedClass(pRecord); + ulRetCount++; + } + } + *pcNestedClasses = ulRetCount; + return S_OK; +} // MDInternalRO::GetNestedClasses + +//******************************************************************************* +// return the ModuleRef properties +//******************************************************************************* +__checkReturn +HRESULT MDInternalRO::GetModuleRefProps( // return hresult + mdModuleRef mur, // [IN] moduleref token + LPCSTR *pszName) // [OUT] buffer to fill with the moduleref name +{ + _ASSERTE(TypeFromToken(mur) == mdtModuleRef); + _ASSERTE(pszName); + + HRESULT hr; + + // Is it a valid token? + if (!IsValidToken(mur)) + { + *pszName = NULL; // Not every caller checks returned HRESULT, allow to fail fast in that case + return COR_E_BADIMAGEFORMAT; // Invalid Token + } + + ModuleRefRec *pModuleRefRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetModuleRefRecord(RidFromToken(mur), &pModuleRefRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfModuleRef(pModuleRefRec, pszName)); + + return S_OK; +} + + + +//***************************************************************************** +// Given a scope and a methoddef, return a pointer to methoddef's signature +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetSigOfMethodDef( + mdMethodDef methoddef, // given a methoddef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig) +{ + // Output parameter should not be NULL + _ASSERTE(pcbSigBlob); + _ASSERTE(TypeFromToken(methoddef) == mdtMethodDef); + + HRESULT hr; + MethodRec *pMethodRec; + *ppSig = NULL; + *pcbSigBlob = 0; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(methoddef), &pMethodRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMethod(pMethodRec, ppSig, pcbSigBlob)); + return S_OK; +} // MDInternalRO::GetSigOfMethodDef + + +//***************************************************************************** +// Given a scope and a fielddef, return a pointer to fielddef's signature +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetSigOfFieldDef( + mdFieldDef fielddef, // given a methoddef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig) +{ + _ASSERTE(pcbSigBlob); + _ASSERTE(TypeFromToken(fielddef) == mdtFieldDef); + + HRESULT hr; + FieldRec *pFieldRec; + *ppSig = NULL; + *pcbSigBlob = 0; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRecord(RidFromToken(fielddef), &pFieldRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfField(pFieldRec, ppSig, pcbSigBlob)); + return S_OK; +} // MDInternalRO::GetSigOfFieldDef + +//***************************************************************************** +// Get signature for the token (FieldDef, MethodDef, Signature, or TypeSpec). +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetSigFromToken( + mdToken tk, + ULONG * pcbSig, + PCCOR_SIGNATURE * ppSig) +{ + HRESULT hr; + + *ppSig = NULL; + *pcbSig = 0; + switch (TypeFromToken(tk)) + { + case mdtSignature: + { + StandAloneSigRec * pRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetStandAloneSigRecord(RidFromToken(tk), &pRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfStandAloneSig(pRec, ppSig, pcbSig)); + return S_OK; + } + case mdtTypeSpec: + { + TypeSpecRec * pRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeSpecRecord(RidFromToken(tk), &pRec)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfTypeSpec(pRec, ppSig, pcbSig)); + return S_OK; + } + case mdtMethodDef: + { + IfFailRet(GetSigOfMethodDef(tk, pcbSig, ppSig)); + return S_OK; + } + case mdtFieldDef: + { + IfFailRet(GetSigOfFieldDef(tk, pcbSig, ppSig)); + return S_OK; + } + } + + // not a known token type. + *pcbSig = 0; + return META_E_INVALID_TOKEN_TYPE; +} // MDInternalRO::GetSigFromToken + + +//***************************************************************************** +// Given methoddef, return the flags +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetMethodDefProps( + mdMethodDef md, + DWORD *pdwFlags) // return mdPublic, mdAbstract, etc +{ + HRESULT hr; + MethodRec *pMethodRec; + + *pdwFlags = (DWORD)-1; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(md), &pMethodRec)); + *pdwFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfMethod(pMethodRec); + + return S_OK; +} // MDInternalRO::GetMethodDefProps + +//***************************************************************************** +// Given a scope and a methoddef/methodimpl, return RVA and impl flags +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetMethodImplProps( + mdMethodDef tk, // [IN] MethodDef + ULONG *pulCodeRVA, // [OUT] CodeRVA + DWORD *pdwImplFlags) // [OUT] Impl. Flags +{ + HRESULT hr; + _ASSERTE(TypeFromToken(tk) == mdtMethodDef); + + MethodRec *pMethodRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(RidFromToken(tk), &pMethodRec)); + + if (pulCodeRVA) + { + *pulCodeRVA = m_LiteWeightStgdb.m_MiniMd.getRVAOfMethod(pMethodRec); + } + + if (pdwImplFlags) + { + *pdwImplFlags = m_LiteWeightStgdb.m_MiniMd.getImplFlagsOfMethod(pMethodRec); + } + + return S_OK; +} // MDInternalRO::GetMethodImplProps + + +//***************************************************************************** +// return the field RVA +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetFieldRVA( + mdToken fd, // [IN] FieldDef + ULONG *pulCodeRVA) // [OUT] CodeRVA +{ + HRESULT hr; + _ASSERTE(TypeFromToken(fd) == mdtFieldDef); + _ASSERTE(pulCodeRVA); + + RID iRecord; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindFieldRVAFor(RidFromToken(fd), &iRecord)); + + if (InvalidRid(iRecord)) + { + if (pulCodeRVA) + *pulCodeRVA = 0; + return CLDB_E_RECORD_NOTFOUND; + } + + FieldRVARec *pFieldRVARec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRVARecord(iRecord, &pFieldRVARec)); + + *pulCodeRVA = m_LiteWeightStgdb.m_MiniMd.getRVAOfFieldRVA(pFieldRVARec); + return NOERROR; +} + +//***************************************************************************** +// Given a fielddef, return the flags. Such as fdPublic, fdStatic, etc +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetFieldDefProps( + mdFieldDef fd, // given memberdef + DWORD *pdwFlags) // [OUT] return fdPublic, fdPrive, etc flags +{ + HRESULT hr; + _ASSERTE(TypeFromToken(fd) == mdtFieldDef); + + FieldRec *pFieldRec; + + *pdwFlags = (DWORD)-1; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFieldRecord(RidFromToken(fd), &pFieldRec)); + *pdwFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfField(pFieldRec); + + return S_OK; +} // MDInternalRO::GetFieldDefProps + +//***************************************************************************** +// return default value of a token(could be paramdef, fielddef, or property) +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetDefaultValue( // return hresult + mdToken tk, // [IN] given FieldDef, ParamDef, or Property + MDDefaultValue *pMDDefaultValue) // [OUT] default value +{ + _ASSERTE(pMDDefaultValue); + + HRESULT hr; + BYTE bType; + const VOID *pValue; + ULONG cbValue; + RID rid; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindConstantFor(RidFromToken(tk), TypeFromToken(tk), &rid)); + if (InvalidRid(rid)) + { + pMDDefaultValue->m_bType = ELEMENT_TYPE_VOID; + return S_OK; + } + ConstantRec *pConstantRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetConstantRecord(rid, &pConstantRec)); + + // get the type of constant value + bType = m_LiteWeightStgdb.m_MiniMd.getTypeOfConstant(pConstantRec); + + // get the value blob + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getValueOfConstant(pConstantRec, reinterpret_cast<const BYTE **>(&pValue), &cbValue)); + // convert it to our internal default value representation + hr = _FillMDDefaultValue(bType, pValue, cbValue, pMDDefaultValue); + return hr; +} // MDInternalRO::GetDefaultValue + +//***************************************************************************** +// Given a scope and a methoddef/fielddef, return the dispid +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetDispIdOfMemberDef( // return hresult + mdToken tk, // given methoddef or fielddef + ULONG *pDispid) // Put the dispid here. +{ +#ifdef FEATURE_COMINTEROP + HRESULT hr; // A result. + const BYTE *pBlob; // Blob with dispid. + ULONG cbBlob; // Length of blob. + UINT32 dispid; // temporary for dispid. + + // Get the DISPID, if any. + _ASSERTE(pDispid); + + *pDispid = DISPID_UNKNOWN; + hr = GetCustomAttributeByName(tk, INTEROP_DISPID_TYPE, (const void**)&pBlob, &cbBlob); + if (hr == S_OK) + { + CustomAttributeParser cap(pBlob, cbBlob); + IfFailGo(cap.SkipProlog()); + IfFailGo(cap.GetU4(&dispid)); + *pDispid = dispid; + } + +ErrExit: + return hr; +#else // FEATURE_COMINTEROP + _ASSERTE(false); + return E_NOTIMPL; +#endif // FEATURE_COMINTEROP +} // MDInternalRO::GetDispIdOfMemberDef + +//***************************************************************************** +// Given interfaceimpl, return the TypeRef/TypeDef and flags +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetTypeOfInterfaceImpl( // return hresult + mdInterfaceImpl iiImpl, // given a interfaceimpl + mdToken *ptkType) +{ + HRESULT hr; + _ASSERTE(TypeFromToken(iiImpl) == mdtInterfaceImpl); + + *ptkType = mdTypeDefNil; + + InterfaceImplRec *pIIRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetInterfaceImplRecord(RidFromToken(iiImpl), &pIIRec)); + *ptkType = m_LiteWeightStgdb.m_MiniMd.getInterfaceOfInterfaceImpl(pIIRec); + return S_OK; +} // MDInternalRO::GetTypeOfInterfaceImpl + +//***************************************************************************** +// This routine gets the properties for the given MethodSpec token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetMethodSpecProps( // S_OK or error. + mdMethodSpec mi, // [IN] The method instantiation + mdToken *tkParent, // [OUT] MethodDef or MemberRef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob) // [OUT] actual size of signature blob +{ + HRESULT hr = NOERROR; + MethodSpecRec *pMethodSpecRec; + + LOG((LOGMD, "MD RegMeta::GetMethodSpecProps(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + mi, tkParent, ppvSigBlob, pcbSigBlob)); + + _ASSERTE(TypeFromToken(mi) == mdtMethodSpec); + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSpecRecord(RidFromToken(mi), &pMethodSpecRec)); + + if (tkParent) + *tkParent = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSpec(pMethodSpecRec); + + if (ppvSigBlob || pcbSigBlob) + { + // caller wants signature information + PCCOR_SIGNATURE pvSigTmp; + ULONG cbSig; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getInstantiationOfMethodSpec(pMethodSpecRec, &pvSigTmp, &cbSig)); + if ( ppvSigBlob ) + *ppvSigBlob = pvSigTmp; + if ( pcbSigBlob) + *pcbSigBlob = cbSig; + } + + + return hr; +} // MDInternalRO::GetMethodSpecProps + + + +//***************************************************************************** +// Given a classname, return the typedef +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::FindTypeDef( + LPCSTR szTypeDefNamespace, // [IN] Namespace for the TypeDef. + LPCSTR szTypeDefName, // [IN] Name of the TypeDef. + mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class. + mdTypeDef * ptkTypeDef) // [OUT] return typedef +{ + HRESULT hr = S_OK; + + _ASSERTE((szTypeDefName != NULL) && (ptkTypeDef != NULL)); + _ASSERTE((TypeFromToken(tkEnclosingClass) == mdtTypeRef) || + (TypeFromToken(tkEnclosingClass) == mdtTypeDef) || + IsNilToken(tkEnclosingClass)); + + // initialize the output parameter + *ptkTypeDef = mdTypeDefNil; + + // Treat no namespace as empty string. + if (szTypeDefNamespace == NULL) + szTypeDefNamespace = ""; + + // Do a linear search + ULONG cTypeDefRecs = m_LiteWeightStgdb.m_MiniMd.getCountTypeDefs(); + TypeDefRec * pTypeDefRec; + LPCUTF8 szName; + LPCUTF8 szNamespace; + DWORD dwFlags; + + // Get TypeDef of the tkEnclosingClass passed in + if (TypeFromToken(tkEnclosingClass) == mdtTypeRef) + { + TypeRefRec * pTypeRefRec; + mdToken tkResolutionScope; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeRefRecord(RidFromToken(tkEnclosingClass), &pTypeRefRec)); + tkResolutionScope = m_LiteWeightStgdb.m_MiniMd.getResolutionScopeOfTypeRef(pTypeRefRec); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeRef(pTypeRefRec, &szNamespace)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeRef(pTypeRefRec, &szName)); + + // Update tkEnclosingClass to TypeDef + IfFailRet(FindTypeDef( + szNamespace, + szName, + (TypeFromToken(tkResolutionScope) == mdtTypeRef) ? tkResolutionScope : mdTokenNil, + &tkEnclosingClass)); + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef); + } + + // Search for the TypeDef + for (ULONG i = 1; i <= cTypeDefRecs; i++) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(i, &pTypeDefRec)); + + dwFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfTypeDef(pTypeDefRec); + + if (!IsTdNested(dwFlags) && !IsNilToken(tkEnclosingClass)) + { + // If the class is not Nested and EnclosingClass passed in is not nil + continue; + } + else if (IsTdNested(dwFlags) && IsNilToken(tkEnclosingClass)) + { + // If the class is nested and EnclosingClass passed is nil + continue; + } + else if (!IsNilToken(tkEnclosingClass)) + { + _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef); + + RID iNestedClassRec; + NestedClassRec * pNestedClassRec; + mdTypeDef tkEnclosingClassTmp; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindNestedClassFor(i, &iNestedClassRec)); + if (InvalidRid(iNestedClassRec)) + continue; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetNestedClassRecord(iNestedClassRec, &pNestedClassRec)); + tkEnclosingClassTmp = m_LiteWeightStgdb.m_MiniMd.getEnclosingClassOfNestedClass(pNestedClassRec); + if (tkEnclosingClass != tkEnclosingClassTmp) + continue; + } + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfTypeDef(pTypeDefRec, &szName)); + if (strcmp(szTypeDefName, szName) == 0) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNamespaceOfTypeDef(pTypeDefRec, &szNamespace)); + if (strcmp(szTypeDefNamespace, szNamespace) == 0) + { + *ptkTypeDef = TokenFromRid(i, mdtTypeDef); + return S_OK; + } + } + } + // Cannot find the TypeDef by name + return CLDB_E_RECORD_NOTFOUND; +} // MDInternalRO::FindTypeDef + +//***************************************************************************** +// Given a memberref, return a pointer to memberref's name and signature +//***************************************************************************** +// Warning: Even when the return value is ok, *ppvSigBlob could be NULL if +// the metadata is corrupted! (e.g. if CPackedLen::GetLength returned -1). +// TODO: consider returning a HRESULT to make errors evident to the caller. +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetNameAndSigOfMemberRef( // meberref's name + mdMemberRef memberref, // given a memberref + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszMemberRefName) +{ + _ASSERTE(TypeFromToken(memberref) == mdtMemberRef); + + HRESULT hr; + MemberRefRec *pMemberRefRec; + *pszMemberRefName = NULL; + if (ppvSigBlob != NULL) + { + _ASSERTE(pcbSigBlob != NULL); + *ppvSigBlob = NULL; + *pcbSigBlob = 0; + } + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec)); + if (ppvSigBlob != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfMemberRef(pMemberRefRec, ppvSigBlob, pcbSigBlob)); + } + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfMemberRef(pMemberRefRec, pszMemberRefName)); + return S_OK; +} // MDInternalRO::GetNameAndSigOfMemberRef + +//***************************************************************************** +// Given a memberref, return parent token. It can be a TypeRef, ModuleRef, or a MethodDef +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetParentOfMemberRef( + mdMemberRef memberref, // given a typedef + mdToken *ptkParent) // return the parent token +{ + HRESULT hr; + _ASSERTE(TypeFromToken(memberref) == mdtMemberRef); + + MemberRefRec *pMemberRefRec; + + *ptkParent = mdTokenNil; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMemberRefRecord(RidFromToken(memberref), &pMemberRefRec)); + *ptkParent = m_LiteWeightStgdb.m_MiniMd.getClassOfMemberRef(pMemberRefRec); + + return S_OK; +} // MDInternalRO::GetParentOfMemberRef + +//***************************************************************************** +// return properties of a paramdef +//*****************************************************************************/ +__checkReturn +HRESULT +MDInternalRO::GetParamDefProps ( + mdParamDef paramdef, // given a paramdef + USHORT *pusSequence, // [OUT] slot number for this parameter + DWORD *pdwAttr, // [OUT] flags + LPCSTR *pszName) // [OUT] return the name of the parameter +{ + _ASSERTE(TypeFromToken(paramdef) == mdtParamDef); + HRESULT hr; + ParamRec *pParamRec; + + *pszName = NULL; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetParamRecord(RidFromToken(paramdef), &pParamRec)); + if (pdwAttr != NULL) + { + *pdwAttr = m_LiteWeightStgdb.m_MiniMd.getFlagsOfParam(pParamRec); + } + if (pusSequence != NULL) + { + *pusSequence = m_LiteWeightStgdb.m_MiniMd.getSequenceOfParam(pParamRec); + } + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfParam(pParamRec, pszName)); + + return S_OK; +} // MDInternalRO::GetParamDefProps + +//***************************************************************************** +// Get property info for the method. +//***************************************************************************** +int MDInternalRO::CMethodSemanticsMapSearcher::Compare( + const CMethodSemanticsMap *psFirst, + const CMethodSemanticsMap *psSecond) +{ + if (psFirst->m_mdMethod < psSecond->m_mdMethod) + return -1; + if (psFirst->m_mdMethod > psSecond->m_mdMethod) + return 1; + return 0; +} // MDInternalRO::CMethodSemanticsMapSearcher::Compare + +#ifndef DACCESS_COMPILE +int MDInternalRO::CMethodSemanticsMapSorter::Compare( + CMethodSemanticsMap *psFirst, + CMethodSemanticsMap *psSecond) +{ + if (psFirst->m_mdMethod < psSecond->m_mdMethod) + return -1; + if (psFirst->m_mdMethod > psSecond->m_mdMethod) + return 1; + return 0; +} // MDInternalRO::CMethodSemanticsMapSorter::Compare + +__checkReturn +HRESULT MDInternalRO::GetPropertyInfoForMethodDef( // Result. + mdMethodDef md, // [IN] memberdef + mdProperty *ppd, // [OUT] put property token here + LPCSTR *pName, // [OUT] put pointer to name here + ULONG *pSemantic) // [OUT] put semantic here +{ + HRESULT hr; + MethodSemanticsRec *pSemantics; // A MethodSemantics record. + MethodSemanticsRec *pFound=0; // A MethodSemantics record that is a property for the desired function. + RID ridCur; // loop control. + RID ridMax; // Count of entries in table. + USHORT usSemantics = 0; // A method's semantics. + mdToken tk; // A method def. + + ridMax = m_LiteWeightStgdb.m_MiniMd.getCountMethodSemantics(); + + // Lazy initialization of m_pMethodSemanticsMap + if ((ridMax > 10) && (m_pMethodSemanticsMap == NULL)) + { + NewArrayHolder<CMethodSemanticsMap> pMethodSemanticsMap = new (nothrow) CMethodSemanticsMap[ridMax]; + if (pMethodSemanticsMap != NULL) + { + // Fill the table in MethodSemantics order. + for (ridCur = 1; ridCur <= ridMax; ridCur++) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + tk = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics); + pMethodSemanticsMap[ridCur-1].m_mdMethod = tk; + pMethodSemanticsMap[ridCur-1].m_ridSemantics = ridCur; + } + // Sort to MethodDef order. + CMethodSemanticsMapSorter sorter(pMethodSemanticsMap, ridMax); + sorter.Sort(); + + if (InterlockedCompareExchangeT<CMethodSemanticsMap *>( + &m_pMethodSemanticsMap, pMethodSemanticsMap, NULL) == NULL) + { // The exchange did happen, supress of the allocated map + pMethodSemanticsMap.SuppressRelease(); + } + } + } + + // Use m_pMethodSemanticsMap if it has been built. + if (m_pMethodSemanticsMap != NULL) + { + CMethodSemanticsMapSearcher searcher(m_pMethodSemanticsMap, ridMax); + CMethodSemanticsMap target; + const CMethodSemanticsMap * pMatchedMethod; + target.m_mdMethod = md; + pMatchedMethod = searcher.Find(&target); + + // Was there at least one match? + if (pMatchedMethod != NULL) + { + _ASSERTE(pMatchedMethod >= m_pMethodSemanticsMap); + _ASSERTE(pMatchedMethod < m_pMethodSemanticsMap+ridMax); + _ASSERTE(pMatchedMethod->m_mdMethod == md); + + ridCur = pMatchedMethod->m_ridSemantics; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics); + + // If the semantics record is a getter or setter for the method, that's what we want. + if (usSemantics == msGetter || usSemantics == msSetter) + pFound = pSemantics; + else + { // The semantics record was neither getter or setter. Because there can be + // multiple semantics records for a given method, look for other semantics + // records that match this record. + const CMethodSemanticsMap *pScan; + const CMethodSemanticsMap *pLo=m_pMethodSemanticsMap; + const CMethodSemanticsMap *pHi=pLo+ridMax-1; + for (pScan = pMatchedMethod-1; pScan >= pLo; --pScan) + { + if (pScan->m_mdMethod == md) + { + ridCur = pScan->m_ridSemantics; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics); + + if (usSemantics == msGetter || usSemantics == msSetter) + { + pFound = pSemantics; + break; + } + } + else + break; + } + + if (pFound == 0) + { // Not found looking down, try looking up. + for (pScan = pMatchedMethod+1; pScan <= pHi; ++pScan) + { + if (pScan->m_mdMethod == md) + { + ridCur = pScan->m_ridSemantics; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics); + + if (usSemantics == msGetter || usSemantics == msSetter) + { + pFound = pSemantics; + break; + } + } + else + break; + } + + } + } + } + } + else + { // Scan entire table. + for (ridCur = 1; ridCur <= ridMax; ridCur++) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + if (md == m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics)) + { // The method matched, is this a property? + usSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics); + if (usSemantics == msGetter || usSemantics == msSetter) + { // found a match. + pFound = pSemantics; + break; + } + } + } + } + + // Did the search find anything? + if (pFound) + { // found a match. Fill out the output parameters + PropertyRec *pProperty; + mdProperty prop; + prop = m_LiteWeightStgdb.m_MiniMd.getAssociationOfMethodSemantics(pFound); + + if (ppd) + *ppd = prop; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty)); + + if (pName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfProperty(pProperty, pName)); + } + + if (pSemantic) + *pSemantic = usSemantics; + return S_OK; + } + return S_FALSE; +} // MDInternalRO::GetPropertyInfoForMethodDef +#endif //!DACCESS_COMPILE + +//***************************************************************************** +// return the pack size of a class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetClassPackSize( + mdTypeDef td, // [IN] give typedef + DWORD *pdwPackSize) // [OUT] +{ + HRESULT hr = NOERROR; + + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pdwPackSize); + + ClassLayoutRec *pRec; + RID ridClassLayout; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindClassLayoutFor(RidFromToken(td), &ridClassLayout)); + if (InvalidRid(ridClassLayout)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec)); + *pdwPackSize = m_LiteWeightStgdb.m_MiniMd.getPackingSizeOfClassLayout(pRec); +ErrExit: + return hr; +} // MDInternalRO::GetClassPackSize + + +//***************************************************************************** +// return the total size of a value class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetClassTotalSize( // return error if a class does not have total size info + mdTypeDef td, // [IN] give typedef + ULONG *pulClassSize) // [OUT] return the total size of the class +{ + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pulClassSize); + + ClassLayoutRec *pRec; + HRESULT hr = NOERROR; + RID ridClassLayout; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindClassLayoutFor(RidFromToken(td), &ridClassLayout)); + if (InvalidRid(ridClassLayout)) + { + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec)); + *pulClassSize = m_LiteWeightStgdb.m_MiniMd.getClassSizeOfClassLayout(pRec); +ErrExit: + return hr; +} // MDInternalRO::GetClassTotalSize + + +//***************************************************************************** +// init the layout enumerator of a class +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetClassLayoutInit( + mdTypeDef td, // [IN] give typedef + MD_CLASS_LAYOUT *pmdLayout) // [OUT] set up the status of query here +{ + HRESULT hr = NOERROR; + _ASSERTE(TypeFromToken(td) == mdtTypeDef); + + // initialize the output parameter + _ASSERTE(pmdLayout); + memset(pmdLayout, 0, sizeof(MD_CLASS_LAYOUT)); + + TypeDefRec *pTypeDefRec; + + // record for this typedef in TypeDef Table + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pTypeDefRec)); + + // find the starting and end field for this typedef + pmdLayout->m_ridFieldCur = m_LiteWeightStgdb.m_MiniMd.getFieldListOfTypeDef(pTypeDefRec); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getEndFieldListOfTypeDef(RidFromToken(td), &(pmdLayout->m_ridFieldEnd))); + return hr; +} // MDInternalRO::GetClassLayoutInit + + +//***************************************************************************** +// return the field offset for a given field +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetFieldOffset( + mdFieldDef fd, // [IN] fielddef + ULONG *pulOffset) // [OUT] FieldOffset +{ + HRESULT hr = S_OK; + FieldLayoutRec *pRec; + + _ASSERTE(pulOffset); + + RID iLayout; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindFieldLayoutFor(RidFromToken(fd), &iLayout)); + + if (InvalidRid(iLayout)) + { + hr = S_FALSE; + goto ErrExit; + } + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldLayoutRecord(iLayout, &pRec)); + *pulOffset = m_LiteWeightStgdb.m_MiniMd.getOffSetOfFieldLayout(pRec); + _ASSERTE(*pulOffset != UINT32_MAX); + +ErrExit: + return hr; +} + + +//***************************************************************************** +// enum the next the field layout +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetClassLayoutNext( + MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here + mdFieldDef *pfd, // [OUT] field def + ULONG *pulOffset) // [OUT] field offset or sequence +{ + HRESULT hr = S_OK; + + _ASSERTE(pfd && pulOffset && pLayout); + + RID iLayout2; + FieldLayoutRec *pRec; + + // Make sure no one is messing with pLayout->m_ridFieldLayoutCur, since this doesn't + // mean anything if we are using FieldLayout table. + while (pLayout->m_ridFieldCur < pLayout->m_ridFieldEnd) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindFieldLayoutFor(pLayout->m_ridFieldCur, &iLayout2)); + pLayout->m_ridFieldCur++; + if (!InvalidRid(iLayout2)) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldLayoutRecord(iLayout2, &pRec)); + *pulOffset = m_LiteWeightStgdb.m_MiniMd.getOffSetOfFieldLayout(pRec); + _ASSERTE(*pulOffset != UINT32_MAX); + *pfd = TokenFromRid(pLayout->m_ridFieldCur - 1, mdtFieldDef); + goto ErrExit; + } + } + + *pfd = mdFieldDefNil; + hr = S_FALSE; + + // fall through + +ErrExit: + return hr; +} // MDInternalRO::GetClassLayoutNext + + +//***************************************************************************** +// return the field's native type signature +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetFieldMarshal( // return error if no native type associate with the token + mdToken tk, // [IN] given fielddef or paramdef + PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature + ULONG *pcbNativeType) // [OUT] the count of bytes of *ppvNativeType +{ + // output parameters have to be supplied + _ASSERTE(pcbNativeType); + + RID rid; + FieldMarshalRec *pFieldMarshalRec; + HRESULT hr = NOERROR; + + // find the row containing the marshal definition for tk + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindFieldMarshalFor(RidFromToken(tk), TypeFromToken(tk), &rid)); + if (InvalidRid(rid)) + { + *pSigNativeType = NULL; + *pcbNativeType = 0; + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldMarshalRecord(rid, &pFieldMarshalRec)); + + // get the native type + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNativeTypeOfFieldMarshal(pFieldMarshalRec, pSigNativeType, pcbNativeType)); +ErrExit: + return hr; +} // MDInternalRO::GetFieldMarshal + + + +//***************************************** +// property APIs +//***************************************** + +//***************************************************************************** +// Find property by name +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::FindProperty( + mdTypeDef td, // [IN] given a typdef + LPCSTR szPropName, // [IN] property name + mdProperty *pProp) // [OUT] return property token +{ + HRESULT hr = NOERROR; + + // output parameters have to be supplied + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pProp); + + PropertyMapRec *pRec; + PropertyRec *pProperty; + RID ridPropertyMap; + RID ridCur; + RID ridEnd; + LPCUTF8 szName; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindPropertyMapFor(RidFromToken(td), &ridPropertyMap)); + if (InvalidRid(ridPropertyMap)) + { + // not found! + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetPropertyMapRecord(ridPropertyMap, &pRec)); + + // get the starting/ending rid of properties of this typedef + ridCur = m_LiteWeightStgdb.m_MiniMd.getPropertyListOfPropertyMap(pRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + + for (; ridCur < ridEnd; ridCur ++) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetPropertyRecord(ridCur, &pProperty)); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfProperty(pProperty, &szName)); + if (strcmp(szName, szPropName) ==0) + { + // Found the match. Set the output parameter and we are done. + *pProp = TokenFromRid(ridCur, mdtProperty); + goto ErrExit; + } + } + + // not found + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; + +} // MDInternalRO::FindProperty + + + +//***************************************************************************** +// return the properties of a property +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetPropertyProps( + mdProperty prop, // [IN] property token + LPCSTR *pszProperty, // [OUT] property name + DWORD *pdwPropFlags, // [OUT] property flags. + PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob + ULONG *pcbSig) // [OUT] count of bytes in *ppvSig +{ + // output parameters have to be supplied + _ASSERTE(TypeFromToken(prop) == mdtProperty); + + HRESULT hr; + + PropertyRec *pProperty; + ULONG cbSig; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetPropertyRecord(RidFromToken(prop), &pProperty)); + + // get name of the property + if (pszProperty != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfProperty(pProperty, pszProperty)); + } + + // get the flags of property + if (pdwPropFlags) + *pdwPropFlags = m_LiteWeightStgdb.m_MiniMd.getPropFlagsOfProperty(pProperty); + + // get the type of the property + if (ppvSig != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getTypeOfProperty(pProperty, ppvSig, &cbSig)); + if (pcbSig != NULL) + { + *pcbSig = cbSig; + } + } + + return S_OK; +} // MDInternalRO::GetPropertyProps + + +//********************************** +// +// Event APIs +// +//********************************** + +//***************************************************************************** +// return an event by given the name +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::FindEvent( + mdTypeDef td, // [IN] given a typdef + LPCSTR szEventName, // [IN] event name + mdEvent *pEvent) // [OUT] return event token +{ + HRESULT hr = NOERROR; + + // output parameters have to be supplied + _ASSERTE(TypeFromToken(td) == mdtTypeDef && pEvent); + + EventMapRec *pRec; + EventRec *pEventRec; + RID ridEventMap; + RID ridCur; + RID ridEnd; + LPCUTF8 szName; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.FindEventMapFor(RidFromToken(td), &ridEventMap)); + if (InvalidRid(ridEventMap)) + { + // not found! + hr = CLDB_E_RECORD_NOTFOUND; + goto ErrExit; + } + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetEventMapRecord(ridEventMap, &pRec)); + + // get the starting/ending rid of properties of this typedef + ridCur = m_LiteWeightStgdb.m_MiniMd.getEventListOfEventMap(pRec); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getEndEventListOfEventMap(ridEventMap, &ridEnd)); + + for (; ridCur < ridEnd; ridCur ++) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetEventRecord(ridCur, &pEventRec)); + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfEvent(pEventRec, &szName)); + if (strcmp(szName, szEventName) ==0) + { + // Found the match. Set the output parameter and we are done. + *pEvent = TokenFromRid(ridCur, mdtEvent); + goto ErrExit; + } + } + + // not found + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; +} // MDInternalRO::FindEvent + + +//***************************************************************************** +// return the properties of an event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetEventProps( // S_OK, S_FALSE, or error. + mdEvent ev, // [IN] event token + LPCSTR *pszEvent, // [OUT] Event name + DWORD *pdwEventFlags, // [OUT] Event flags. + mdToken *ptkEventType) // [OUT] EventType class +{ + // output parameters have to be supplied + _ASSERTE(TypeFromToken(ev) == mdtEvent); + + HRESULT hr; + EventRec *pEvent; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetEventRecord(RidFromToken(ev), &pEvent)); + if (pszEvent != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfEvent(pEvent, pszEvent)); + } + if (pdwEventFlags) + *pdwEventFlags = m_LiteWeightStgdb.m_MiniMd.getEventFlagsOfEvent(pEvent); + if (ptkEventType) + *ptkEventType = m_LiteWeightStgdb.m_MiniMd.getEventTypeOfEvent(pEvent); + + return S_OK; +} // MDInternalRO::GetEventProps + +//***************************************************************************** +// return the properties of a generic param +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetGenericParamProps( // S_OK or error. + mdGenericParam rd, // [IN] The type parameter + ULONG* pulSequence, // [OUT] Parameter sequence number + DWORD* pdwAttr, // [OUT] Type parameter flags (for future use) + mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef) + DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use) + LPCSTR *szName) // [OUT] The name +{ + HRESULT hr = NOERROR; + GenericParamRec * pGenericParamRec = NULL; + + // See if this version of the metadata can do Generics + if (!m_LiteWeightStgdb.m_MiniMd.SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + _ASSERTE(TypeFromToken(rd) == mdtGenericParam); + if (TypeFromToken(rd) != mdtGenericParam) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetGenericParamRecord(RidFromToken(rd), &pGenericParamRec)); + + if (pulSequence) + *pulSequence = m_LiteWeightStgdb.m_MiniMd.getNumberOfGenericParam(pGenericParamRec); + if (pdwAttr) + *pdwAttr = m_LiteWeightStgdb.m_MiniMd.getFlagsOfGenericParam(pGenericParamRec); + if (ptOwner) + *ptOwner = m_LiteWeightStgdb.m_MiniMd.getOwnerOfGenericParam(pGenericParamRec); + if (szName != NULL) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getNameOfGenericParam(pGenericParamRec, szName)); + } +ErrExit: + return hr; +} // MDInternalRO::GetGenericParamProps + +//***************************************************************************** +// This routine gets the properties for the given GenericParamConstraint token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetGenericParamConstraintProps( // S_OK or error. + mdGenericParamConstraint rd, // [IN] The constraint token + mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained + mdToken *ptkConstraintType) // [OUT] TypeDef/Ref/Spec constraint +{ + HRESULT hr = NOERROR; + GenericParamConstraintRec *pGPCRec; + RID ridRD = RidFromToken(rd); + + // See if this version of the metadata can do Generics + if (!m_LiteWeightStgdb.m_MiniMd.SupportsGenerics()) + IfFailGo(CLDB_E_INCOMPATIBLE); + + if((TypeFromToken(rd) == mdtGenericParamConstraint) && (ridRD != 0)) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetGenericParamConstraintRecord(ridRD, &pGPCRec)); + + if (ptGenericParam) + *ptGenericParam = TokenFromRid(m_LiteWeightStgdb.m_MiniMd.getOwnerOfGenericParamConstraint(pGPCRec),mdtGenericParam); + if (ptkConstraintType) + *ptkConstraintType = m_LiteWeightStgdb.m_MiniMd.getConstraintOfGenericParamConstraint(pGPCRec); + } + else + hr = META_E_BAD_INPUT_PARAMETER; + +ErrExit: + return hr; +} // MDInternalRO::GetGenericParamConstraintProps + +//***************************************************************************** +// Find methoddef of a particular associate with a property or an event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::FindAssociate( + mdToken evprop, // [IN] given a property or event token + DWORD dwSemantics, // [IN] given a associate semantics(setter, getter, testdefault, reset) + mdMethodDef *pmd) // [OUT] return method def token +{ + HRESULT hr = NOERROR; + + // output parameters have to be supplied + _ASSERTE(pmd); + _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty); + + MethodSemanticsRec *pSemantics; + RID ridCur; + RID ridEnd; + + IfFailGo(m_LiteWeightStgdb.m_MiniMd.getAssociatesForToken(evprop, &ridEnd, &ridCur)); + for (; ridCur < ridEnd; ridCur++) + { + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + if (dwSemantics == m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics)) + { + // found a match + *pmd = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics); + goto ErrExit; + } + } + + // not found + hr = CLDB_E_RECORD_NOTFOUND; +ErrExit: + return hr; +} // MDInternalRO::FindAssociate + + +//***************************************************************************** +// get counts of methodsemantics associated with a particular property/event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::EnumAssociateInit( + mdToken evprop, // [IN] given a property or an event token + HENUMInternal *phEnum) // [OUT] cursor to hold the query result +{ + HRESULT hr; + _ASSERTE(phEnum); + + HENUMInternal::ZeroEnum(phEnum); + + // There is no token kind!!! + phEnum->m_tkKind = UINT32_MAX; + + // output parameters have to be supplied + _ASSERTE(TypeFromToken(evprop) == mdtEvent || TypeFromToken(evprop) == mdtProperty); + + phEnum->m_EnumType = MDSimpleEnum; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getAssociatesForToken(evprop, &phEnum->u.m_ulEnd, &phEnum->u.m_ulStart)); + phEnum->u.m_ulCur = phEnum->u.m_ulStart; + phEnum->m_ulCount = phEnum->u.m_ulEnd - phEnum->u.m_ulStart; + + return S_OK; +} // MDInternalRO::EnumAssociateInit + + +//***************************************************************************** +// get all methodsemantics associated with a particular property/event +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetAllAssociates( + HENUMInternal *phEnum, // [OUT] cursor to hold the query result + ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output + ULONG cAssociateRec) // [IN] size of the buffer +{ + _ASSERTE(phEnum && pAssociateRec); + + HRESULT hr; + MethodSemanticsRec *pSemantics; + RID ridCur; + _ASSERTE(cAssociateRec == phEnum->m_ulCount); + + // Convert from row pointers to RIDs. + for (ridCur = phEnum->u.m_ulStart; ridCur < phEnum->u.m_ulEnd; ++ridCur) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetMethodSemanticsRecord(ridCur, &pSemantics)); + + pAssociateRec[ridCur-phEnum->u.m_ulStart].m_memberdef = m_LiteWeightStgdb.m_MiniMd.getMethodOfMethodSemantics(pSemantics); + pAssociateRec[ridCur-phEnum->u.m_ulStart].m_dwSemantics = m_LiteWeightStgdb.m_MiniMd.getSemanticOfMethodSemantics(pSemantics); + } + + return S_OK; +} // MDInternalRO::GetAllAssociates + + +//***************************************************************************** +// Get the Action and Permissions blob for a given PermissionSet. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetPermissionSetProps( + mdPermission pm, // [IN] the permission token. + DWORD *pdwAction, // [OUT] CorDeclSecurity. + void const **ppvPermission, // [OUT] permission blob. + ULONG *pcbPermission) // [OUT] count of bytes of pvPermission. +{ + HRESULT hr; + _ASSERTE(TypeFromToken(pm) == mdtPermission); + _ASSERTE(pdwAction && ppvPermission && pcbPermission); + + DeclSecurityRec *pPerm; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetDeclSecurityRecord(RidFromToken(pm), &pPerm)); + *pdwAction = m_LiteWeightStgdb.m_MiniMd.getActionOfDeclSecurity(pPerm); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPermissionSetOfDeclSecurity(pPerm, (const BYTE **)ppvPermission, pcbPermission)); + + return S_OK; +} // MDInternalRO::GetPermissionSetProps + +//***************************************************************************** +// Get the String given the String token. +// Return a pointer to the string, or NULL in case of error. +//***************************************************************************** +__checkReturn +HRESULT +MDInternalRO::GetUserString( // Offset into the string blob heap. + mdString stk, // [IN] the string token. + ULONG *pcchStringSize, // [OUT] count of characters in the string. + BOOL *pfIs80Plus, // [OUT] specifies where there are extended characters >= 0x80. + LPCWSTR *pwszUserString) +{ + HRESULT hr; + LPWSTR wszTmp; + + if (pfIs80Plus != NULL) + { + *pfIs80Plus = FALSE; + } + *pwszUserString = NULL; + *pcchStringSize = 0; + + _ASSERTE(pcchStringSize != NULL); + MetaData::DataBlob userString; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetUserString(RidFromToken(stk), &userString)); + + wszTmp = reinterpret_cast<LPWSTR>(userString.GetDataPointer()); + + *pcchStringSize = userString.GetSize() / sizeof(WCHAR); + + if (userString.IsEmpty()) + { + *pwszUserString = NULL; + return S_OK; + } + + if (pfIs80Plus != NULL) + { + if (userString.GetSize() % sizeof(WCHAR) == 0) + { + *pfIs80Plus = TRUE; // no indicator, presume the worst + } + // Return the user string terminator (contains value fIs80Plus) + *pfIs80Plus = *(reinterpret_cast<PBYTE>(wszTmp + *pcchStringSize)); + } + + *pwszUserString = wszTmp; + return S_OK; +} // MDInternalRO::GetUserString + +//***************************************************************************** +// Return contents of Pinvoke given the forwarded member token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetPinvokeMap( + mdToken tk, // [IN] FieldDef or MethodDef. + DWORD *pdwMappingFlags, // [OUT] Flags used for mapping. + LPCSTR *pszImportName, // [OUT] Import name. + mdModuleRef *pmrImportDLL) // [OUT] ModuleRef token for the target DLL. +{ + HRESULT hr; + ImplMapRec *pRecord; + RID iRecord; + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.FindImplMapFor(RidFromToken(tk), TypeFromToken(tk), &iRecord)); + if (InvalidRid(iRecord)) + { + return CLDB_E_RECORD_NOTFOUND; + } + else + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetImplMapRecord(iRecord, &pRecord)); + } + + if (pdwMappingFlags) + *pdwMappingFlags = m_LiteWeightStgdb.m_MiniMd.getMappingFlagsOfImplMap(pRecord); + if (pszImportName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getImportNameOfImplMap(pRecord, pszImportName)); + } + if (pmrImportDLL) + *pmrImportDLL = m_LiteWeightStgdb.m_MiniMd.getImportScopeOfImplMap(pRecord); + + return S_OK; +} // MDInternalRO::GetPinvokeMap + +//***************************************************************************** +// Get the properties for the given Assembly token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetAssemblyProps( + mdAssembly mda, // [IN] The Assembly for which to get the properties. + const void **ppbPublicKey, // [OUT] Pointer to the public key. + ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key. + ULONG *pulHashAlgId, // [OUT] Hash Algorithm. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + DWORD *pdwAssemblyFlags) // [OUT] Flags. +{ + HRESULT hr; + AssemblyRec *pRecord; + + _ASSERTE(TypeFromToken(mda) == mdtAssembly && RidFromToken(mda)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetAssemblyRecord(RidFromToken(mda), &pRecord)); + + if (ppbPublicKey != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPublicKeyOfAssembly(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKey), pcbPublicKey)); + } + if (pulHashAlgId) + *pulHashAlgId = m_LiteWeightStgdb.m_MiniMd.getHashAlgIdOfAssembly(pRecord); + if (pszName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfAssembly(pRecord, pszName)); + } + if (pMetaData) + { + pMetaData->usMajorVersion = m_LiteWeightStgdb.m_MiniMd.getMajorVersionOfAssembly(pRecord); + pMetaData->usMinorVersion = m_LiteWeightStgdb.m_MiniMd.getMinorVersionOfAssembly(pRecord); + pMetaData->usBuildNumber = m_LiteWeightStgdb.m_MiniMd.getBuildNumberOfAssembly(pRecord); + pMetaData->usRevisionNumber = m_LiteWeightStgdb.m_MiniMd.getRevisionNumberOfAssembly(pRecord); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getLocaleOfAssembly(pRecord, &pMetaData->szLocale)); + } + if (pdwAssemblyFlags) + { + *pdwAssemblyFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfAssembly(pRecord); + + // Turn on the afPublicKey if PublicKey blob is not empty + DWORD cbPublicKey; + const BYTE *pbPublicKey; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey)); + if (cbPublicKey != 0) + *pdwAssemblyFlags |= afPublicKey; + } + + return S_OK; +} // MDInternalRO::GetAssemblyProps + +//***************************************************************************** +// Get the properties for the given AssemblyRef token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetAssemblyRefProps( + mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties. + const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token. + ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + const void **ppbHashValue, // [OUT] Hash blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob. + DWORD *pdwAssemblyRefFlags) // [OUT] Flags. +{ + HRESULT hr; + AssemblyRefRec *pRecord; + + _ASSERTE(TypeFromToken(mdar) == mdtAssemblyRef && RidFromToken(mdar)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetAssemblyRefRecord(RidFromToken(mdar), &pRecord)); + + if (ppbPublicKeyOrToken != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getPublicKeyOrTokenOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbPublicKeyOrToken), pcbPublicKeyOrToken)); + } + if (pszName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfAssemblyRef(pRecord, pszName)); + } + if (pMetaData) + { + pMetaData->usMajorVersion = m_LiteWeightStgdb.m_MiniMd.getMajorVersionOfAssemblyRef(pRecord); + pMetaData->usMinorVersion = m_LiteWeightStgdb.m_MiniMd.getMinorVersionOfAssemblyRef(pRecord); + pMetaData->usBuildNumber = m_LiteWeightStgdb.m_MiniMd.getBuildNumberOfAssemblyRef(pRecord); + pMetaData->usRevisionNumber = m_LiteWeightStgdb.m_MiniMd.getRevisionNumberOfAssemblyRef(pRecord); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getLocaleOfAssemblyRef(pRecord, &pMetaData->szLocale)); + } + if (ppbHashValue != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getHashValueOfAssemblyRef(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue)); + } + if (pdwAssemblyRefFlags != NULL) + { + *pdwAssemblyRefFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfAssemblyRef(pRecord); + } + + return S_OK; +} // MDInternalRO::GetAssemblyRefProps + +//***************************************************************************** +// Get the properties for the given File token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetFileProps( + mdFile mdf, // [IN] The File for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob. + DWORD *pdwFileFlags) // [OUT] Flags. +{ + HRESULT hr; + FileRec *pRecord; + + _ASSERTE(TypeFromToken(mdf) == mdtFile && RidFromToken(mdf)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetFileRecord(RidFromToken(mdf), &pRecord)); + + if (pszName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfFile(pRecord, pszName)); + } + if (ppbHashValue != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getHashValueOfFile(pRecord, reinterpret_cast<const BYTE **>(ppbHashValue), pcbHashValue)); + } + if (pdwFileFlags != NULL) + { + *pdwFileFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfFile(pRecord); + } + + return S_OK; +} // MDInternalRO::GetFileProps + +//***************************************************************************** +// Get the properties for the given ExportedType token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetExportedTypeProps( + mdExportedType mdct, // [IN] The ExportedType for which to get the properties. + LPCSTR *pszNamespace, // [OUT] Buffer to fill with namespace. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file. + DWORD *pdwExportedTypeFlags) // [OUT] Flags. +{ + HRESULT hr; + ExportedTypeRec *pRecord; + + _ASSERTE(TypeFromToken(mdct) == mdtExportedType && RidFromToken(mdct)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetExportedTypeRecord(RidFromToken(mdct), &pRecord)); + + if (pszNamespace != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getTypeNamespaceOfExportedType(pRecord, pszNamespace)); + } + if (pszName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getTypeNameOfExportedType(pRecord, pszName)); + } + if (ptkImplementation) + *ptkImplementation = m_LiteWeightStgdb.m_MiniMd.getImplementationOfExportedType(pRecord); + if (ptkTypeDef) + *ptkTypeDef = m_LiteWeightStgdb.m_MiniMd.getTypeDefIdOfExportedType(pRecord); + if (pdwExportedTypeFlags) + *pdwExportedTypeFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfExportedType(pRecord); + + return S_OK; +} // MDInternalRO::GetExportedTypeProps + +//***************************************************************************** +// Get the properties for the given Resource token. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetManifestResourceProps( + mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file. + DWORD *pdwResourceFlags) // [OUT] Flags. +{ + HRESULT hr; + ManifestResourceRec *pRecord; + + _ASSERTE(TypeFromToken(mdmr) == mdtManifestResource && RidFromToken(mdmr)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetManifestResourceRecord(RidFromToken(mdmr), &pRecord)); + + if (pszName != NULL) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfManifestResource(pRecord, pszName)); + } + if (ptkImplementation) + *ptkImplementation = m_LiteWeightStgdb.m_MiniMd.getImplementationOfManifestResource(pRecord); + if (pdwOffset) + *pdwOffset = m_LiteWeightStgdb.m_MiniMd.getOffsetOfManifestResource(pRecord); + if (pdwResourceFlags) + *pdwResourceFlags = m_LiteWeightStgdb.m_MiniMd.getFlagsOfManifestResource(pRecord); + + return S_OK; +} // MDInternalRO::GetManifestResourceProps + +//***************************************************************************** +// Find the ExportedType given the name. +//***************************************************************************** +__checkReturn +STDMETHODIMP MDInternalRO::FindExportedTypeByName( // S_OK or error + LPCSTR szNamespace, // [IN] Namespace of the ExportedType. + LPCSTR szName, // [IN] Name of the ExportedType. + mdExportedType tkEnclosingType, // [IN] Token for the Enclosing Type. + mdExportedType *pmct) // [OUT] Put ExportedType token here. +{ + IMetaModelCommon *pCommon = static_cast<IMetaModelCommon*>(&m_LiteWeightStgdb.m_MiniMd); + return pCommon->CommonFindExportedType(szNamespace, szName, tkEnclosingType, pmct); +} // MDInternalRO::FindExportedTypeByName + +//***************************************************************************** +// Find the ManifestResource given the name. +//***************************************************************************** +__checkReturn +STDMETHODIMP MDInternalRO::FindManifestResourceByName( // S_OK or error + LPCSTR szName, // [IN] Name of the resource. + mdManifestResource *pmmr) // [OUT] Put ManifestResource token here. +{ + _ASSERTE(szName && pmmr); + + HRESULT hr; + ManifestResourceRec *pRecord; + ULONG cRecords; // Count of records. + LPCUTF8 szNameTmp = 0; // Name obtained from the database. + ULONG i; + + cRecords = m_LiteWeightStgdb.m_MiniMd.getCountManifestResources(); + + // Search for the ExportedType. + for (i = 1; i <= cRecords; i++) + { + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetManifestResourceRecord(i, &pRecord)); + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getNameOfManifestResource(pRecord, &szNameTmp)); + if (! strcmp(szName, szNameTmp)) + { + *pmmr = TokenFromRid(i, mdtManifestResource); + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // MDInternalRO::FindManifestResourceByName + +//***************************************************************************** +// Get the Assembly token from the given scope. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetAssemblyFromScope( // S_OK or error + mdAssembly *ptkAssembly) // [OUT] Put token here. +{ + _ASSERTE(ptkAssembly); + + if (m_LiteWeightStgdb.m_MiniMd.getCountAssemblys()) + { + *ptkAssembly = TokenFromRid(1, mdtAssembly); + return S_OK; + } + else + return CLDB_E_RECORD_NOTFOUND; +} // MDInternalRO::GetAssemblyFromScope + +//******************************************************************************* +// return properties regarding a TypeSpec +//******************************************************************************* +__checkReturn +HRESULT MDInternalRO::GetTypeSpecFromToken( // S_OK or error. + mdTypeSpec typespec, // [IN] Signature token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token. + ULONG *pcbSig) // [OUT] return size of signature. +{ + HRESULT hr = NOERROR; + + _ASSERTE(TypeFromToken(typespec) == mdtTypeSpec); + _ASSERTE(ppvSig && pcbSig); + + if (!IsValidToken(typespec)) + { + *ppvSig = NULL; + *pcbSig = 0; + return E_INVALIDARG; + } + + TypeSpecRec *pRec; + IfFailRet(m_LiteWeightStgdb.m_MiniMd.GetTypeSpecRecord(RidFromToken(typespec), &pRec)); + + if (pRec == NULL) + { + *ppvSig = NULL; + *pcbSig = 0; + return CLDB_E_FILE_CORRUPT; + } + + IfFailRet(m_LiteWeightStgdb.m_MiniMd.getSignatureOfTypeSpec(pRec, ppvSig, pcbSig)); + + return hr; +} // MDInternalRO::GetTypeSpecFromToken + +//***************************************************************************** +// This function gets the "built for" version of a metadata scope. +// NOTE: if the scope has never been saved, it will not have a built-for +// version, and an empty string will be returned. +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetVersionString( + LPCSTR * pVer) // [OUT] Put version string here. +{ + HRESULT hr = NOERROR; + + if (m_LiteWeightStgdb.m_pvMd != NULL) + { + // For convenience, get a pointer to the version string. + // @todo: get from alternate locations when there is no STOREAGESIGNATURE. + *pVer = reinterpret_cast<const char*>(reinterpret_cast<const STORAGESIGNATURE*>(m_LiteWeightStgdb.m_pvMd)->pVersion); + } + else + { // No string. + *pVer = NULL; + } + + return hr; +} // MDInternalRO::GetVersionString + +//***************************************************************************** +// convert a text signature to com format +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::ConvertTextSigToComSig(// Return hresult. + BOOL fCreateTrIfNotFound, // create typeref if not found or not + LPCSTR pSignature, // class file format signature + CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature + ULONG *pcbCount) // [OUT] the result size of signature +{ + return E_NOTIMPL; +} // MDInternalRO::ConvertTextSigToComSig + + +//***************************************************************************** +// determine if a token is valid or not +//***************************************************************************** +BOOL MDInternalRO::IsValidToken( // True or False. + mdToken tk) // [IN] Given token. +{ + RID rid = RidFromToken(tk); + if (rid == 0) + { + return FALSE; + } + switch (TypeFromToken(tk)) + { + case mdtModule: + // can have only one module record + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountModules()); + case mdtTypeRef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountTypeRefs()); + case mdtTypeDef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountTypeDefs()); + case mdtFieldDef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountFields()); + case mdtMethodDef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountMethods()); + case mdtParamDef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountParams()); + case mdtInterfaceImpl: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountInterfaceImpls()); + case mdtMemberRef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountMemberRefs()); + case mdtCustomAttribute: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountCustomAttributes()); + case mdtPermission: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountDeclSecuritys()); + case mdtSignature: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountStandAloneSigs()); + case mdtEvent: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountEvents()); + case mdtProperty: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountPropertys()); + case mdtModuleRef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountModuleRefs()); + case mdtTypeSpec: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountTypeSpecs()); + case mdtAssembly: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountAssemblys()); + case mdtAssemblyRef: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountAssemblyRefs()); + case mdtFile: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountFiles()); + case mdtExportedType: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountExportedTypes()); + case mdtManifestResource: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountManifestResources()); + case mdtMethodSpec: + return (rid <= m_LiteWeightStgdb.m_MiniMd.getCountMethodSpecs()); + case mdtString: + // need to check the user string heap + return m_LiteWeightStgdb.m_MiniMd.m_UserStringHeap.IsValidIndex(rid); + default: +/* Don't Assert here, this will break verifier tests. + _ASSERTE(!"Unknown token kind!"); +*/ + return FALSE; + } +} // MDInternalRO::IsValidToken + +mdModule MDInternalRO::GetModuleFromScope(void) +{ + return TokenFromRid(1, mdtModule); +} // MDInternalRO::GetModuleFromScope + +//***************************************************************************** +// Fill a variant given a MDDefaultValue +// This routine will create a bstr if the ELEMENT_TYPE of default value is STRING +//***************************************************************************** +__checkReturn +HRESULT _FillVariant( + MDDefaultValue *pMDDefaultValue, + VARIANT *pvar) +{ + HRESULT hr = NOERROR; + + _ASSERTE(pMDDefaultValue); + + switch (pMDDefaultValue->m_bType) + { + case ELEMENT_TYPE_BOOLEAN: + V_VT(pvar) = VT_BOOL; + V_BOOL(pvar) = pMDDefaultValue->m_bValue; + break; + case ELEMENT_TYPE_I1: + V_VT(pvar) = VT_I1; + V_I1(pvar) = pMDDefaultValue->m_cValue; + break; + case ELEMENT_TYPE_U1: + V_VT(pvar) = VT_UI1; + V_UI1(pvar) = pMDDefaultValue->m_byteValue; + break; + case ELEMENT_TYPE_I2: + V_VT(pvar) = VT_I2; + V_I2(pvar) = pMDDefaultValue->m_sValue; + break; + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: // char is stored as UI2 internally + V_VT(pvar) = VT_UI2; + V_UI2(pvar) = pMDDefaultValue->m_usValue; + break; + case ELEMENT_TYPE_I4: + V_VT(pvar) = VT_I4; + V_I4(pvar) = pMDDefaultValue->m_lValue; + break; + case ELEMENT_TYPE_U4: + V_VT(pvar) = VT_UI4; + V_UI4(pvar) = pMDDefaultValue->m_ulValue; + break; + case ELEMENT_TYPE_R4: + V_VT(pvar) = VT_R4; + V_R4(pvar) = pMDDefaultValue->m_fltValue; + break; + case ELEMENT_TYPE_R8: + V_VT(pvar) = VT_R8; + V_R8(pvar) = pMDDefaultValue->m_dblValue; + break; + case ELEMENT_TYPE_STRING: + // allocated bstr here + V_BSTR(pvar) = ::SysAllocStringLen(pMDDefaultValue->m_wzValue, pMDDefaultValue->m_cbSize / sizeof(WCHAR)); + if (V_BSTR(pvar) == NULL) + hr = E_OUTOFMEMORY; + V_VT(pvar) = VT_BSTR; + break; + case ELEMENT_TYPE_CLASS: + V_VT(pvar) = VT_UNKNOWN; + V_UNKNOWN(pvar) = pMDDefaultValue->m_unkValue; + break; + case ELEMENT_TYPE_I8: + V_VT(pvar) = VT_I8; + V_CY(pvar).int64 = pMDDefaultValue->m_llValue; + break; + case ELEMENT_TYPE_U8: + V_VT(pvar) = VT_UI8; + V_CY(pvar).int64 = pMDDefaultValue->m_ullValue; + break; + case ELEMENT_TYPE_VOID: + V_VT(pvar) = VT_EMPTY; + break; + default: + _ASSERTE(!"bad constant value type!"); + } + + return hr; +} // _FillVariant + + +//***************************************************************************** +// Fill a variant given a MDDefaultValue +// This routine will create a bstr if the ELEMENT_TYPE of default value is STRING +//***************************************************************************** +__checkReturn +HRESULT _FillMDDefaultValue( + BYTE bType, + void const *pValue, + ULONG cbValue, + MDDefaultValue *pMDDefaultValue) +{ + HRESULT hr = NOERROR; + + pMDDefaultValue->m_bType = bType; + pMDDefaultValue->m_cbSize = cbValue; + switch (bType) + { + case ELEMENT_TYPE_BOOLEAN: + if (cbValue < 1) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_bValue = *((BYTE *) pValue); + break; + case ELEMENT_TYPE_I1: + if (cbValue < 1) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_cValue = *((CHAR *) pValue); + break; + case ELEMENT_TYPE_U1: + if (cbValue < 1) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_byteValue = *((BYTE *) pValue); + break; + case ELEMENT_TYPE_I2: + if (cbValue < 2) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_sValue = GET_UNALIGNED_VAL16(pValue); + break; + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: + if (cbValue < 2) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_usValue = GET_UNALIGNED_VAL16(pValue); + break; + case ELEMENT_TYPE_I4: + if (cbValue < 4) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_lValue = GET_UNALIGNED_VAL32(pValue); + break; + case ELEMENT_TYPE_U4: + if (cbValue < 4) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_ulValue = GET_UNALIGNED_VAL32(pValue); + break; + case ELEMENT_TYPE_R4: + { + if (cbValue < 4) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + __int32 Value = GET_UNALIGNED_VAL32(pValue); + pMDDefaultValue->m_fltValue = (float &)Value; + } + break; + case ELEMENT_TYPE_R8: + { + if (cbValue < 8) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + __int64 Value = GET_UNALIGNED_VAL64(pValue); + pMDDefaultValue->m_dblValue = (double &) Value; + } + break; + case ELEMENT_TYPE_STRING: + if (cbValue == 0) + pValue = NULL; + +#if BIGENDIAN + { + // We need to allocate and swap the string if we're on a big endian + pMDDefaultValue->m_wzValue = new WCHAR[(cbValue + 1) / sizeof (WCHAR)]; + _ASSERTE(FALSE); // Nothing ever free's this newly allocated array. Inserting assert so that if we ever actually + // use this code path, we'll fix it then. (Don't want to fix something I can't test.) + IfNullGo(pMDDefaultValue->m_wzValue); + memcpy(const_cast<WCHAR *>(pMDDefaultValue->m_wzValue), pValue, cbValue); + _ASSERTE(cbValue % sizeof(WCHAR) == 0); + SwapStringLength(const_cast<WCHAR *>(pMDDefaultValue->m_wzValue), cbValue / sizeof(WCHAR)); + } +#else + pMDDefaultValue->m_wzValue = (LPWSTR) pValue; +#endif + break; + case ELEMENT_TYPE_CLASS: + // + // There is only a 4-byte quantity in the MetaData, and it must always + // be zero. So, we load an INT32 and zero-extend it to be pointer-sized. + // + if (cbValue < 4) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_unkValue = (IUnknown *)(UINT_PTR)GET_UNALIGNED_VAL32(pValue); + if (pMDDefaultValue->m_unkValue != NULL) + { + _ASSERTE(!"Non-NULL objectref's are not supported as default values!"); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + break; + case ELEMENT_TYPE_I8: + if (cbValue < 8) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_llValue = GET_UNALIGNED_VAL64(pValue); + break; + case ELEMENT_TYPE_U8: + if (cbValue < 8) + { + IfFailGo(CLDB_E_FILE_CORRUPT); + } + pMDDefaultValue->m_ullValue = GET_UNALIGNED_VAL64(pValue); + break; + case ELEMENT_TYPE_VOID: + break; + default: + _ASSERTE(!"BAD TYPE!"); + IfFailGo(CLDB_E_FILE_CORRUPT); + break; + } +ErrExit: + return hr; +} // _FillMDDefaultValue + +//***************************************************************************** +// Given a scope, return the table size and table ptr for a given index +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::GetTableInfoWithIndex( // return size + ULONG index, // [IN] pass in the index + void **pTable, // [OUT] pointer to table at index + void **pTableSize) // [OUT] size of table at index +{ + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} // MDInternalRO::GetTableInfoWithIndex + + + +//***************************************************************************** +// Given a delta metadata byte stream, apply the changes to the current metadata +// object returning the resulting metadata object in ppv +//***************************************************************************** +__checkReturn +HRESULT MDInternalRO::ApplyEditAndContinue( + void *pDeltaMD, // [IN] the delta metadata + ULONG cbDeltaMD, // [IN] length of pData + IMDInternalImport **ppv) // [OUT] the resulting metadata interface +{ + _ASSERTE(pDeltaMD); + _ASSERTE(ppv); + + HRESULT hr = E_FAIL; + + IMDInternalImportENC *pDeltaMDImport = NULL; + + IfFailGo(GetInternalWithRWFormat(pDeltaMD, cbDeltaMD, 0, IID_IMDInternalImportENC, (void**)&pDeltaMDImport)); + + *ppv = this; + IfFailGo(MDApplyEditAndContinue(ppv, pDeltaMDImport)); + +ErrExit: + if (pDeltaMDImport) + pDeltaMDImport->Release(); + return hr; +} + +HRESULT MDInternalRO::GetRvaOffsetData( + DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table. + DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table. + DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table. + DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table. + DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table. + DWORD *pFieldRvaCount) // [OUT] Number of records in FieldRVA table. +{ + HRESULT hr = S_OK; + DWORD methodDefCount = *pMethodDefCount = m_LiteWeightStgdb.m_MiniMd.getCountMethods(); + if (methodDefCount == 0) + *pFirstMethodRvaOffset = *pMethodDefRecordSize = 0; + else + { + MethodRec *pMethodRec; + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetMethodRecord(1, &pMethodRec)); + + // RVA is the first column of the MethodDef table, so the address of MethodRec is also address of RVA column. + if ((const BYTE *)m_LiteWeightStgdb.m_pvMd > (const BYTE *)pMethodRec) + { + Debug_ReportError("Stream header is not within MetaData block."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + *pFirstMethodRvaOffset = (DWORD)((const BYTE *)pMethodRec - (const BYTE *)m_LiteWeightStgdb.m_pvMd); + *pMethodDefRecordSize = m_LiteWeightStgdb.m_MiniMd._CBREC(Method); + } + + { + DWORD fieldRvaCount = *pFieldRvaCount = m_LiteWeightStgdb.m_MiniMd.getCountFieldRVAs(); + if (fieldRvaCount == 0) + *pFirstFieldRvaOffset = *pFieldRvaRecordSize = 0; + else + { + + // orig + // FieldRVARec *pFieldRVARec = m_LiteWeightStgdb.m_MiniMd.getFieldRVA(1); + FieldRVARec *pFieldRVARec; + IfFailGo(m_LiteWeightStgdb.m_MiniMd.GetFieldRVARecord(1, &pFieldRVARec)); + +//FieldRVARec *pFieldRVARec; +//mdToken fakeTok = 1; +//RidToToken(&fakeTok, mdtFieldDef); +//GetFieldRVA(fakeTok, &pFieldRVARec); + // RVA is the first column of the FieldRVA table, so the address of FieldRVARec is also address of RVA column. + if ((const BYTE *)m_LiteWeightStgdb.m_pvMd > (const BYTE *)pFieldRVARec) + { + Debug_ReportError("Stream header is not within MetaData block."); + IfFailGo(CLDB_E_FILE_CORRUPT); + } + *pFirstFieldRvaOffset = (DWORD)((const BYTE *)pFieldRVARec - (const BYTE *)m_LiteWeightStgdb.m_pvMd); + *pFieldRvaRecordSize = m_LiteWeightStgdb.m_MiniMd._CBREC(FieldRVA); + } + } + hr = S_OK; + +ErrExit: + return hr; +} + +#endif //FEATURE_METADATA_INTERNAL_APIS diff --git a/src/coreclr/md/runtime/mdinternalro.h b/src/coreclr/md/runtime/mdinternalro.h new file mode 100644 index 00000000000..2ac1c90680c --- /dev/null +++ b/src/coreclr/md/runtime/mdinternalro.h @@ -0,0 +1,805 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MDInternalRO.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __MDInternalRO__h__ +#define __MDInternalRO__h__ + +#include "metamodel.h" + +#ifdef FEATURE_METADATA_INTERNAL_APIS + +class MDInternalRO : public IMDInternalImport, IMDCommon +{ +public: + + MDInternalRO(); + virtual ~MDInternalRO(); + __checkReturn + HRESULT Init(LPVOID pData, ULONG cbData); + + // *** IUnknown methods *** + __checkReturn + STDMETHODIMP QueryInterface(REFIID riid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + __checkReturn + STDMETHODIMP TranslateSigWithScope( + IMDInternalImport *pAssemImport, // [IN] import assembly scope. + const void *pbHashValue, // [IN] hash value for the import assembly. + ULONG cbHashValue, // [IN] count of bytes in the hash value. + PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope + ULONG cbSigBlob, // [IN] count of bytes of signature + IMetaDataAssemblyEmit *pAssemEmit, // [IN] assembly emit scope. + IMetaDataEmit *emit, // [IN] emit interface + CQuickBytes *pqkSigEmit, // [OUT] buffer to hold translated signature + ULONG *pcbSig) // [OUT] count of bytes in the translated signature + DAC_UNEXPECTED(); + + + __checkReturn + STDMETHODIMP GetTypeDefRefTokenInTypeSpec(// return S_FALSE if enclosing type does not have a token + mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at + mdToken *tkEnclosedToken); // [OUT] The enclosed type token + + + + STDMETHODIMP_(IMetaModelCommon*) GetMetaModelCommon() + { + return static_cast<IMetaModelCommon*>(&m_LiteWeightStgdb.m_MiniMd); + } + + STDMETHODIMP_(IMetaModelCommonRO*) GetMetaModelCommonRO() + { + if (m_LiteWeightStgdb.m_MiniMd.IsWritable()) + { + _ASSERTE(!"IMetaModelCommonRO methods cannot be used because this importer is writable."); + return NULL; + } + return static_cast<IMetaModelCommonRO*>(&m_LiteWeightStgdb.m_MiniMd); + } + + __checkReturn + STDMETHODIMP SetOptimizeAccessForSpeed( + BOOL fOptSpeed) + { +#ifdef FEATURE_PREJIT + // The metadata cache of hot items is an optional working-set optimization + // that has a large speed cost relative to direct table lookup + if (fOptSpeed) + { // We want to disable usage of hot data (e.g. in ngen compilation process) + m_LiteWeightStgdb.m_MiniMd.DisableHotDataUsage(); + } +#endif + return S_OK; + } + + //***************************************************************************** + // return the count of entries of a given kind in a scope + // For example, pass in mdtMethodDef will tell you how many MethodDef + // contained in a scope + //***************************************************************************** + STDMETHODIMP_(ULONG) GetCountWithTokenKind(// return hresult + DWORD tkKind) // [IN] pass in the kind of token. + DAC_UNEXPECTED(); + + //***************************************************************************** + // enumerator for typedef + //***************************************************************************** + __checkReturn + STDMETHODIMP EnumTypeDefInit( // return hresult + HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data + + //***************************************************************************** + // enumerator for MethodImpl + //***************************************************************************** + __checkReturn + STDMETHODIMP EnumMethodImplInit( // return hresult + mdTypeDef td, // [IN] TypeDef over which to scope the enumeration. + HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens. + HENUMInternal *phEnumDecl); // [OUT] buffer to fill for enumerator data for MethodDecl tokens. + + STDMETHODIMP_(ULONG) EnumMethodImplGetCount( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl); // [IN] MethodDecl enumerator. + + STDMETHODIMP_(void) EnumMethodImplReset( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl); // [IN] MethodDecl enumerator. + + __checkReturn + STDMETHODIMP EnumMethodImplNext( // return hresult + HENUMInternal *phEnumBody, // [IN] input enum for MethodBody + HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl + mdToken *ptkBody, // [OUT] return token for MethodBody + mdToken *ptkDecl); // [OUT] return token for MethodDecl + + STDMETHODIMP_(void) EnumMethodImplClose( + HENUMInternal *phEnumBody, // [IN] MethodBody enumerator. + HENUMInternal *phEnumDecl); // [IN] MethodDecl enumerator. + + //***************************************** + // Enumerator helpers for memberdef, memberref, interfaceimp, + // event, property, param, methodimpl + //***************************************** + + __checkReturn + STDMETHODIMP EnumGlobalFunctionsInit( // return hresult + HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data + + __checkReturn + STDMETHODIMP EnumGlobalFieldsInit( // return hresult + HENUMInternal *phEnum); // [OUT] buffer to fill for enumerator data + + __checkReturn + STDMETHODIMP EnumInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + mdToken tkParent, // [IN] token to scope the search + HENUMInternal *phEnum); // [OUT] the enumerator to fill + + __checkReturn + STDMETHODIMP EnumAllInit( // return S_FALSE if record not found + DWORD tkKind, // [IN] which table to work on + HENUMInternal *phEnum); // [OUT] the enumerator to fill + + __checkReturn + STDMETHODIMP EnumCustomAttributeByNameInit(// return S_FALSE if record not found + mdToken tkParent, // [IN] token to scope the search + LPCSTR szName, // [IN] CustomAttribute's name to scope the search + HENUMInternal *phEnum); // [OUT] the enumerator to fill + + __checkReturn + STDMETHODIMP GetParentToken( + mdToken tkChild, // [IN] given child token + mdToken *ptkParent); // [OUT] returning parent + + __checkReturn + STDMETHODIMP GetCustomAttributeProps( + mdCustomAttribute at, // [IN] The attribute. + mdToken *ptkType); // [OUT] Put attribute type here. + + __checkReturn + STDMETHODIMP GetCustomAttributeAsBlob( + mdCustomAttribute cv, // [IN] given custom attribute token + void const **ppBlob, // [OUT] return the pointer to internal blob + ULONG *pcbSize); // [OUT] return the size of the blob + + __checkReturn + STDMETHODIMP GetCustomAttributeByName( // S_OK or error. + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData); // [OUT] Put size of data here. + + __checkReturn + STDMETHODIMP GetNameOfCustomAttribute( // S_OK or error. + mdCustomAttribute mdAttribute, // [IN] The Custom Attribute + LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute. + LPCUTF8 *pszName); // [OUT] Name of Custom Attribute. + + __checkReturn + STDMETHODIMP GetScopeProps( + LPCSTR *pszName, // [OUT] scope name + GUID *pmvid); // [OUT] version id + + // finding a particular method + __checkReturn + STDMETHODIMP FindMethodDef( + mdTypeDef classdef, // [IN] given typedef + LPCSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + mdMethodDef *pmd); // [OUT] matching memberdef + + // return a iSeq's param given a MethodDef + __checkReturn + STDMETHODIMP FindParamOfMethod( // S_OK or error. + mdMethodDef md, // [IN] The owning method of the param. + ULONG iSeq, // [IN] The sequence # of the param. + mdParamDef *pparamdef); // [OUT] Put ParamDef token here. + + //***************************************** + // + // GetName* functions + // + //***************************************** + + // return the name and namespace of typedef + __checkReturn + STDMETHODIMP GetNameOfTypeDef( + mdTypeDef classdef, // given classdef + LPCSTR *pszname, // return class name(unqualified) + LPCSTR *psznamespace); // return the name space name + + __checkReturn + STDMETHODIMP GetIsDualOfTypeDef( + mdTypeDef classdef, // [IN] given classdef. + ULONG *pDual); // [OUT] return dual flag here. + + __checkReturn + STDMETHODIMP GetIfaceTypeOfTypeDef( + mdTypeDef classdef, // [IN] given classdef. + ULONG *pIface); // [OUT] 0=dual, 1=vtable, 2=dispinterface + + // get the name of either methoddef + __checkReturn + STDMETHODIMP GetNameOfMethodDef( // return the name of the memberdef in UTF8 + mdMethodDef md, // given memberdef + LPCSTR *pszName); + + __checkReturn + STDMETHODIMP GetNameAndSigOfMethodDef( + mdMethodDef methoddef, // [IN] given memberdef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszName); + + // return the name of a FieldDef + __checkReturn + STDMETHODIMP GetNameOfFieldDef( + mdFieldDef fd, // given memberdef + LPCSTR *pszName); + + // return the name of typeref + __checkReturn + STDMETHODIMP GetNameOfTypeRef( + mdTypeRef classref, // [IN] given typeref + LPCSTR *psznamespace, // [OUT] return typeref name + LPCSTR *pszname); // [OUT] return typeref namespace + + // return the resolutionscope of typeref + __checkReturn + STDMETHODIMP GetResolutionScopeOfTypeRef( + mdTypeRef classref, // given classref + mdToken *ptkResolutionScope); + + // return the typeref token given the name. + __checkReturn + STDMETHODIMP FindTypeRefByName( + LPCSTR szNamespace, // [IN] Namespace for the TypeRef. + LPCSTR szName, // [IN] Name of the TypeRef. + mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef. + mdTypeRef *ptk); // [OUT] TypeRef token returned. + + // return the TypeDef properties + __checkReturn + STDMETHODIMP GetTypeDefProps( // return hresult + mdTypeDef classdef, // given classdef + DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract + mdToken *ptkExtends); // [OUT] Put base class TypeDef/TypeRef here. + + // return the item's guid + __checkReturn + STDMETHODIMP GetItemGuid( // return hresult + mdToken tkObj, // [IN] given item. + CLSID *pGuid); // [OUT] Put guid here. + + // get enclosing class of NestedClass. + __checkReturn + STDMETHODIMP GetNestedClassProps( // S_OK or error + mdTypeDef tkNestedClass, // [IN] NestedClass token. + mdTypeDef *ptkEnclosingClass); // [OUT] EnclosingClass token. + + // Get count of Nested classes given the enclosing class. + __checkReturn + STDMETHODIMP GetCountNestedClasses( // return count of Nested classes. + mdTypeDef tkEnclosingClass, // [IN]Enclosing class. + ULONG *pcNestedClassesCount); + + // Return array of Nested classes given the enclosing class. + __checkReturn + STDMETHODIMP GetNestedClasses( // Return actual count. + mdTypeDef tkEnclosingClass, // [IN] Enclosing class. + mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens. + ULONG ulNestedClasses, // [IN] Size of array. + ULONG *pcNestedClasses); + + // return the ModuleRef properties + __checkReturn + STDMETHODIMP GetModuleRefProps( + mdModuleRef mur, // [IN] moduleref token + LPCSTR *pszName); // [OUT] buffer to fill with the moduleref name + + //***************************************** + // + // GetSig* functions + // + //***************************************** + __checkReturn + STDMETHODIMP GetSigOfMethodDef( + mdMethodDef methoddef, // [IN] given memberdef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig); + + __checkReturn + STDMETHODIMP GetSigOfFieldDef( + mdMethodDef methoddef, // [IN] given memberdef + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + PCCOR_SIGNATURE *ppSig); + + __checkReturn + STDMETHODIMP GetSigFromToken( + mdToken tk, // FieldDef, MethodDef, Signature or TypeSpec token + ULONG * pcbSig, + PCCOR_SIGNATURE * ppSig); + + + + //***************************************** + // get method property + //***************************************** + __checkReturn + STDMETHODIMP GetMethodDefProps( + mdMethodDef md, // The method for which to get props. + DWORD *pdwFlags); + + __checkReturn + STDMETHODIMP_(ULONG) GetMethodDefSlot( + mdMethodDef mb); // The method for which to get props. + + //***************************************** + // return method implementation informaiton, like RVA and implflags + //***************************************** + __checkReturn + STDMETHODIMP GetMethodImplProps( + mdMethodDef tk, // [IN] MethodDef + ULONG *pulCodeRVA, // [OUT] CodeRVA + DWORD *pdwImplFlags); // [OUT] Impl. Flags + + //***************************************************************************** + // return the field RVA + //***************************************************************************** + __checkReturn + STDMETHODIMP GetFieldRVA( + mdToken fd, // [IN] FieldDef + ULONG *pulCodeRVA); // [OUT] CodeRVA + + //***************************************************************************** + // return the field offset for a given field + //***************************************************************************** + __checkReturn + STDMETHODIMP GetFieldOffset( + mdFieldDef fd, // [IN] fielddef + ULONG *pulOffset); // [OUT] FieldOffset + + //***************************************** + // get field property + //***************************************** + __checkReturn + STDMETHODIMP GetFieldDefProps( + mdFieldDef fd, // [IN] given fielddef + DWORD *pdwFlags); // [OUT] return fdPublic, fdPrive, etc flags + + //***************************************************************************** + // return default value of a token (could be paramdef, fielddef, or property) + //***************************************************************************** + __checkReturn + STDMETHODIMP GetDefaultValue( + mdToken tk, // [IN] given FieldDef, ParamDef, or Property + MDDefaultValue *pDefaultValue); // [OUT] default value to fill + + + //***************************************** + // get dispid of a MethodDef or a FieldDef + //***************************************** + __checkReturn + STDMETHODIMP GetDispIdOfMemberDef( // return hresult + mdToken tk, // [IN] given methoddef or fielddef + ULONG *pDispid); // [OUT] Put the dispid here. + + //***************************************** + // return TypeRef/TypeDef given an InterfaceImpl token + //***************************************** + __checkReturn + STDMETHODIMP GetTypeOfInterfaceImpl( // return the TypeRef/typedef token for the interfaceimpl + mdInterfaceImpl iiImpl, // given a interfaceimpl + mdToken *ptkType); + + //***************************************** + // return information about a generic method instantiation + //***************************************** + __checkReturn + STDMETHODIMP GetMethodSpecProps( + mdMethodSpec mi, // [IN] The method instantiation + mdToken *tkParent, // [OUT] MethodDef or MemberRef + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data + ULONG *pcbSigBlob); // [OUT] actual size of signature blob + + //***************************************** + // look up function for TypeDef + //***************************************** + __checkReturn + STDMETHODIMP FindTypeDef( + LPCSTR szNamespace, // [IN] Namespace for the TypeDef. + LPCSTR szName, // [IN] Name of the TypeDef. + mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef of enclosing class. + mdTypeDef *ptypedef); // [OUT] return typedef + + __checkReturn + STDMETHODIMP FindTypeDefByGUID( + REFGUID guid, // guid to look up + mdTypeDef *ptypedef); // return typedef + + + + //***************************************** + // return name and sig of a memberref + //***************************************** + __checkReturn + STDMETHODIMP GetNameAndSigOfMemberRef( // return name here + mdMemberRef memberref, // given memberref + PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of COM+ signature + ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob + LPCSTR *pszName); + + //***************************************************************************** + // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef + //***************************************************************************** + __checkReturn + STDMETHODIMP GetParentOfMemberRef( + mdMemberRef memberref, // given memberref + mdToken *ptkParent); // return the parent token + + __checkReturn + STDMETHODIMP GetParamDefProps( + mdParamDef paramdef, // given a paramdef + USHORT *pusSequence, // [OUT] slot number for this parameter + DWORD *pdwAttr, // [OUT] flags + LPCSTR *pszName); // [OUT] return the name of the parameter + + //****************************************** + // property info for method. + //****************************************** + __checkReturn + STDMETHODIMP GetPropertyInfoForMethodDef( // Result. + mdMethodDef md, // [IN] memberdef + mdProperty *ppd, // [OUT] put property token here + LPCSTR *pName, // [OUT] put pointer to name here + ULONG *pSemantic) // [OUT] put semantic here + DAC_UNEXPECTED(); + + //***************************************** + // class layout/sequence information + //***************************************** + __checkReturn + STDMETHODIMP GetClassPackSize( // [OUT] return error if a class doesn't have packsize info + mdTypeDef td, // [IN] give typedef + ULONG *pdwPackSize); // [OUT] return the pack size of the class. 1, 2, 4, 8 or 16 + + __checkReturn + STDMETHODIMP GetClassTotalSize( // [OUT] return error if a class doesn't have total size info + mdTypeDef td, // [IN] give typedef + ULONG *pdwClassSize); // [OUT] return the total size of the class + + __checkReturn + STDMETHODIMP GetClassLayoutInit( + mdTypeDef td, // [IN] give typedef + MD_CLASS_LAYOUT *pLayout); // [OUT] set up the status of query here + + __checkReturn + STDMETHODIMP GetClassLayoutNext( + MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here + mdFieldDef *pfd, // [OUT] return the fielddef + ULONG *pulOffset); // [OUT] return the offset/ulSequence associate with it + + //***************************************** + // marshal information of a field + //***************************************** + __checkReturn + STDMETHODIMP GetFieldMarshal( // return error if no native type associate with the token + mdFieldDef fd, // [IN] given fielddef + PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature + ULONG *pcbNativeType); // [OUT] the count of bytes of *ppvNativeType + + + //***************************************** + // property APIs + //***************************************** + // find a property by name + __checkReturn + STDMETHODIMP FindProperty( + mdTypeDef td, // [IN] given a typdef + LPCSTR szPropName, // [IN] property name + mdProperty *pProp); // [OUT] return property token + + __checkReturn + STDMETHODIMP GetPropertyProps( + mdProperty prop, // [IN] property token + LPCSTR *szProperty, // [OUT] property name + DWORD *pdwPropFlags, // [OUT] property flags. + PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob + ULONG *pcbSig); // [OUT] count of bytes in *ppvSig + + //********************************** + // Event APIs + //********************************** + __checkReturn + STDMETHODIMP FindEvent( + mdTypeDef td, // [IN] given a typdef + LPCSTR szEventName, // [IN] event name + mdEvent *pEvent); // [OUT] return event token + + __checkReturn + STDMETHODIMP GetEventProps( // S_OK, S_FALSE, or error. + mdEvent ev, // [IN] event token + LPCSTR *pszEvent, // [OUT] Event name + DWORD *pdwEventFlags, // [OUT] Event flags. + mdToken *ptkEventType); // [OUT] EventType class + + //********************************** + // Generics APIs + //********************************** + __checkReturn + STDMETHODIMP GetGenericParamProps( // S_OK or error. + mdGenericParam rd, // [IN] The type parameter + ULONG* pulSequence, // [OUT] Parameter sequence number + DWORD* pdwAttr, // [OUT] Type parameter flags (for future use) + mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef) + DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use) + LPCSTR *szName); // [OUT] The name + + __checkReturn + STDMETHODIMP GetGenericParamConstraintProps( // S_OK or error. + mdGenericParamConstraint rd, // [IN] The constraint token + mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained + mdToken *ptkConstraintType); // [OUT] TypeDef/Ref/Spec constraint + + + //********************************** + // find a particular associate of a property or an event + //********************************** + __checkReturn + STDMETHODIMP FindAssociate( + mdToken evprop, // [IN] given a property or event token + DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire) + mdMethodDef *pmd); // [OUT] return method def token + + __checkReturn + STDMETHODIMP EnumAssociateInit( + mdToken evprop, // [IN] given a property or an event token + HENUMInternal *phEnum); // [OUT] cursor to hold the query result + + __checkReturn + STDMETHODIMP GetAllAssociates( + HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts + ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output + ULONG cAssociateRec); // [IN] size of the buffer + + + //********************************** + // Get info about a PermissionSet. + //********************************** + __checkReturn + STDMETHODIMP GetPermissionSetProps( + mdPermission pm, // [IN] the permission token. + DWORD *pdwAction, // [OUT] CorDeclSecurity. + void const **ppvPermission, // [OUT] permission blob. + ULONG *pcbPermission); // [OUT] count of bytes of pvPermission. + + //**************************************** + // Get the String given the String token. + // Returns a pointer to the string, or NULL in case of error. + //**************************************** + __checkReturn + STDMETHODIMP GetUserString( + mdString stk, // [IN] the string token. + ULONG *pchString, // [OUT] count of characters in the string. + BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80. + LPCWSTR *pwszUserString); + + //***************************************************************************** + // p-invoke APIs. + //***************************************************************************** + __checkReturn + STDMETHODIMP GetPinvokeMap( + mdMethodDef tk, // [IN] FieldDef or MethodDef. + DWORD *pdwMappingFlags, // [OUT] Flags used for mapping. + LPCSTR *pszImportName, // [OUT] Import name. + mdModuleRef *pmrImportDLL); // [OUT] ModuleRef token for the target DLL. + + //***************************************************************************** + // Assembly MetaData APIs. + //***************************************************************************** + __checkReturn + STDMETHODIMP GetAssemblyProps( + mdAssembly mda, // [IN] The Assembly for which to get the properties. + const void **ppbPublicKey, // [OUT] Pointer to the public key. + ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key. + ULONG *pulHashAlgId, // [OUT] Hash Algorithm. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + DWORD *pdwAssemblyFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetAssemblyRefProps( + mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties. + const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token. + ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData. + const void **ppbHashValue, // [OUT] Hash blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob. + DWORD *pdwAssemblyRefFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetFileProps( + mdFile mdf, // [IN] The File for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob. + ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob. + DWORD *pdwFileFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetExportedTypeProps( + mdExportedType mdct, // [IN] The ExportedType for which to get the properties. + LPCSTR *pszNamespace, // [OUT] Buffer to fill with namespace. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file. + DWORD *pdwExportedTypeFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP GetManifestResourceProps( + mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties. + LPCSTR *pszName, // [OUT] Buffer to fill with name. + mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType. + DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file. + DWORD *pdwResourceFlags); // [OUT] Flags. + + __checkReturn + STDMETHODIMP FindExportedTypeByName( // S_OK or error + LPCSTR szNamespace, // [IN] Namespace of the ExportedType. + LPCSTR szName, // [IN] Name of the ExportedType. + mdExportedType tkEnclosingType, // [IN] Enclosing ExportedType. + mdExportedType *pmct); // [OUT] Put ExportedType token here. + + __checkReturn + STDMETHODIMP FindManifestResourceByName(// S_OK or error + LPCSTR szName, // [IN] Name of the resource. + mdManifestResource *pmmr); // [OUT] Put ManifestResource token here. + + __checkReturn + STDMETHODIMP GetAssemblyFromScope( // S_OK or error + mdAssembly *ptkAssembly); // [OUT] Put token here. + + //*************************************************************************** + // return properties regarding a TypeSpec + //*************************************************************************** + __checkReturn + STDMETHODIMP GetTypeSpecFromToken( // S_OK or error. + mdTypeSpec typespec, // [IN] Signature token. + PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token. + ULONG *pcbSig); // [OUT] return size of signature. + + //***************************************************************************** + // This function gets the "built for" version of a metadata scope. + // NOTE: if the scope has never been saved, it will not have a built-for + // version, and an empty string will be returned. + //***************************************************************************** + __checkReturn + STDMETHODIMP GetVersionString( // S_OK or error. + LPCSTR *pVer); // [OUT] Put version string here. + + + //***************************************************************************** + // helpers to convert a text signature to a com format + //***************************************************************************** + __checkReturn + STDMETHODIMP ConvertTextSigToComSig( // Return hresult. + BOOL fCreateTrIfNotFound, // [IN] create typeref if not found + LPCSTR pSignature, // [IN] class file format signature + CQuickBytes *pqbNewSig, // [OUT] place holder for COM+ signature + ULONG *pcbCount); // [OUT] the result size of signature + + __checkReturn + STDMETHODIMP SetUserContextData( // S_OK or E_NOTIMPL + IUnknown *pIUnk) // The user context. + { return E_NOTIMPL; } + + STDMETHODIMP_(BOOL) IsValidToken( // True or False. + mdToken tk); // [IN] Given token. + + STDMETHODIMP_(IUnknown *) GetCachedPublicInterface(BOOL fWithLock) { return NULL;} // return the cached public interface + __checkReturn + STDMETHODIMP SetCachedPublicInterface(IUnknown *pUnk) { return E_FAIL;} ;// return hresult + STDMETHODIMP_(UTSemReadWrite*) GetReaderWriterLock() {return NULL;} // return the reader writer lock + __checkReturn + STDMETHODIMP SetReaderWriterLock(UTSemReadWrite *pSem) { return NOERROR; } + STDMETHODIMP_(mdModule) GetModuleFromScope(void); + + // Find a paticular method and pass in the signature comparison routine. Very + // helpful when the passed in signature does not come from the same scope. + __checkReturn + STDMETHODIMP FindMethodDefUsingCompare( + mdTypeDef classdef, // [IN] given typedef + LPCSTR szName, // [IN] member name + PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of COM+ signature + ULONG cbSigBlob, // [IN] count of bytes in the signature blob + PSIGCOMPARE pSignatureCompare, // [IN] Routine to compare signatures + void* pSignatureArgs, // [IN] Additional info to supply the compare function + mdMethodDef *pmd); // [OUT] matching memberdef + + + //***************************************************************************** + // return the table pointer and size for a given table index + //***************************************************************************** + __checkReturn + STDMETHODIMP GetTableInfoWithIndex( + ULONG index, // [IN] pass in the index + void **pTable, // [OUT] pointer to table at index + void **pTableSize); // [OUT] size of table at index + + __checkReturn + STDMETHODIMP ApplyEditAndContinue( + void *pData, // [IN] the delta metadata + ULONG cbData, // [IN] length of pData + IMDInternalImport **ppv); // [OUT] the resulting metadata interface + + STDMETHODIMP GetRvaOffsetData( + DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table. + DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table. + DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table. + DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table. + DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table. + DWORD *pFieldRvaCount // [OUT] Number of records in FieldRVA table. + ); + + CLiteWeightStgdb<CMiniMd> m_LiteWeightStgdb; + +private: + + struct CMethodSemanticsMap + { + mdToken m_mdMethod; // Method token. + RID m_ridSemantics; // RID of semantics record. + }; + CMethodSemanticsMap *m_pMethodSemanticsMap; // Possible array of method semantics pointers, ordered by method token. + +#ifndef DACCESS_COMPILE + class CMethodSemanticsMapSorter : public CQuickSort<CMethodSemanticsMap> + { + public: + CMethodSemanticsMapSorter(CMethodSemanticsMap *pBase, int iCount) : CQuickSort<CMethodSemanticsMap>(pBase, iCount) {} + virtual int Compare(CMethodSemanticsMap *psFirst, CMethodSemanticsMap *psSecond); + }; +#endif //!DACCESS_COMPILE + + class CMethodSemanticsMapSearcher : public CBinarySearch<CMethodSemanticsMap> + { + public: + CMethodSemanticsMapSearcher(const CMethodSemanticsMap *pBase, int iCount) : CBinarySearch<CMethodSemanticsMap>(pBase, iCount) {} + virtual int Compare(const CMethodSemanticsMap *psFirst, const CMethodSemanticsMap *psSecond); + }; + + static BOOL CompareSignatures(PCCOR_SIGNATURE pvFirstSigBlob, DWORD cbFirstSigBlob, + PCCOR_SIGNATURE pvSecondSigBlob, DWORD cbSecondSigBlob, + void* SigARguments); + + mdTypeDef m_tdModule; // <Module> typedef value. + LONG m_cRefs; // Ref count. + +public: + STDMETHODIMP_(DWORD) GetMetadataStreamVersion() + { + return (DWORD)m_LiteWeightStgdb.m_MiniMd.m_Schema.m_minor | + ((DWORD)m_LiteWeightStgdb.m_MiniMd.m_Schema.m_major << 16); + }; + + STDMETHODIMP SetVerifiedByTrustedSource(// return hresult + BOOL fVerified) + { + m_LiteWeightStgdb.m_MiniMd.SetVerifiedByTrustedSource(fVerified); + return S_OK; + } +}; // class MDInternalRO + +#endif //FEATURE_METADATA_INTERNAL_APIS + +#endif // __MDInternalRO__h__ diff --git a/src/coreclr/md/runtime/metamodel.cpp b/src/coreclr/md/runtime/metamodel.cpp new file mode 100644 index 00000000000..cd1ccde9cc3 --- /dev/null +++ b/src/coreclr/md/runtime/metamodel.cpp @@ -0,0 +1,1204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaModel.cpp -- Base portion of compressed COM+ metadata. +// + +// +//***************************************************************************** +#include "stdafx.h" + +#include <metamodel.h> +#include <corerror.h> +#include <posterror.h> + +//***************************************************************************** +// meta-meta model. +//***************************************************************************** + +//----------------------------------------------------------------------------- +// Start of column definitions. +//----------------------------------------------------------------------------- +// Column type, offset, size. +#define SCHEMA_TABLE_START(tbl) static CMiniColDef r##tbl##Cols[] = { +#define SCHEMA_ITEM_NOFIXED() +#define SCHEMA_ITEM_ENTRY(col,typ) {typ, 0,0}, +#define SCHEMA_ITEM_ENTRY2(col,typ,ofs,siz) {typ, ofs, siz}, +#define SCHEMA_ITEM(tbl,typ,col) SCHEMA_ITEM_ENTRY2(col, i##typ, offsetof(tbl##Rec,m_##col), sizeof(((tbl##Rec*)(0))->m_##col)) +#define SCHEMA_ITEM_RID(tbl,col,tbl2) SCHEMA_ITEM_ENTRY(col,TBL_##tbl2) +#define SCHEMA_ITEM_STRING(tbl,col) SCHEMA_ITEM_ENTRY(col,iSTRING) +#define SCHEMA_ITEM_GUID(tbl,col) SCHEMA_ITEM_ENTRY(col,iGUID) +#define SCHEMA_ITEM_BLOB(tbl,col) SCHEMA_ITEM_ENTRY(col,iBLOB) +#define SCHEMA_ITEM_CDTKN(tbl,col,tkns) SCHEMA_ITEM_ENTRY(col,iCodedToken+(CDTKN_##tkns)) +#define SCHEMA_TABLE_END(tbl) }; +//----------------------------------------------------------------------------- +#include "metamodelcolumndefs.h" +//----------------------------------------------------------------------------- +#undef SCHEMA_TABLE_START +#undef SCHEMA_ITEM_NOFIXED +#undef SCHEMA_ITEM_ENTRY +#undef SCHEMA_ITEM_ENTRY2 +#undef SCHEMA_ITEM +#undef SCHEMA_ITEM_RID +#undef SCHEMA_ITEM_STRING +#undef SCHEMA_ITEM_GUID +#undef SCHEMA_ITEM_BLOB +#undef SCHEMA_ITEM_CDTKN +#undef SCHEMA_TABLE_END +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Column names. +#define SCHEMA_TABLE_START(tbl) static const char *r##tbl##ColNames[] = { +#define SCHEMA_ITEM_NOFIXED() +#define SCHEMA_ITEM_ENTRY(col,typ) #col, +#define SCHEMA_ITEM_ENTRY2(col,typ,ofs,siz) #col, +#define SCHEMA_ITEM(tbl,typ,col) SCHEMA_ITEM_ENTRY2(col, i##typ, offsetof(tbl##Rec,m_##col), sizeof(((tbl##Rec*)(0))->m_##col)) +#define SCHEMA_ITEM_RID(tbl,col,tbl2) SCHEMA_ITEM_ENTRY(col,TBL_##tbl2) +#define SCHEMA_ITEM_STRING(tbl,col) SCHEMA_ITEM_ENTRY(col,iSTRING) +#define SCHEMA_ITEM_GUID(tbl,col) SCHEMA_ITEM_ENTRY(col,iGUID) +#define SCHEMA_ITEM_BLOB(tbl,col) SCHEMA_ITEM_ENTRY(col,iBLOB) +#define SCHEMA_ITEM_CDTKN(tbl,col,tkns) SCHEMA_ITEM_ENTRY(col,iCodedToken+(CDTKN_##tkns)) +#define SCHEMA_TABLE_END(tbl) }; +//----------------------------------------------------------------------------- +#include "metamodelcolumndefs.h" +//----------------------------------------------------------------------------- +#undef SCHEMA_TABLE_START +#undef SCHEMA_ITEM_NOFIXED +#undef SCHEMA_ITEM_ENTRY +#undef SCHEMA_ITEM_ENTRY2 +#undef SCHEMA_ITEM +#undef SCHEMA_ITEM_RID +#undef SCHEMA_ITEM_STRING +#undef SCHEMA_ITEM_GUID +#undef SCHEMA_ITEM_BLOB +#undef SCHEMA_ITEM_CDTKN +#undef SCHEMA_TABLE_END + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB +//Dummy tables to fill the gap to 0x30 +static CMiniColDef rDummy1Cols[] = { { 0 } }; +static CMiniColDef rDummy2Cols[] = { { 0 } }; +static CMiniColDef rDummy3Cols[] = { { 0 } }; +static const char* rDummy1ColNames[] = { "" }; +static const char* rDummy2ColNames[] = { "" }; +static const char* rDummy3ColNames[] = { "" }; +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB + +//----------------------------------------------------------------------------- +// End of column definitions. +//----------------------------------------------------------------------------- + +// Define the array of Coded Token Definitions. +#define MiniMdCodedToken(x) {lengthof(CMiniMdBase::mdt##x), CMiniMdBase::mdt##x, #x}, +const CCodedTokenDef g_CodedTokens [] = { + MiniMdCodedTokens() +}; +#undef MiniMdCodedToken + +// Define the array of Table Definitions. +#undef MiniMdTable +#define MiniMdTable(x) { { r##x##Cols, lengthof(r##x##Cols), x##Rec::COL_KEY, 0 }, r##x##ColNames, #x}, +const CMiniTableDefEx g_Tables[TBL_COUNT] = { + MiniMdTables() +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + PortablePdbMiniMdTables() +#endif +}; + +// Define a table descriptor for the obsolete v1.0 GenericParam table definition. +const CMiniTableDefEx g_Table_GenericParamV1_1 = { { rGenericParamV1_1Cols, lengthof(rGenericParamV1_1Cols), GenericParamV1_1Rec::COL_KEY, 0 }, rGenericParamV1_1ColNames, "GenericParamV1_"}; + + + +// Define the array of Ptr Tables. This is initialized to TBL_COUNT here. +// The correct values will be set in the constructor for MiniMdRW. +#undef MiniMdTable +#define MiniMdTable(x) { TBL_COUNT, 0 }, +TblCol g_PtrTableIxs[TBL_COUNT] = { + MiniMdTables() +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + PortablePdbMiniMdTables() +#endif +}; + +//***************************************************************************** +// Initialize a new schema. +//***************************************************************************** +HRESULT +CMiniMdSchema::InitNew( + MetadataVersion mdVersion) +{ + // Make sure the tables fit in the mask. + _ASSERTE((sizeof(m_maskvalid) * 8) > TBL_COUNT); + + m_ulReserved = 0; + + if(mdVersion == MDVersion1) + { + m_major = METAMODEL_MAJOR_VER_V1_0; + m_minor = METAMODEL_MINOR_VER_V1_0; + } + else if (mdVersion == MDVersion2) + { + m_major = METAMODEL_MAJOR_VER; + m_minor = METAMODEL_MINOR_VER; + } + else + { + return E_INVALIDARG; + } + + m_heaps = 0; + m_rid = 0; + m_maskvalid = 0; + m_sorted = 0; + memset(m_cRecs, 0, sizeof(m_cRecs)); + m_ulExtra = 0; + + return S_OK; +} // CMiniMdSchema::InitNew + +//***************************************************************************** +// Compress a schema into a compressed version of the schema. +//***************************************************************************** +ULONG +CMiniMdSchema::SaveTo( + void *pvData) +{ + ULONG ulData; // Bytes stored. + CMiniMdSchema *pDest = reinterpret_cast<CMiniMdSchema*>(pvData); + const unsigned __int64 one = 1; + + // Make sure the tables fit in the mask. + _ASSERTE((sizeof(m_maskvalid) * 8) > TBL_COUNT); + + // Set the flag for the extra data. +#if defined(EXTRA_DATA) + if (m_ulExtra != 0) + { + m_heaps |= EXTRA_DATA; + } + else +#endif // 0 + { + m_heaps &= ~EXTRA_DATA; + } + + // Minor version is preset when we instantiate the MiniMd. + + // Make sure we're saving out a version that Beta1 version can read + _ASSERTE((m_major == METAMODEL_MAJOR_VER && m_minor == METAMODEL_MINOR_VER) || + (m_major == METAMODEL_MAJOR_VER_B1 && m_minor == METAMODEL_MINOR_VER_B1) || + (m_major == METAMODEL_MAJOR_VER_V1_0 && m_minor == METAMODEL_MINOR_VER_V1_0)); + + // Transfer the fixed fields. + *static_cast<CMiniMdSchemaBase*>(pDest) = *static_cast<CMiniMdSchemaBase*>(this); + static_cast<CMiniMdSchemaBase*>(pDest)->ConvertEndianness(); + ulData = sizeof(CMiniMdSchemaBase); + + // Transfer the variable fields. + m_maskvalid = 0; + for (int iSrc=0, iDst=0; iSrc<TBL_COUNT; ++iSrc) + { + if (m_cRecs[iSrc] != 0) + { + pDest->m_cRecs[iDst++] = VAL32(m_cRecs[iSrc]); + m_maskvalid |= (one << iSrc); + ulData += sizeof(m_cRecs[iSrc]); + } + } + // Refresh the mask. + pDest->m_maskvalid = VAL64(m_maskvalid); + +#if defined(EXTRA_DATA) + // Store the extra data. + if (m_ulExtra != 0) + { + *reinterpret_cast<ULONG*>(&pDest->m_cRecs[iDst]) = VAL32(m_ulExtra); + ulData += sizeof(ULONG); + } +#endif // 0 + return ulData; +} // CMiniMdSchema::SaveTo + +//***************************************************************************** +// Load a schema from a compressed version of the schema. +// Returns count of bytes consumed. -1 if error. +//***************************************************************************** +ULONG +CMiniMdSchema::LoadFrom( + const void *pvData, // Data to load from. + ULONG cbData) // Amount of data available. +{ + ULONG ulData; // Bytes consumed. + + ulData = sizeof(CMiniMdSchemaBase); + + // Be sure we can get the base part. + if (cbData < ulData) + return (ULONG)(-1); + + // Transfer the fixed fields. The (void*) casts prevents the compiler + // from making bad assumptions about the alignment. + memcpy((void *)this, (void *)pvData, sizeof(CMiniMdSchemaBase)); + static_cast<CMiniMdSchemaBase*>(this)->ConvertEndianness(); + + unsigned __int64 maskvalid = m_maskvalid; + + // Transfer the variable fields. + memset(m_cRecs, 0, sizeof(m_cRecs)); + int iDst; + for (iDst = 0; iDst < TBL_COUNT; ++iDst, maskvalid >>= 1) + { + if ((maskvalid & 1) != 0) + { + // Check integer overflow for: ulData + sizeof(UINT32) + ULONG ulDataTemp; + if (!ClrSafeInt<ULONG>::addition(ulData, sizeof(UINT32), ulDataTemp)) + { + return (ULONG)(-1); + } + // Verify that the data is there before touching it. + if (cbData < (ulData + sizeof(UINT32))) + return (ULONG)(-1); + + m_cRecs[iDst] = GET_UNALIGNED_VAL32((const BYTE *)pvData + ulData); + // It's safe to sum, because we checked integer overflow above + ulData += sizeof(UINT32); + } + } + // Also accumulate the sizes of any counters that we don't understand. + for (iDst = TBL_COUNT; (maskvalid != 0) && (iDst < ((int)sizeof(m_maskvalid) * 8)); ++iDst, maskvalid >>= 1) + { + if ((maskvalid & 1) != 0) + { + // Check integer overflow for: ulData += sizeof(UINT32); + if (!ClrSafeInt<ULONG>::addition(ulData, sizeof(UINT32), ulData)) + { + return (ULONG)(-1); + } + // Did we go past end of buffer? + if (cbData < ulData) + { + return (ULONG)(-1); + } + } + } + + // Retrieve the extra 4 bytes data. + if ((m_heaps & EXTRA_DATA) != 0) + { + // Check integer overflow for: ulData + sizeof(UINT32) + ULONG ulDataTemp; + if (!ClrSafeInt<ULONG>::addition(ulData, sizeof(UINT32), ulDataTemp)) + { + return (ULONG)(-1); + } + // Verify that the 4 bytes data is there before touching it. + if (cbData < (ulData + sizeof(UINT32))) + return (ULONG)(-1); + + m_ulExtra = GET_UNALIGNED_VAL32((const BYTE *)pvData + ulData); + // Check the size we used for buffer overflow verification above + ulData += sizeof(UINT32); + } + + // Did we go past end of buffer? + if (cbData < ulData) + return (ULONG)(-1); + + return ulData; +} // CMiniMdSchema::LoadFrom + + +const mdToken CMiniMdBase::mdtTypeDefOrRef[3] = { + mdtTypeDef, + mdtTypeRef, + mdtTypeSpec +}; + +// This array needs to be ordered the same as the source tables are processed (currently +// {field, param, property}) for binary search. +const mdToken CMiniMdBase::mdtHasConstant[3] = { + mdtFieldDef, + mdtParamDef, + mdtProperty +}; + +const mdToken CMiniMdBase::mdtHasCustomAttribute[24] = { + mdtMethodDef, + mdtFieldDef, + mdtTypeRef, + mdtTypeDef, + mdtParamDef, + mdtInterfaceImpl, + mdtMemberRef, + mdtModule, + mdtPermission, + mdtProperty, + mdtEvent, + mdtSignature, + mdtModuleRef, + mdtTypeSpec, + mdtAssembly, + mdtAssemblyRef, + mdtFile, + mdtExportedType, + mdtManifestResource, + mdtGenericParam, + mdtGenericParamConstraint, + mdtMethodSpec +}; + +const mdToken CMiniMdBase::mdtHasFieldMarshal[2] = { + mdtFieldDef, + mdtParamDef, +}; + +const mdToken CMiniMdBase::mdtHasDeclSecurity[3] = { + mdtTypeDef, + mdtMethodDef, + mdtAssembly +}; + +const mdToken CMiniMdBase::mdtMemberRefParent[5] = { + mdtTypeDef, + mdtTypeRef, + mdtModuleRef, + mdtMethodDef, + mdtTypeSpec +}; + +const mdToken CMiniMdBase::mdtHasSemantic[2] = { + mdtEvent, + mdtProperty, +}; + +const mdToken CMiniMdBase::mdtMethodDefOrRef[2] = { + mdtMethodDef, + mdtMemberRef +}; + +const mdToken CMiniMdBase::mdtMemberForwarded[2] = { + mdtFieldDef, + mdtMethodDef +}; + +const mdToken CMiniMdBase::mdtImplementation[3] = { + mdtFile, + mdtAssemblyRef, + mdtExportedType +}; + +const mdToken CMiniMdBase::mdtCustomAttributeType[5] = { + 0, + 0, + mdtMethodDef, + mdtMemberRef, + 0 +}; + +const mdToken CMiniMdBase::mdtResolutionScope[4] = { + mdtModule, + mdtModuleRef, + mdtAssemblyRef, + mdtTypeRef +}; + +const mdToken CMiniMdBase::mdtTypeOrMethodDef[2] = { + mdtTypeDef, + mdtMethodDef +}; + +const int CMiniMdBase::m_cb[] = {0,1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5}; + +//***************************************************************************** +// Function to encode a token into fewer bits. Looks up token type in array of types. +//***************************************************************************** +//<TODO>@consider whether this could be a binary search.</TODO> +ULONG +CMiniMdBase::encodeToken( + RID rid, // Rid to encode. + mdToken typ, // Token type to encode. + const mdToken rTokens[], // Table of valid token. + ULONG32 cTokens) // Size of the table. +{ + mdToken tk = TypeFromToken(typ); + size_t ix; + for (ix = 0; ix < cTokens; ++ix) + { + if (rTokens[ix] == tk) + break; + } + _ASSERTE(ix < cTokens); + if (ix >= cTokens) + return mdTokenNil; + //<TODO>@FUTURE: make compile-time calculation</TODO> + return (ULONG)((rid << m_cb[cTokens]) | ix); +} // CMiniMd::encodeToken + + +//***************************************************************************** +// Helpers for populating the hard-coded schema. +//***************************************************************************** +inline BYTE cbRID(ULONG ixMax) { return ixMax > USHRT_MAX ? (BYTE) sizeof(ULONG) : (BYTE) sizeof(USHORT); } + +#define _CBTKN(cRecs,tkns) cbRID((cRecs) << m_cb[lengthof(tkns)]) + +//***************************************************************************** +// Constructor. +//***************************************************************************** +CMiniMdBase::CMiniMdBase() +{ +#undef MiniMdTable +#define MiniMdTable(tbl) \ + m_TableDefs[TBL_##tbl] = g_Tables[TBL_##tbl].m_Def; \ + m_TableDefs[TBL_##tbl].m_pColDefs = BYTEARRAY_TO_COLDES(s_##tbl##Col); + MiniMdTables() +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + PortablePdbMiniMdTables() +#endif + + m_TblCount = TBL_COUNT; + _ASSERTE(TBL_COUNT == TBL_COUNT_V2); // v2 counts. + + m_fVerifiedByTrustedSource = FALSE; + + // Validator depends on the Table Ids and the Token Ids being identical. + // Catch it if this ever breaks. + _ASSERTE((TypeFromToken(mdtModule) >> 24) == TBL_Module); + _ASSERTE((TypeFromToken(mdtTypeRef) >> 24) == TBL_TypeRef); + _ASSERTE((TypeFromToken(mdtTypeDef) >> 24) == TBL_TypeDef); + _ASSERTE((TypeFromToken(mdtFieldDef) >> 24) == TBL_Field); + _ASSERTE((TypeFromToken(mdtMethodDef) >> 24) == TBL_Method); + _ASSERTE((TypeFromToken(mdtParamDef) >> 24) == TBL_Param); + _ASSERTE((TypeFromToken(mdtInterfaceImpl) >> 24) == TBL_InterfaceImpl); + _ASSERTE((TypeFromToken(mdtMemberRef) >> 24) == TBL_MemberRef); + _ASSERTE((TypeFromToken(mdtCustomAttribute) >> 24) == TBL_CustomAttribute); + _ASSERTE((TypeFromToken(mdtPermission) >> 24) == TBL_DeclSecurity); + _ASSERTE((TypeFromToken(mdtSignature) >> 24) == TBL_StandAloneSig); + _ASSERTE((TypeFromToken(mdtEvent) >> 24) == TBL_Event); + _ASSERTE((TypeFromToken(mdtProperty) >> 24) == TBL_Property); + _ASSERTE((TypeFromToken(mdtModuleRef) >> 24) == TBL_ModuleRef); + _ASSERTE((TypeFromToken(mdtTypeSpec) >> 24) == TBL_TypeSpec); + _ASSERTE((TypeFromToken(mdtAssembly) >> 24) == TBL_Assembly); + _ASSERTE((TypeFromToken(mdtAssemblyRef) >> 24) == TBL_AssemblyRef); + _ASSERTE((TypeFromToken(mdtFile) >> 24) == TBL_File); + _ASSERTE((TypeFromToken(mdtExportedType) >> 24) == TBL_ExportedType); + _ASSERTE((TypeFromToken(mdtManifestResource) >> 24) == TBL_ManifestResource); + _ASSERTE((TypeFromToken(mdtGenericParam) >> 24) == TBL_GenericParam); + _ASSERTE((TypeFromToken(mdtMethodSpec) >> 24) == TBL_MethodSpec); + _ASSERTE((TypeFromToken(mdtGenericParamConstraint) >> 24) == TBL_GenericParamConstraint); +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + _ASSERTE((TypeFromToken(mdtDocument) >> 24) == TBL_Document); + _ASSERTE((TypeFromToken(mdtMethodDebugInformation) >> 24) == TBL_MethodDebugInformation); + _ASSERTE((TypeFromToken(mdtLocalScope) >> 24) == TBL_LocalScope); + _ASSERTE((TypeFromToken(mdtLocalVariable) >> 24) == TBL_LocalVariable); + _ASSERTE((TypeFromToken(mdtLocalConstant) >> 24) == TBL_LocalConstant); + _ASSERTE((TypeFromToken(mdtImportScope) >> 24) == TBL_ImportScope); + // TODO: + // _ASSERTE((TypeFromToken(mdtStateMachineMethod) >> 24) == TBL_StateMachineMethod); + // _ASSERTE((TypeFromToken(mdtCustomDebugInformation) >> 24) == TBL_CustomDebugInformation); +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB +} // CMiniMdBase::CMiniMdBase + + +//***************************************************************************** +// Destructor. +//***************************************************************************** +CMiniMdBase::~CMiniMdBase() +{ + for (ULONG i = 0; i < m_TblCount; i++) + { + if ((m_TableDefs[i].m_pColDefs != NULL) && UsesAllocatedMemory(m_TableDefs[i].m_pColDefs)) + { + delete[] COLDES_TO_BYTEARRAY(m_TableDefs[i].m_pColDefs); + m_TableDefs[i].m_pColDefs = NULL; + } + } +} // CMiniMdBase::~CMiniMdBase + +//***************************************************************************** +// Build the schema based on the header data provided. +// Handle all supported versions, and adjust data structures appropriately. +//***************************************************************************** +HRESULT +CMiniMdBase::SchemaPopulate( + const void *pvData, // Pointer to the buffer. + ULONG cbData, // Size of the buffer. + ULONG *pcbUsed) // Put size of the header here. +{ + HRESULT hr; + ULONG cb; // Bytes read for header. + ULONG cbTables; // Bytes needed for tables. + ULONG cbTotal; // Bytes read for header + needed for tables. + + // Uncompress the schema from the buffer into our structures. + cb = m_Schema.LoadFrom(pvData, cbData); + + if ((cb > cbData) || (cb == (ULONG)(-1))) + { + Debug_ReportError("Schema is not in MetaData block."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // Is this the "native" version of the metadata for this runtime? + if ((m_Schema.m_major != METAMODEL_MAJOR_VER) || (m_Schema.m_minor != METAMODEL_MINOR_VER)) + { + // No it's not. Is this an older version that we support? + + // Is this v1.0? + if ((m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0) && + (m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0)) + { + // Older version has fewer tables. + m_TblCount = TBL_COUNT_V1; + } + else if ((m_Schema.m_major == METAMODEL_MAJOR_VER_B1) && + (m_Schema.m_minor == METAMODEL_MINOR_VER_B1)) + { + // 1.1 had a different type of GenericParam table + m_TableDefs[TBL_GenericParam] = g_Table_GenericParamV1_1.m_Def; + m_TableDefs[TBL_GenericParam].m_pColDefs = BYTEARRAY_TO_COLDES(s_GenericParamCol); + } + else + { // We don't support this version of the metadata + Debug_ReportError("Unsupported version of MetaData."); + return PostError(CLDB_E_FILE_OLDVER, m_Schema.m_major, m_Schema.m_minor); + } + } + + // Populate the schema, based on the row counts and heap sizes. + IfFailRet(SchemaPopulate2(&cbTables)); + + // Check that header plus tables fits within the size given. + if (!ClrSafeInt<ULONG>::addition(cb, cbTables, cbTotal) || (cbTotal > cbData)) + { + Debug_ReportError("Tables are not within MetaData block."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + *pcbUsed = cb; + return S_OK; +} // CMiniMdBase::SchemaPopulate + +//***************************************************************************** +// Initialize from another MD +//***************************************************************************** +HRESULT +CMiniMdBase::SchemaPopulate( + const CMiniMdBase &that) +{ + HRESULT hr; + // Copy over the schema. + m_Schema = that.m_Schema; + + // Adjust for prior versions. + if (m_Schema.m_major != METAMODEL_MAJOR_VER || m_Schema.m_minor != METAMODEL_MINOR_VER) + { + if ((m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0) && (m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0)) + { // Older version has fewer tables. + m_TblCount = that.m_TblCount; + _ASSERTE(m_TblCount == TBL_COUNT_V1); + } + else if (m_Schema.m_major == METAMODEL_MAJOR_VER_B1 && m_Schema.m_minor == METAMODEL_MINOR_VER_B1) + { + // 1.1 had a different type of GenericParam table + m_TableDefs[TBL_GenericParam] = g_Table_GenericParamV1_1.m_Def; + m_TableDefs[TBL_GenericParam].m_pColDefs = BYTEARRAY_TO_COLDES(s_GenericParamCol); + } + // Is it a supported old version? This should never fail! + else + { + Debug_ReportError("Initializing on an unknown schema version"); + return PostError(CLDB_E_FILE_OLDVER, m_Schema.m_major,m_Schema.m_minor); + } + } + + IfFailRet(SchemaPopulate2(NULL)); + + return S_OK; +} // CMiniMdBase::SchemaPopulate + +//***************************************************************************** +// Iterate the tables, and fix the column sizes, based on size of data. +//***************************************************************************** +HRESULT CMiniMdBase::SchemaPopulate2( + ULONG *pcbTables, // [out, optional] Put size needed for tables here. + int bExtra) // Reserve an extra bit for rid columns? +{ + HRESULT hr; // A result. + ULONG cbTotal = 0; // Total size of all tables. + + // How big are the various pool inidices? + m_iStringsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_STRING_4) ? 0xffffffff : 0xffff; + m_iGuidsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_GUID_4) ? 0xffffffff : 0xffff; + m_iBlobsMask = (m_Schema.m_heaps & CMiniMdSchema::HEAP_BLOB_4) ? 0xffffffff : 0xffff; + + // Make extra bits exactly zero or one bit. + if (bExtra) + bExtra = 1; + + // Until ENC, make extra bits exactly zero. + bExtra = 0; + + // For each table... + for (int ixTbl = 0; ixTbl < (int)m_TblCount; ++ixTbl) + { + IfFailRet(InitColsForTable(m_Schema, ixTbl, &m_TableDefs[ixTbl], bExtra, TRUE)); + + // Accumulate size of this table. + // Check integer overflow for table size: USHORT * ULONG: m_TableDefs[ixTbl].m_cbRec * GetCountRecs(ixTbl) + ULONG cbTable; + if (!ClrSafeInt<ULONG>::multiply(m_TableDefs[ixTbl].m_cbRec, GetCountRecs(ixTbl), cbTable)) + { + Debug_ReportError("Table is too large - size overflow."); + return PostError(CLDB_E_FILE_CORRUPT); + } + // Check integer overflow for all tables so far: cbTotal += cbTable + if (!ClrSafeInt<ULONG>::addition(cbTotal, cbTable, cbTotal)) + { + Debug_ReportError("Tables are too large - size overflow."); + return PostError(CLDB_E_FILE_CORRUPT); + } + } + // Check that unused table (e.g. generic tables in v1 format) are empty + for (ULONG ixTbl = m_TblCount; ixTbl < TBL_COUNT; ixTbl++) + { + // All unused tables have to be empty - malicious assemblies can have v1 format version, but can + // contain non-empty v2-only tables, this will catch it and refuse to load such assemblies + if (GetCountRecs(ixTbl) != 0) + { + Debug_ReportError("Invalid table present - 2.0 table in v1.x image."); + return PostError(CLDB_E_FILE_CORRUPT); + } + } + + // Let caller know sizes required. + if (pcbTables != NULL) + *pcbTables = cbTotal; + + return S_OK; +} // CMiniMdBase::SchemaPopulate2 + +//***************************************************************************** +// Get the template table definition for a given table. +//***************************************************************************** +const CMiniTableDef * +CMiniMdBase::GetTableDefTemplate( + int ixTbl) +{ + const CMiniTableDef *pTemplate; // the return value. + + // Return the table definition for the given table. Account for version of schema. + if ((m_Schema.m_major == METAMODEL_MAJOR_VER_B1) && (m_Schema.m_minor == METAMODEL_MINOR_VER_B1) && (ixTbl == TBL_GenericParam)) + { + pTemplate = &g_Table_GenericParamV1_1.m_Def; + } + else + { + pTemplate = &g_Tables[ixTbl].m_Def; + } + + return pTemplate; +} // CMiniMdBase::GetTableDefTemplate + +//***************************************************************************** +// Initialize the column defs for a table, based on their types and sizes. +//***************************************************************************** +HRESULT +CMiniMdBase::InitColsForTable( + CMiniMdSchema &Schema, // Schema with sizes. + int ixTbl, // Index of table to init. + CMiniTableDef *pTable, // Table to init. + int bExtra, // Extra bits for rid column. + BOOL fUsePointers) // Should we have pTable point to it's Column Descriptors, or + // should we write the data into the structure +{ + const CMiniTableDef *pTemplate; // Template table definition. + CMiniColDef pCols[9]; // The col defs to init. + BYTE iOffset; // Running size of a record. + BYTE iSize; // Size of a field. + HRESULT hr = S_OK; + + _ASSERTE((bExtra == 0) || (bExtra == 1)); + _ASSERTE(NumItems(pCols) >= pTable->m_cCols); + + bExtra = 0;//<TODO>@FUTURE: save in schema header. until then use 0.</TODO> + + iOffset = 0; + + pTemplate = GetTableDefTemplate(ixTbl); + + PREFIX_ASSUME(pTemplate->m_pColDefs != NULL); + + // For each column in the table... + for (ULONG ixCol = 0; ixCol < pTable->m_cCols; ++ixCol) + { + // Initialize from the template values (type, maybe offset, size). + pCols[ixCol] = pTemplate->m_pColDefs[ixCol]; + + // Is the field a RID into a table? + if (pCols[ixCol].m_Type <= iRidMax) + { + iSize = cbRID(Schema.m_cRecs[pCols[ixCol].m_Type] << bExtra); + } + else + // Is the field a coded token? + if (pCols[ixCol].m_Type <= iCodedTokenMax) + { + ULONG iCdTkn = pCols[ixCol].m_Type - iCodedToken; + ULONG cRecs = 0; + + _ASSERTE(iCdTkn < lengthof(g_CodedTokens)); + CCodedTokenDef const *pCTD = &g_CodedTokens[iCdTkn]; + + // Iterate the token list of this coded token. + for (ULONG ixToken=0; ixToken<pCTD->m_cTokens; ++ixToken) + { // Ignore string tokens. + if (pCTD->m_pTokens[ixToken] != mdtString) + { + // Get the table for the token. + ULONG nTokenTable = CMiniMdRW::GetTableForToken(pCTD->m_pTokens[ixToken]); + _ASSERTE(nTokenTable < TBL_COUNT); + // If largest token seen so far, remember it. + if (Schema.m_cRecs[nTokenTable] > cRecs) + cRecs = Schema.m_cRecs[nTokenTable]; + } + } + + iSize = cbRID(cRecs << (bExtra + m_cb[pCTD->m_cTokens])); + + } + else + { // Fixed type. + switch (pCols[ixCol].m_Type) + { +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + /* Portable PDB tables */ + // Commenting out column offset asserts. + // It makes sense to assert calculated offsets against values from the table + // definition templates, only if the fixed field columns appear at the beginning + // of a table record. + // Initializing StartOffset and Length (4th and 5th columns of the portable PDB + // LocalScope table) can cause assertion to fail at this point, since preceeding + // column sizes are determined dynamically (2 or 4 bytes for RIDs depending on the + // number of records) and cannot be compared against the static template. +#endif + case iBYTE: + iSize = 1; + _ASSERTE(pCols[ixCol].m_cbColumn == iSize); +#ifndef FEATURE_METADATA_EMIT_PORTABLE_PDB + _ASSERTE(pCols[ixCol].m_oColumn == iOffset); +#endif + break; + case iSHORT: + case iUSHORT: + iSize = 2; + _ASSERTE(pCols[ixCol].m_cbColumn == iSize); +#ifndef FEATURE_METADATA_EMIT_PORTABLE_PDB + _ASSERTE(pCols[ixCol].m_oColumn == iOffset); +#endif + break; + case iLONG: + case iULONG: + iSize = 4; + _ASSERTE(pCols[ixCol].m_cbColumn == iSize); +#ifndef FEATURE_METADATA_EMIT_PORTABLE_PDB + _ASSERTE(pCols[ixCol].m_oColumn == iOffset); +#endif + break; + case iSTRING: + iSize = (Schema.m_heaps & CMiniMdSchema::HEAP_STRING_4) ? 4 : 2; + break; + case iGUID: + iSize = (Schema.m_heaps & CMiniMdSchema::HEAP_GUID_4) ? 4 : 2; + break; + case iBLOB: + iSize = (Schema.m_heaps & CMiniMdSchema::HEAP_BLOB_4) ? 4 : 2; + break; + default: + _ASSERTE(!"Unexpected schema type"); + iSize = 0; + break; + } + } + + // Now save the size and offset. + pCols[ixCol].m_oColumn = iOffset; + pCols[ixCol].m_cbColumn = iSize; + + // Align to 2 bytes. + iSize += iSize & 1; + + iOffset += iSize; + } + // Record size of entire record. + pTable->m_cbRec = iOffset; + + _ASSERTE(pTable->m_pColDefs != NULL); + + // Can we write to the memory + if (!fUsePointers) + { + memcpy(pTable->m_pColDefs, pCols, sizeof(CMiniColDef)*pTable->m_cCols); + } + else + { + // We'll need to have pTable->m_pColDefs point to some data instead + hr = SetNewColumnDefinition(pTable, pCols, ixTbl); + } + // If no key, set to a distinct value. + if (pTable->m_iKey >= pTable->m_cCols) + pTable->m_iKey = (BYTE) -1; + + return hr; +} // CMiniMdBase::InitColsForTable + +//***************************************************************************** +// Place a new Column Definition into the metadata. +//***************************************************************************** +HRESULT +CMiniMdBase::SetNewColumnDefinition( + CMiniTableDef *pTable, + CMiniColDef *pCols, + DWORD ixTbl) +{ + // Look up the global cache to see if we can use a cached copy + if (UsesAllocatedMemory(pCols) || + !FindSharedColDefs(pTable, pCols, ixTbl)) + { + // See if we've already allocated memory for this item + + if (!UsesAllocatedMemory(pTable->m_pColDefs)) + { + // We don't have this column definition cached. Allocate new memory for it. + // Notice, we allocate one more byte than necessary, so we can 'mark' this chunk of memory + // as allocated so we can free it later. + + BYTE *newMemory = new (nothrow) BYTE[(sizeof(CMiniColDef)*pTable->m_cCols)+1]; + + if (newMemory == NULL) + return E_OUTOFMEMORY; + + // Mark the first byte in this as with the "allocated memory marker" + *newMemory = ALLOCATED_MEMORY_MARKER; + + // Have the pointer point to the first Column Descriptor + pTable->m_pColDefs = BYTEARRAY_TO_COLDES(newMemory); + } + + memcpy(pTable->m_pColDefs, pCols, sizeof(CMiniColDef)*pTable->m_cCols); + } + + return S_OK; +} // CMiniMdBase::SetNewColumnDefinition + + +//***************************************************************************** +// Get the count of records in a table. Virtual. +//***************************************************************************** +ULONG +CMiniMdBase::GetCountRecs( + ULONG ixTbl) +{ + _ASSERTE(ixTbl < TBL_COUNT); + return m_Schema.m_cRecs[ixTbl]; +} // CMiniMdBase::GetCountRecs + + +#if BIGENDIAN +// Endian Swaps the passed in blob representing a constant into the passed in StgPool +HRESULT +CMiniMdBase::SwapConstant( + const void *pBlobValue, // Original Value pointer + DWORD dwType, // Type of the constant + void *pConstant, // [Out] Location to store constant into + ULONG ValueLength) // [In] Length of constant +{ + HRESULT hr = NOERROR; + + switch (dwType) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_VOID: + // Just return the value + *(BYTE *)pConstant = *(BYTE *)pBlobValue; + return NOERROR; + + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: + _ASSERTE(ValueLength == 2); + *(SHORT *)pConstant = GET_UNALIGNED_VAL16(pBlobValue); + break; + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + _ASSERTE(ValueLength == 4); + *(__int32 *)pConstant = GET_UNALIGNED_VAL32(pBlobValue); + break; + case ELEMENT_TYPE_R4: + { + __int32 Value = GET_UNALIGNED_VAL32(pBlobValue); + *(float *)pConstant = (float &)Value; + } + break; + + case ELEMENT_TYPE_R8: + { + __int64 Value = GET_UNALIGNED_VAL64(pBlobValue); + *(double *)pConstant = (double &) Value; + } + break; + + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + _ASSERTE(ValueLength == 8); + *(__int64 *)pConstant = GET_UNALIGNED_VAL64(pBlobValue); + break; + case ELEMENT_TYPE_STRING: + memcpy(pConstant, pBlobValue, ValueLength); + SwapStringLength((WCHAR *)pConstant, (ValueLength)/sizeof(WCHAR)); + break; + default: + _ASSERTE(!"BAD TYPE!"); + return E_INVALIDARG; + break; + } + return hr; +} // CMiniMdBase::SwapConstant +#endif //BIGENDIAN + +//***************************************************************************** +// It is non-trivial to sort propertymap. VB is generating properties in +// non-sorted order!!! +//***************************************************************************** +HRESULT +CMiniMdBase::FindPropertyMapFor( + RID ridParent, + RID *pFoundRid) +{ + HRESULT hr; + ULONG i; + ULONG iCount; + void *pRec; + RID rid; + + // If the table is sorted, use binary search. However we can only trust + // the sorted bit if we have verified it (see definition in MetaModel.h) + if (IsVerified() && m_Schema.IsSorted(TBL_PropertyMap)) + { + return vSearchTable(TBL_PropertyMap, + _COLDEF(PropertyMap,Parent), + ridParent, + pFoundRid); + } + else + { + iCount = GetCountRecs(TBL_PropertyMap); + + // loop through all LocalVar + for (i = 1; i <= iCount; i++) + { + IfFailRet(vGetRow(TBL_PropertyMap, i, &pRec)); + + // linear search for propertymap record + rid = getIX(pRec, _COLDEF(PropertyMap,Parent)); + if (rid == ridParent) + { + *pFoundRid = i; + return S_OK; + } + } + + *pFoundRid = 0; + return S_OK; + } + +} // CMiniMdBase::FindPropertyMapFor + + +//***************************************************************************** +// It is non-trivial to sort eventmap. VB is generating events in +// non-sorted order!!! +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdBase::FindEventMapFor( + RID ridParent, + RID *pFoundRid) +{ + HRESULT hr; + ULONG i; + ULONG iCount; + void *pRec; + RID rid; + + // If the table is sorted, use binary search. However we can only trust + // the sorted bit if we have verified it (see definition in MetaModel.h) + if (IsVerified() && m_Schema.IsSorted(TBL_EventMap)) + { + return vSearchTable(TBL_EventMap, + _COLDEF(EventMap,Parent), + ridParent, + pFoundRid); + } + else + { + iCount = GetCountRecs(TBL_EventMap); + + // loop through all LocalVar + for (i = 1; i <= iCount; i++) + { + IfFailRet(vGetRow(TBL_EventMap, i, &pRec)); + + // linear search for propertymap record + rid = getIX(pRec, _COLDEF(EventMap,Parent)); + if (rid == ridParent) + { + *pFoundRid = i; + return S_OK; + } + } + + *pFoundRid = 0; + return S_OK; + } +} // CMiniMdBase::FindEventMapFor + + +//***************************************************************************** +// Search for a custom value with a given type. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMdBase::FindCustomAttributeFor( + RID rid, // The object's rid. + mdToken tkObj, // The object's type. + mdToken tkType, // Type of custom value. + RID *pFoundRid) // RID of custom value, or 0. +{ + HRESULT hr; + int ixFound; // index of some custom value row. + ULONG ulTarget = encodeToken(rid,tkObj,mdtHasCustomAttribute,lengthof(mdtHasCustomAttribute)); // encoded token representing target. + ULONG ixCur; // Current row being examined. + mdToken tkFound; // Type of some custom value row. + void *pCur; // A custom value entry. + + // Search for any entry in CustomAttribute table. Convert to RID. + IfFailRet(vSearchTable(TBL_CustomAttribute, _COLDEF(CustomAttribute,Parent), ulTarget, (RID *)&ixFound)); + if (ixFound == 0) + { + *pFoundRid = 0; + return S_OK; + } + + // Found an entry that matches the item. Could be anywhere in a range of + // custom values for the item, somewhat at random. Search for a match + // on name. On entry to the first loop, we know the object is the desired + // one, so the object test is at the bottom. + ixCur = ixFound; + IfFailRet(vGetRow(TBL_CustomAttribute, ixCur, &pCur)); + for(;;) + { + // Test the type of the current row. + tkFound = getIX(pCur, _COLDEF(CustomAttribute,Type)); + tkFound = decodeToken(tkFound, mdtCustomAttributeType, lengthof(mdtCustomAttributeType)); + if (tkFound == tkType) + { + *pFoundRid = ixCur; + return S_OK; + } + // Was this the last row of the CustomAttribute table? + if (ixCur == GetCountRecs(TBL_CustomAttribute)) + break; + // No match, more rows, try for the next row. + ++ixCur; + // Get the row and see if it is for the same object. + IfFailRet(vGetRow(TBL_CustomAttribute, ixCur, &pCur)); + if (getIX(pCur, _COLDEF(CustomAttribute,Parent)) != ulTarget) + break; + } + // Didn't find the name looking up. Try looking down. + ixCur = ixFound - 1; + for(;;) + { + // Run out of table yet? + if (ixCur == 0) + break; + // Get the row and see if it is for the same object. + IfFailRet(vGetRow(TBL_CustomAttribute, ixCur, &pCur)); + // still looking at the same object? + if (getIX(pCur, _COLDEF(CustomAttribute,Parent)) != ulTarget) + break; + // Test the type of the current row. + tkFound = getIX(pCur, _COLDEF(CustomAttribute,Type)); + tkFound = decodeToken(tkFound, mdtCustomAttributeType, lengthof(mdtCustomAttributeType)); + if (tkFound == tkType) + { + *pFoundRid = ixCur; + return S_OK; + } + // No match, try for the previous row. + --ixCur; + } + // Didn't find anything. + *pFoundRid = 0; + return S_OK; +} // CMiniMdBase::FindCustomAttributeFor + +//***************************************************************************** +// See if we can find a globally shared Column Def Array for this table +//***************************************************************************** +BOOL +CMiniMdBase::FindSharedColDefs( + CMiniTableDef *pTable, + CMiniColDef *pColsToMatch, + DWORD ixTbl) +{ + // The majority of the time, m_pColDefs will point to the correct Column Definition Array. + if (!memcmp(pTable->m_pColDefs, pColsToMatch, sizeof(CMiniColDef)*(pTable->m_cCols))) + return TRUE; + + else + { + // m_pColDefs points to a set of Column Def Arrays, with the byte previous to it the number + // of column descriptors that we have. + CMiniColDef *pListOfColumnDefs = BYTEARRAY_TO_COLDES(s_TableColumnDescriptors[ixTbl]); + + BYTE nNumColDes = *(s_TableColumnDescriptors[ixTbl]); + + // Start at '1' since we already compared the first set of column definitions above. + for (int i = 1; i < nNumColDes; i++) + { + pListOfColumnDefs += pTable->m_cCols; + + if (!memcmp(pListOfColumnDefs, pColsToMatch, sizeof(CMiniColDef)*(pTable->m_cCols))) + { + pTable->m_pColDefs = pListOfColumnDefs; + return TRUE; + } + } + } + + // We weren't able to find a shared column definition + return FALSE; +}// CMiniMdBase::FindSharedColDefs + +//***************************************************************************** +// Determines where the Table Def's Column Definitions used shared memory or +// allocated memory +//***************************************************************************** +BOOL +CMiniMdBase::UsesAllocatedMemory( + CMiniColDef *pCols) +{ + BYTE *pMem = COLDES_TO_BYTEARRAY(pCols); + + // If the byte preceding this pointer is -1, then we allocated it and it must be freed + return (*pMem == ALLOCATED_MEMORY_MARKER); +}// CMiniMdBase::UsesAllocatedMemory diff --git a/src/coreclr/md/runtime/metamodelcolumndefs.h b/src/coreclr/md/runtime/metamodelcolumndefs.h new file mode 100644 index 00000000000..6790252d825 --- /dev/null +++ b/src/coreclr/md/runtime/metamodelcolumndefs.h @@ -0,0 +1,454 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaModelColumnDefs.h -- Table definitions for MetaData. +// + +// +//***************************************************************************** + +#if METAMODEL_MAJOR_VER != 2 +#if METAMODEL_MAJOR_VER != 1 +#error "METAMODEL_MAJOR_VER other than 1 or 2 is not implemented" +#endif +#endif + // + // These are used by #defining appropriately, then #including this file. + // + //------------------------------------------------------------------------- + //Module + SCHEMA_TABLE_START(Module) + SCHEMA_ITEM(Module, USHORT, Generation) + SCHEMA_ITEM_STRING(Module, Name) + SCHEMA_ITEM_GUID(Module, Mvid) + SCHEMA_ITEM_GUID(Module, EncId) + SCHEMA_ITEM_GUID(Module, EncBaseId) + SCHEMA_TABLE_END(Module) + + //------------------------------------------------------------------------- + //TypeRef + SCHEMA_TABLE_START(TypeRef) + SCHEMA_ITEM_CDTKN(TypeRef, ResolutionScope, ResolutionScope) + SCHEMA_ITEM_STRING(TypeRef, Name) + SCHEMA_ITEM_STRING(TypeRef, Namespace) + SCHEMA_TABLE_END(TypeRef) + + //------------------------------------------------------------------------- + // TypeDef + SCHEMA_TABLE_START(TypeDef) + SCHEMA_ITEM(TypeDef, ULONG, Flags) + SCHEMA_ITEM_STRING(TypeDef, Name) + SCHEMA_ITEM_STRING(TypeDef, Namespace) + SCHEMA_ITEM_CDTKN(TypeDef, Extends, TypeDefOrRef) + SCHEMA_ITEM_RID(TypeDef, FieldList, Field) + SCHEMA_ITEM_RID(TypeDef, MethodList, Method) + SCHEMA_TABLE_END(TypeDef) + + //------------------------------------------------------------------------- + //FieldPtr + SCHEMA_TABLE_START(FieldPtr) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_RID(FieldPtr, Field, Field) + SCHEMA_TABLE_END(FieldPtr) + + //------------------------------------------------------------------------- + //Field + SCHEMA_TABLE_START(Field) + SCHEMA_ITEM(Field, USHORT, Flags) + SCHEMA_ITEM_STRING(Field,Name) + SCHEMA_ITEM_BLOB(Field,Signature) + SCHEMA_TABLE_END(Field) + + //------------------------------------------------------------------------- + //MethodPtr + SCHEMA_TABLE_START(MethodPtr) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_RID(MethodPtr, Method, Method) + SCHEMA_TABLE_END(MethodPtr) + + //------------------------------------------------------------------------- + //Method + SCHEMA_TABLE_START(Method) + SCHEMA_ITEM(Method, ULONG, RVA) + SCHEMA_ITEM(Method, USHORT, ImplFlags) + SCHEMA_ITEM(Method, USHORT, Flags) + SCHEMA_ITEM_STRING(Method,Name) + SCHEMA_ITEM_BLOB(Method,Signature) + SCHEMA_ITEM_RID(Method,ParamList,Param) + SCHEMA_TABLE_END(Method) + + //------------------------------------------------------------------------- + //ParamPtr + SCHEMA_TABLE_START(ParamPtr) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_RID(ParamPtr, Param, Param) + SCHEMA_TABLE_END(ParamPtr) + + //------------------------------------------------------------------------- + // Param + SCHEMA_TABLE_START(Param) + SCHEMA_ITEM(Param, USHORT, Flags) + SCHEMA_ITEM(Param, USHORT, Sequence) + SCHEMA_ITEM_STRING(Param,Name) + SCHEMA_TABLE_END(Param) + + //------------------------------------------------------------------------- + //InterfaceImpl + SCHEMA_TABLE_START(InterfaceImpl) + SCHEMA_ITEM_RID(InterfaceImpl,Class,TypeDef) + SCHEMA_ITEM_CDTKN(InterfaceImpl,Interface,TypeDefOrRef) + SCHEMA_TABLE_END(InterfaceImpl) + + //------------------------------------------------------------------------- + //MemberRef + SCHEMA_TABLE_START(MemberRef) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_CDTKN(MemberRef,Class,MemberRefParent) + SCHEMA_ITEM_STRING(MemberRef,Name) + SCHEMA_ITEM_BLOB(MemberRef,Signature) + SCHEMA_TABLE_END(MemberRef) + + //------------------------------------------------------------------------- + //Constant + SCHEMA_TABLE_START(Constant) + SCHEMA_ITEM(Constant, BYTE, Type) + SCHEMA_ITEM_CDTKN(Constant,Parent,HasConstant) + SCHEMA_ITEM_BLOB(Constant,Value) + SCHEMA_TABLE_END(Constant) + + //------------------------------------------------------------------------- + //CustomAttribute + SCHEMA_TABLE_START(CustomAttribute) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_CDTKN(CustomAttribute,Parent,HasCustomAttribute) + SCHEMA_ITEM_CDTKN(CustomAttribute,Type,CustomAttributeType) + SCHEMA_ITEM_BLOB(CustomAttribute,Value) + SCHEMA_TABLE_END(CustomAttribute) + + //------------------------------------------------------------------------- + //FieldMarshal + SCHEMA_TABLE_START(FieldMarshal) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_CDTKN(FieldMarshal,Parent,HasFieldMarshal) + SCHEMA_ITEM_BLOB(FieldMarshal,NativeType) + SCHEMA_TABLE_END(FieldMarshal) + + //------------------------------------------------------------------------- + //DeclSecurity + SCHEMA_TABLE_START(DeclSecurity) + SCHEMA_ITEM(DeclSecurity, SHORT, Action) + SCHEMA_ITEM_CDTKN(DeclSecurity,Parent,HasDeclSecurity) + SCHEMA_ITEM_BLOB(DeclSecurity,PermissionSet) + SCHEMA_TABLE_END(DeclSecurity) + + //------------------------------------------------------------------------- + //ClassLayout + SCHEMA_TABLE_START(ClassLayout) + SCHEMA_ITEM(ClassLayout, USHORT, PackingSize) + SCHEMA_ITEM(ClassLayout, ULONG, ClassSize) + SCHEMA_ITEM_RID(ClassLayout,Parent,TypeDef) + SCHEMA_TABLE_END(ClassLayout) + + //------------------------------------------------------------------------- + //FieldLayout + SCHEMA_TABLE_START(FieldLayout) + SCHEMA_ITEM(FieldLayout, ULONG, OffSet) + SCHEMA_ITEM_RID(FieldLayout, Field, Field) + SCHEMA_TABLE_END(FieldLayout) + + //------------------------------------------------------------------------- + //StandAloneSig + SCHEMA_TABLE_START(StandAloneSig) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_BLOB(StandAloneSig,Signature) + SCHEMA_TABLE_END(StandAloneSig) + + //------------------------------------------------------------------------- + //EventMap + SCHEMA_TABLE_START(EventMap) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_RID(EventMap,Parent,TypeDef) + SCHEMA_ITEM_RID(EventMap,EventList,Event) + SCHEMA_TABLE_END(EventMap) + + //------------------------------------------------------------------------- + //EventPtr + SCHEMA_TABLE_START(EventPtr) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_RID(EventPtr, Event, Event) + SCHEMA_TABLE_END(EventPtr) + + //------------------------------------------------------------------------- + //Event + SCHEMA_TABLE_START(Event) + SCHEMA_ITEM(Event, USHORT, EventFlags) + SCHEMA_ITEM_STRING(Event,Name) + SCHEMA_ITEM_CDTKN(Event,EventType,TypeDefOrRef) + SCHEMA_TABLE_END(Event) + + //------------------------------------------------------------------------- + //PropertyMap + SCHEMA_TABLE_START(PropertyMap) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_RID(PropertyMap,Parent,TypeDef) + SCHEMA_ITEM_RID(PropertyMap,PropertyList,Property) + SCHEMA_TABLE_END(PropertyMap) + + //------------------------------------------------------------------------- + //PropertyPtr + SCHEMA_TABLE_START(PropertyPtr) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_RID(PropertyPtr, Property, Property) + SCHEMA_TABLE_END(PropertyPtr) + + //------------------------------------------------------------------------- + //Property + SCHEMA_TABLE_START(Property) + SCHEMA_ITEM(Property, USHORT, PropFlags) + SCHEMA_ITEM_STRING(Property,Name) + SCHEMA_ITEM_BLOB(Property,Type) + SCHEMA_TABLE_END(Property) + + //------------------------------------------------------------------------- + //MethodSemantics + SCHEMA_TABLE_START(MethodSemantics) + SCHEMA_ITEM(MethodSemantics, USHORT, Semantic) + SCHEMA_ITEM_RID(MethodSemantics,Method,Method) + SCHEMA_ITEM_CDTKN(MethodSemantics,Association,HasSemantic) + SCHEMA_TABLE_END(MethodSemantics) + + //------------------------------------------------------------------------- + //MethodImpl + SCHEMA_TABLE_START(MethodImpl) + SCHEMA_ITEM_RID(MethodImpl,Class,TypeDef) + SCHEMA_ITEM_CDTKN(MethodImpl,MethodBody,MethodDefOrRef) + SCHEMA_ITEM_CDTKN(MethodImpl, MethodDeclaration, MethodDefOrRef) + SCHEMA_TABLE_END(MethodImpl) + + //------------------------------------------------------------------------- + //ModuleRef + SCHEMA_TABLE_START(ModuleRef) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_STRING(ModuleRef, Name) + SCHEMA_TABLE_END(ModuleRef) + + //------------------------------------------------------------------------- + // TypeSpec + SCHEMA_TABLE_START(TypeSpec) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_BLOB(TypeSpec,Signature) + SCHEMA_TABLE_END(TypeSpec) + + //------------------------------------------------------------------------- + // ENCLog + SCHEMA_TABLE_START(ENCLog) + SCHEMA_ITEM(ENCLog, ULONG, Token) + SCHEMA_ITEM(ENCLog, ULONG, FuncCode) + SCHEMA_TABLE_END(ENCLog) + + //------------------------------------------------------------------------- + // ImplMap + SCHEMA_TABLE_START(ImplMap) + SCHEMA_ITEM(ImplMap, USHORT, MappingFlags) + SCHEMA_ITEM_CDTKN(ImplMap, MemberForwarded, MemberForwarded) + SCHEMA_ITEM_STRING(ImplMap, ImportName) + SCHEMA_ITEM_RID(ImplMap, ImportScope, ModuleRef) + SCHEMA_TABLE_END(ImplMap) + + //------------------------------------------------------------------------- + // ENCMap + SCHEMA_TABLE_START(ENCMap) + SCHEMA_ITEM(ENCMap, ULONG, Token) + SCHEMA_TABLE_END(ENCMap) + + //------------------------------------------------------------------------- + // FieldRVA + SCHEMA_TABLE_START(FieldRVA) + SCHEMA_ITEM(FieldRVA, ULONG, RVA) + SCHEMA_ITEM_RID(FieldRVA, Field, Field) + SCHEMA_TABLE_END(FieldRVA) + + //------------------------------------------------------------------------- + // Assembly + SCHEMA_TABLE_START(Assembly) + SCHEMA_ITEM(Assembly, ULONG, HashAlgId) + SCHEMA_ITEM(Assembly, USHORT, MajorVersion) + SCHEMA_ITEM(Assembly, USHORT, MinorVersion) + SCHEMA_ITEM(Assembly, USHORT, BuildNumber) + SCHEMA_ITEM(Assembly, USHORT, RevisionNumber) + SCHEMA_ITEM(Assembly, ULONG, Flags) + SCHEMA_ITEM_BLOB(Assembly, PublicKey) + SCHEMA_ITEM_STRING(Assembly, Name) + SCHEMA_ITEM_STRING(Assembly, Locale) + SCHEMA_TABLE_END(Assembly) + + //------------------------------------------------------------------------- + // AssemblyProcessor + SCHEMA_TABLE_START(AssemblyProcessor) + SCHEMA_ITEM(AssemblyProcessor, ULONG, Processor) + SCHEMA_TABLE_END(AssemblyProcessor) + + //------------------------------------------------------------------------- + // AssemblyOS + SCHEMA_TABLE_START(AssemblyOS) + SCHEMA_ITEM(AssemblyOS, ULONG, OSPlatformId) + SCHEMA_ITEM(AssemblyOS, ULONG, OSMajorVersion) + SCHEMA_ITEM(AssemblyOS, ULONG, OSMinorVersion) + SCHEMA_TABLE_END(AssemblyOS) + + //------------------------------------------------------------------------- + // AssemblyRef + SCHEMA_TABLE_START(AssemblyRef) + SCHEMA_ITEM(AssemblyRef, USHORT, MajorVersion) + SCHEMA_ITEM(AssemblyRef, USHORT, MinorVersion) + SCHEMA_ITEM(AssemblyRef, USHORT, BuildNumber) + SCHEMA_ITEM(AssemblyRef, USHORT, RevisionNumber) + SCHEMA_ITEM(AssemblyRef, ULONG, Flags) + SCHEMA_ITEM_BLOB(AssemblyRef, PublicKeyOrToken) + SCHEMA_ITEM_STRING(AssemblyRef, Name) + SCHEMA_ITEM_STRING(AssemblyRef, Locale) + SCHEMA_ITEM_BLOB(AssemblyRef, HashValue) + SCHEMA_TABLE_END(AssemblyRef) + + //------------------------------------------------------------------------- + // AssemblyRefProcessor + SCHEMA_TABLE_START(AssemblyRefProcessor) + SCHEMA_ITEM(AssemblyRefProcessor, ULONG, Processor) + SCHEMA_ITEM_RID(AssemblyRefProcessor, AssemblyRef, AssemblyRef) + SCHEMA_TABLE_END(AssemblyRefProcessor) + + //------------------------------------------------------------------------- + // AssemblyRefOS + SCHEMA_TABLE_START(AssemblyRefOS) + SCHEMA_ITEM(AssemblyRefOS, ULONG, OSPlatformId) + SCHEMA_ITEM(AssemblyRefOS, ULONG, OSMajorVersion) + SCHEMA_ITEM(AssemblyRefOS, ULONG, OSMinorVersion) + SCHEMA_ITEM_RID(AssemblyRefOS, AssemblyRef, AssemblyRef) + SCHEMA_TABLE_END(AssemblyRefOS) + + //------------------------------------------------------------------------- + // File + SCHEMA_TABLE_START(File) + SCHEMA_ITEM(File, ULONG, Flags) + SCHEMA_ITEM_STRING(File, Name) + SCHEMA_ITEM_BLOB(File, HashValue) + SCHEMA_TABLE_END(File) + + //------------------------------------------------------------------------- + // ExportedType + SCHEMA_TABLE_START(ExportedType) + SCHEMA_ITEM(ExportedType, ULONG, Flags) + SCHEMA_ITEM(ExportedType, ULONG, TypeDefId) + SCHEMA_ITEM_STRING(ExportedType, TypeName) + SCHEMA_ITEM_STRING(ExportedType, TypeNamespace) + SCHEMA_ITEM_CDTKN(ExportedType, Implementation, Implementation) + SCHEMA_TABLE_END(ExportedType) + + //------------------------------------------------------------------------- + // ManifestResource + SCHEMA_TABLE_START(ManifestResource) + SCHEMA_ITEM(ManifestResource, ULONG, Offset) + SCHEMA_ITEM(ManifestResource, ULONG, Flags) + SCHEMA_ITEM_STRING(ManifestResource, Name) + SCHEMA_ITEM_CDTKN(ManifestResource, Implementation, Implementation) + SCHEMA_TABLE_END(ManifestResource) + + //------------------------------------------------------------------------- + // NestedClass + SCHEMA_TABLE_START(NestedClass) + SCHEMA_ITEM_RID(NestedClass, NestedClass, TypeDef) + SCHEMA_ITEM_RID(NestedClass, EnclosingClass, TypeDef) + SCHEMA_TABLE_END(NestedClass) + + + //------------------------------------------------------------------------- + // GenericParam + SCHEMA_TABLE_START(GenericParam) + SCHEMA_ITEM(GenericParam, USHORT, Number) + SCHEMA_ITEM(GenericParam, USHORT, Flags) + SCHEMA_ITEM_CDTKN(GenericParam, Owner, TypeOrMethodDef) + SCHEMA_ITEM_STRING(GenericParam, Name) + SCHEMA_TABLE_END(GenericParam) + + //------------------------------------------------------------------------- + // Transitional table for Metadata v1.1 for GenericParam + SCHEMA_TABLE_START(GenericParamV1_1) + SCHEMA_ITEM(GenericParam, USHORT, Number) + SCHEMA_ITEM(GenericParam, USHORT, Flags) + SCHEMA_ITEM_CDTKN(GenericParam, Owner, TypeOrMethodDef) + SCHEMA_ITEM_STRING(GenericParam, Name) + SCHEMA_ITEM_CDTKN(GenericParam, Kind, TypeDefOrRef) + SCHEMA_TABLE_END(GenericParam) + + + + //------------------------------------------------------------------------- + //MethodSpec + SCHEMA_TABLE_START(MethodSpec) + SCHEMA_ITEM_NOFIXED() + SCHEMA_ITEM_CDTKN(MethodSpec, Method, MethodDefOrRef) + SCHEMA_ITEM_BLOB(MethodSpec, Instantiation) + SCHEMA_TABLE_END(MethodSpec) + + //------------------------------------------------------------------------- + // GenericParamConstraint + SCHEMA_TABLE_START(GenericParamConstraint) + SCHEMA_ITEM_RID(GenericParamConstraint, Owner, GenericParam) + SCHEMA_ITEM_CDTKN(GenericParamConstraint, Constraint, TypeDefOrRef) + SCHEMA_TABLE_END(GenericParamConstraint) + +#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB + //------------------------------------------------------------------------- + //Document + SCHEMA_TABLE_START(Document) + SCHEMA_ITEM_BLOB(Document, Name) + SCHEMA_ITEM_GUID(Document, HashAlgorithm) + SCHEMA_ITEM_BLOB(Document, Hash) + SCHEMA_ITEM_GUID(Document, Language) + SCHEMA_TABLE_END(Document) + + //------------------------------------------------------------------------- + //MethodDebugInformation + SCHEMA_TABLE_START(MethodDebugInformation) + SCHEMA_ITEM_RID(MethodDebugInformation, Document, Document) + SCHEMA_ITEM_BLOB(MethodDebugInformation, SequencePoints) + SCHEMA_TABLE_END(MethodDebugInformation) + + //------------------------------------------------------------------------- + //LocalScope + SCHEMA_TABLE_START(LocalScope) + SCHEMA_ITEM_RID(LocalScope, Method, Method) + SCHEMA_ITEM_RID(LocalScope, ImportScope, ImportScope) + SCHEMA_ITEM_RID(LocalScope, VariableList, LocalVariable) + SCHEMA_ITEM_RID(LocalScope, ConstantList, LocalConstant) + SCHEMA_ITEM(LocalScope, ULONG, StartOffset) + SCHEMA_ITEM(LocalScope, ULONG, Length) + SCHEMA_TABLE_END(LocalScope) + + //------------------------------------------------------------------------- + //LocalVariable + SCHEMA_TABLE_START(LocalVariable) + SCHEMA_ITEM(LocalVariable, USHORT, Attributes) + SCHEMA_ITEM(LocalVariable, USHORT, Index) + SCHEMA_ITEM_STRING(LocalVariable, Name) + SCHEMA_TABLE_END(LocalVariable) + + //------------------------------------------------------------------------- + //LocalConstant + SCHEMA_TABLE_START(LocalConstant) + SCHEMA_ITEM_STRING(LocalConstant, Name) + SCHEMA_ITEM_BLOB(LocalConstant, Signature) + SCHEMA_TABLE_END(LocalConstant) + + //------------------------------------------------------------------------- + //ImportScope + SCHEMA_TABLE_START(ImportScope) + SCHEMA_ITEM_RID(ImportScope, Parent, ImportScope) + SCHEMA_ITEM_BLOB(ImportScope, Imports) + SCHEMA_TABLE_END(ImportScope) + + // TODO: + // StateMachineMethod + // CustomDebugInformation +#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB +// eof ------------------------------------------------------------------------ diff --git a/src/coreclr/md/runtime/metamodelro.cpp b/src/coreclr/md/runtime/metamodelro.cpp new file mode 100644 index 00000000000..55ce8430a34 --- /dev/null +++ b/src/coreclr/md/runtime/metamodelro.cpp @@ -0,0 +1,443 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// MetaModelRO.cpp -- Read-only implementation of compressed COM+ metadata. +// + +// +//***************************************************************************** +#include "stdafx.h" + +#include "metamodelro.h" +#include <posterror.h> +#include <corerror.h> +#include "metadatatracker.h" + +//***************************************************************************** +// Set the pointers to consecutive areas of a large buffer. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMd::InitializeTables( + MetaData::DataBlob tablesData) +{ + HRESULT hr; + + for (int i = 0; i < TBL_COUNT; i++) + { + // This table data + MetaData::DataBlob tableData; + + S_UINT32 cbTableSize = + S_UINT32(m_TableDefs[i].m_cbRec) * + S_UINT32(m_Schema.m_cRecs[i]); + if (cbTableSize.IsOverflow()) + { + Debug_ReportError("Table is too large - size overflow."); + return CLDB_E_FILE_CORRUPT; + } + if (!tablesData.GetDataOfSize(cbTableSize.Value(), &tableData)) + { + Debug_ReportError("Table is not within MetaData tables block."); + return CLDB_E_FILE_CORRUPT; + } + _ASSERTE(cbTableSize.Value() == tableData.GetSize()); + + METADATATRACKER_ONLY(MetaDataTracker::NoteSection( + i, + tableData.GetDataPointer(), + tableData.GetSize(), + m_TableDefs[i].m_cbRec)); + + IfFailRet(m_Tables[i].Initialize( + m_TableDefs[i].m_cbRec, + tableData, + FALSE)); // fCopyData + } + + return S_OK; +} // CMiniMd::SetTablePointers + +//***************************************************************************** +// Given a buffer that contains a MiniMd, init to read it. +//***************************************************************************** +HRESULT +CMiniMd::InitOnMem( + void *pvBuf, // The buffer. + ULONG ulBufLen) // Size of the buffer.. +{ + HRESULT hr = S_OK; + ULONG cbData; + BYTE *pBuf = reinterpret_cast<BYTE*>(pvBuf); + + // Uncompress the schema from the buffer into our structures. + IfFailGo(SchemaPopulate(pvBuf, ulBufLen, &cbData)); + PREFAST_ASSUME(cbData <= ulBufLen); + + // There shouldn't be any pointer tables. + if ((m_Schema.m_cRecs[TBL_MethodPtr] != 0) || (m_Schema.m_cRecs[TBL_FieldPtr] != 0)) + { + Debug_ReportError("MethodPtr and FieldPtr tables are not allowed in Read-Only format."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // initialize the pointers to the rest of the data. + IfFailGo(InitializeTables(MetaData::DataBlob(pBuf + Align4(cbData), ulBufLen-cbData))); + +ErrExit: + return hr; +} // CMiniMd::InitOnMem + +//***************************************************************************** +// Validate cross-stream consistency. +//***************************************************************************** +HRESULT +CMiniMd::PostInit( + int iLevel) +{ + return S_OK; +} // CMiniMd::PostInit + +//***************************************************************************** +// converting a ANSI heap string to unicode string to an output buffer +//***************************************************************************** +HRESULT +CMiniMd::Impl_GetStringW( + ULONG ix, + __inout_ecount (cchBuffer) LPWSTR szOut, + ULONG cchBuffer, + ULONG *pcchBuffer) +{ + LPCSTR szString; // Single byte version. + int iSize; // Size of resulting string, in wide chars. + HRESULT hr = NOERROR; + + IfFailGo(getString(ix, &szString)); + + if (*szString == 0) + { + // If emtpy string "", return pccBuffer 0 + if ((szOut != NULL) && (cchBuffer != 0)) + szOut[0] = W('\0'); + if (pcchBuffer != NULL) + *pcchBuffer = 0; + goto ErrExit; + } + iSize = ::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, szOut, cchBuffer); + if (iSize == 0) + { + // What was the problem? + DWORD dwNT = GetLastError(); + + // Not truncation? + if (dwNT != ERROR_INSUFFICIENT_BUFFER) + IfFailGo(HRESULT_FROM_NT(dwNT)); + + // Truncation error; get the size required. + if (pcchBuffer != NULL) + *pcchBuffer = ::WszMultiByteToWideChar(CP_UTF8, 0, szString, -1, NULL, 0); + + if ((szOut != NULL) && (cchBuffer > 0)) + { // null-terminate the truncated output string + szOut[cchBuffer - 1] = W('\0'); + } + + hr = CLDB_S_TRUNCATION; + goto ErrExit; + } + if (pcchBuffer != NULL) + *pcchBuffer = iSize; + +ErrExit: + return hr; +} // CMiniMd::Impl_GetStringW + + +//***************************************************************************** +// Given a table with a pointer (index) to a sequence of rows in another +// table, get the RID of the end row. This is the STL-ish end; the first row +// not in the list. Thus, for a list of 0 elements, the start and end will +// be the same. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMd::Impl_GetEndRidForColumn( // The End rid. + UINT32 nTableIndex, + RID nRowIndex, + CMiniColDef &def, // Column containing the RID into other table. + UINT32 nTargetTableIndex, // The other table. + RID *pEndRid) +{ + HRESULT hr; + _ASSERTE(nTableIndex < TBL_COUNT); + RID nLastRowIndex = m_Schema.m_cRecs[nTableIndex]; + + // Last rid in range from NEXT record, or count of table, if last record. + if (nRowIndex < nLastRowIndex) + { + BYTE *pRow; + IfFailRet(Impl_GetRow(nTableIndex, nRowIndex + 1, &pRow)); + *pEndRid = getIX(pRow, def); + } + else // Convert count to 1-based rid. + { + if (nRowIndex != nLastRowIndex) + { + Debug_ReportError("Invalid table row index."); + IfFailRet(METADATA_E_INDEX_NOTFOUND); + } + _ASSERTE(nTargetTableIndex < TBL_COUNT); + *pEndRid = m_Schema.m_cRecs[nTargetTableIndex] + 1; + } + + return S_OK; +} // CMiniMd::Impl_GetEndRidForColumn + + +//***************************************************************************** +// return all found CAs in an enumerator +//***************************************************************************** +HRESULT +CMiniMd::CommonEnumCustomAttributeByName( + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + bool fStopAtFirstFind, // [IN] just find the first one + HENUMInternal *phEnum) // enumerator to fill up +{ + HRESULT hr = S_OK; + HRESULT hrRet = S_FALSE; // Assume that we won't find any + ULONG ridStart, ridEnd; // Loop start and endpoints. + + _ASSERTE(phEnum != NULL); + + HENUMInternal::ZeroEnum(phEnum); + + HENUMInternal::InitDynamicArrayEnum(phEnum); + + phEnum->m_tkKind = mdtCustomAttribute; + + // Get the list of custom values for the parent object. + + IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart)); + if (ridStart == 0) + return S_FALSE; + + // Look for one with the given name. + for (; ridStart < ridEnd; ++ridStart) + { + IfFailGoto(CompareCustomAttribute( tkObj, szName, ridStart), ErrExit); + if (hr == S_OK) + { + // If here, found a match. + hrRet = S_OK; + IfFailGo(HENUMInternal::AddElementToEnum( + phEnum, + TokenFromRid(ridStart, mdtCustomAttribute))); + if (fStopAtFirstFind) + goto ErrExit; + } + } + +ErrExit: + if (FAILED(hr)) + return hr; + return hrRet; +} // CMiniMd::CommonEnumCustomAttributeByName + + +//***************************************************************************** +// Search a table for the row containing the given key value. +// EG. Constant table has pointer back to Param or Field. +// +//***************************************************************************** +__checkReturn +HRESULT +CMiniMd::vSearchTable( + ULONG ixTbl, // Table to search. + CMiniColDef sColumn, // Sorted key column, containing search value. + ULONG ulTarget, // Target for search. + RID *pRid) // RID of matching row, or 0. +{ + HRESULT hr; + void *pRow = NULL; // Row from a table. + ULONG val; // Value from a row. + int lo, mid, hi; // binary search indices. + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // If you change the rows touched while searching, please update + // CMiniMdRW::GetHotMetadataTokensSearchAware + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + // Start with entire table. + lo = 1; + hi = GetCountRecs(ixTbl); + // While there are rows in the range... + while (lo <= hi) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + IfFailRet(getRow(ixTbl, mid, &pRow)); + val = getIX_NoLogging(pRow, sColumn); + // If equal to the target, done. + if (val == ulTarget) + { + *pRid = mid; + METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow)); + return S_OK; + } + // If middle item is too small, search the top half. + if (val < ulTarget) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + // Didn't find anything that matched. + *pRid = 0; + + METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow)); + return S_OK; +} // CMiniMd::vSearchTable + +//***************************************************************************** +// Search a table for the highest-RID row containing a value that is less than +// or equal to the target value. EG. TypeDef points to first Field, but if +// a TypeDef has no fields, it points to first field of next TypeDef. +//***************************************************************************** +__checkReturn +HRESULT +CMiniMd::vSearchTableNotGreater( + ULONG ixTbl, // Table to search. + CMiniColDef sColumn, // the column def containing search value + ULONG ulTarget, // target for search + RID *pRid) // RID of matching row, or 0 +{ + HRESULT hr; + void *pRow = NULL; // Row from a table. + ULONG cRecs; // Rows in the table. + ULONG val = 0; // Value from a table. + ULONG lo, mid = 0, hi; // binary search indices. + + cRecs = GetCountRecs(ixTbl); + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // If you change the rows touched while searching, please update + // CMiniMdRW::GetHotMetadataTokensSearchAware + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + // Start with entire table. + lo = 1; + hi = cRecs; + // If no recs, return. + if (lo > hi) + { + *pRid = 0; + return S_OK; + } + // While there are rows in the range... + while (lo <= hi) + { // Look at the one in the middle. + mid = (lo + hi) / 2; + IfFailRet(getRow(ixTbl, mid, &pRow)); + val = getIX_NoLogging(pRow, sColumn); + // If equal to the target, done searching. + if (val == ulTarget) + break; + // If middle item is too small, search the top half. + if (val < ulTarget) + lo = mid + 1; + else // but if middle is to big, search bottom half. + hi = mid - 1; + } + + METADATATRACKER_ONLY(MetaDataTracker::NoteSearch(pRow)); + + // May or may not have found anything that matched. Mid will be close, but may + // be to high or too low. It should point to the highest acceptable + // record. + + // If the value is greater than the target, back up just until the value is + // less than or equal to the target. SHOULD only be one step. + if (val > ulTarget) + { + while (val > ulTarget) + { + // If there is nothing else to look at, we won't find it. + if (--mid < 1) + break; + IfFailRet(getRow(ixTbl, mid, &pRow)); + val = getIX(pRow, sColumn); + } + } + else + { + // Value is less than or equal to the target. As long as the next + // record is also acceptable, move forward. + while (mid < cRecs) + { + // There is another record. Get its value. + IfFailRet(getRow(ixTbl, mid+1, &pRow)); + val = getIX(pRow, sColumn); + // If that record is too high, stop. + if (val > ulTarget) + break; + mid++; + } + } + + // Return the value that's just less than the target. + *pRid = mid; + return S_OK; +} // CMiniMd::vSearchTableNotGreater + +//***************************************************************************** +// return just the blob value of the first found CA matching the query. +// returns S_FALSE if there is no match +//***************************************************************************** +HRESULT +CMiniMd::CommonGetCustomAttributeByNameEx( + mdToken tkObj, // [IN] Object with Custom Attribute. + LPCUTF8 szName, // [IN] Name of desired Custom Attribute. + mdCustomAttribute *ptkCA, // [OUT] put custom attribute token here + const void **ppData, // [OUT] Put pointer to data here. + ULONG *pcbData) // [OUT] Put size of data here. +{ + HRESULT hr; + + ULONG cbData; + CustomAttributeRec *pRec; + + ULONG ridStart, ridEnd; // Loop start and endpoints. + + // Get the list of custom values for the parent object. + + IfFailGo(getCustomAttributeForToken(tkObj, &ridEnd, &ridStart)); + + hr = S_FALSE; + if (ridStart == 0) + { + goto ErrExit; + } + + // Look for one with the given name. + for (; ridStart < ridEnd; ++ridStart) + { + IfFailGoto(CompareCustomAttribute( tkObj, szName, ridStart), ErrExit); + if (hr == S_OK) + { + if (ppData != NULL) + { + // now get the record out. + if (pcbData == NULL) + pcbData = &cbData; + + IfFailGo(GetCustomAttributeRecord(ridStart, &pRec)); + IfFailGo(getValueOfCustomAttribute(pRec, reinterpret_cast<const BYTE **>(ppData), pcbData)); + if (ptkCA) + *ptkCA = TokenFromRid(mdtCustomAttribute, ridStart); + } + break; + } + } + +ErrExit: + return hr; +} // CMiniMd::CommonGetCustomAttributeByName diff --git a/src/coreclr/md/runtime/recordpool.cpp b/src/coreclr/md/runtime/recordpool.cpp new file mode 100644 index 00000000000..73680757ed4 --- /dev/null +++ b/src/coreclr/md/runtime/recordpool.cpp @@ -0,0 +1,387 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// RecordPool.cpp -- Implementation of record heaps. +// + +// +//***************************************************************************** +#include "stdafx.h" +#include <recordpool.h> + +#define RECORDPOOL_GROW_FACTOR 8 +#define RECORDPOOL_GROW_MAX 2048 +#define RECORDPOOL_GROW_MINROWS 2 +#define RECORDPOOL_GROW_DEFAULTROWS 16 + +HRESULT +RecordPool::InitNew( + UINT32 cbRec, // Record size. + UINT32 cRecsInit) // Initial guess of count of record. +{ + HRESULT hr; + S_UINT32 cbGrow; // Initial grow size of the pool. + + // Size of each record is fixed. + m_cbRec = cbRec; + + if (cRecsInit > 0) + { + cbGrow = S_UINT32(cbRec) * S_UINT32(cRecsInit); + } + else + { + cbGrow = S_UINT32(cbRec) * S_UINT32(RECORDPOOL_GROW_DEFAULTROWS); + } + if (cbGrow.IsOverflow()) + { + Debug_ReportInternalError("Growing record pool overflowed."); + return CLDB_E_INTERNALERROR; + } + + m_ulGrowInc = cbGrow.Value(); + + IfFailRet(StgPool::InitNew()); + + // If there is an initial size for the record pool, grow to that now. + if (cRecsInit > 0) + { + if (!Grow(cbGrow.Value())) + return E_OUTOFMEMORY; + } + + return S_OK; +} // RecordPool::InitNew + +//***************************************************************************** +// Load a Record heap from persisted memory. If a copy of the data is made +// (so that it may be updated), then a new hash table is generated which can +// be used to elminate duplicates with new Records. +//***************************************************************************** +HRESULT +RecordPool::InitOnMem( + ULONG cbRec, // Record size. + void *pData, // Predefined data. + ULONG iSize, // Size of data. + BOOL fReadOnly) // true if append is forbidden. +{ + HRESULT hr; + m_cbRec = cbRec; + + // Let base class init our memory structure. + IfFailRet(StgPool::InitOnMem(pData, iSize, fReadOnly)); + + // For init on existing mem case. + if ((pData != NULL) && (iSize != 0)) + { + // If we are doing an update in place don't make a copy + // If we cannot update, then we don't need a hash table. + if (fReadOnly) + return S_OK; + + // Other wise copy the memory to do the update + IfFailRet(TakeOwnershipOfInitMem()); + } + + return S_OK; +} // RecordPool::InitOnMem + +//***************************************************************************** +// Allocate memory if we don't have any, or grow what we have. If successful, +// then at least iRequired bytes will be allocated. +//***************************************************************************** +bool RecordPool::Grow( // true if successful. + ULONG iRequired) // Min required bytes to allocate. +{ + // Allocate the memory. + if (!StgPool::Grow(iRequired)) + return false; + + // Zero the new memory. + memset(GetNextLocation(), 0, GetCbSegAvailable()); + + return true; +} // bool RecordProol::Grow() + +//***************************************************************************** +// The Record will be added to the pool. The index of the Record in the pool +// is returned in *piIndex. If the Record is already in the pool, then the +// index will be to the existing copy of the Record. +//***************************************************************************** +HRESULT +RecordPool::AddRecord( + BYTE **ppRecord, + UINT32 *pnIndex) // Return 1-based index of Record here. +{ + _ASSERTE(pnIndex != NULL); + + // Space on heap for new Record? + if (m_cbRec > GetCbSegAvailable()) + { + if (!Grow(m_cbRec)) + { + *ppRecord = NULL; + return E_OUTOFMEMORY; + } + } + + // Records should be aligned on record boundaries. + _ASSERTE((GetNextOffset() % m_cbRec) == 0); + + // Copy the Record to the heap. + *ppRecord = GetNextLocation(); + + // Give the 1-based index back to caller. + *pnIndex = (GetNextOffset() / m_cbRec) + 1; + + // Update heap counters. + SegAllocate(m_cbRec); + + return S_OK; +} // RecordPool::AddRecord + +//***************************************************************************** +// Insert a Record into the pool. The index of the Record before which to +// insert is specified. Shifts all records down. Return a pointer to the +// new record. +//***************************************************************************** +HRESULT +RecordPool::InsertRecord( + UINT32 nIndex, // [IN] Insert record before this. + BYTE **ppRecord) +{ + HRESULT hr; + StgPoolSeg *pCurSeg; // Current segment. + StgPoolSeg *pPrevSeg; // Previous segment. + BYTE *pSegEnd; // Last record in a segment. + BYTE *pFrom; // A copy/move source. + ULONG cbMove; // Bytes to move. + BYTE *pNew; // New record. + + // Notice the case of appending. + if (nIndex == (Count() + 1)) + { + UINT32 nNewIndex_Ignore; + return AddRecord(ppRecord, &nNewIndex_Ignore); + } + + // If past end or before beginning, invalid. + if ((nIndex > Count()) || (nIndex == 0)) + { + Debug_ReportError("Invalid index passed for inserting record."); + return CLDB_E_INDEX_NOTFOUND; + } + + // This code works by allocating a new record at the end. + // The last record is moved to the new end record. + // Working backwards through the chained segments, + // shift the segment by one record, so the empty record + // is at the start of the segment instead of the end. + // copy the last record of the previous segment to the + // newly emptied first record of the current segment. + // When the segment containing the insert point is finally + // reached, its last record is empty (from above loop), so + // shift from the insertion point to the end-1 by one record. + + // Current last record. + pCurSeg = m_pCurSeg; + IfFailRet(GetRecord(Count(), &pSegEnd)); + _ASSERTE(hr == S_OK); + + // Add an empty record to the end of the heap. + { + UINT32 nLastRecordIndex_Ignore; + IfFailRet(AddRecord(&pNew, &nLastRecordIndex_Ignore)); + } + + // Copy the current last record to the new record. + memcpy(pNew, pSegEnd, m_cbRec); + + // While the insert location is prior to the current segment, + while (nIndex < GetIndexForRecord(pCurSeg->m_pSegData)) + { + // Shift the segment up by one record. + cbMove = (ULONG)(pSegEnd - pCurSeg->m_pSegData); + memmove(pCurSeg->m_pSegData + m_cbRec, pCurSeg->m_pSegData, cbMove); + + // Find the previous segment. + pPrevSeg = this; + while (pPrevSeg->m_pNextSeg != pCurSeg) + { + pPrevSeg = pPrevSeg->m_pNextSeg; + } + + // Copy the final record of the previous segment to the start of this one. + pSegEnd = pPrevSeg->m_pSegData+pPrevSeg->m_cbSegNext-m_cbRec; + memcpy(pCurSeg->m_pSegData, pSegEnd, m_cbRec); + + // Make the previous segment the current segment. + pCurSeg = pPrevSeg; + } + + // Shift at the insert location, forward by one. + IfFailRet(GetRecord(nIndex, &pFrom)); + _ASSERTE(hr == S_OK); + + cbMove = (ULONG)(pSegEnd - pFrom); + memmove(pFrom + m_cbRec, pFrom, cbMove); + + *ppRecord = pFrom; + + return S_OK; +} // RecordPool::InsertRecord + +//***************************************************************************** +// Return a pointer to a Record given an index previously handed out by +// AddRecord or FindRecord. +//***************************************************************************** +HRESULT +RecordPool::GetRecord( + UINT32 nIndex, // 1-based index of Record in pool. + BYTE **ppRecord) +{ + MetaData::DataBlob record; + + if (nIndex == 0) + { + Debug_ReportError("Invalid index 0 passed."); + *ppRecord = NULL; + return CLDB_E_INDEX_NOTFOUND; + } + + // Convert to 0-based internal form, defer to implementation. + HRESULT hr = GetData((nIndex - 1) * m_cbRec, &record); + if (FAILED(hr)) + { + *ppRecord = NULL; + return hr; + } + _ASSERTE(record.ContainsData(m_cbRec)); + *ppRecord = record.GetDataPointer(); + + return hr; +} // RecordPool::GetRecord + +//***************************************************************************** +// Return the first record in a pool, and set up a context for fast +// iterating through the pool. Note that this scheme does pretty minimal +// error checking. +//***************************************************************************** +void *RecordPool::GetFirstRecord( // Pointer to Record in pool. + void **pContext) // Store context here. +{ + StgPoolSeg **ppSeg = reinterpret_cast<StgPoolSeg**>(pContext); + + *ppSeg = static_cast<StgPoolSeg*>(this); + return (*ppSeg)->m_pSegData; +} // void *RecordPool::GetFirstRecord() + +//***************************************************************************** +// Given a pointer to a record, return a pointer to the next record. +// Note that this scheme does pretty minimal error checking. In particular, +// this will let the caller walk off of the end of valid data in the last +// segment. +//***************************************************************************** +void *RecordPool::GetNextRecord( // Pointer to Record in pool. + void *pRecord, // Current record. + void **pContext) // Stored context here. +{ + BYTE *pbRec = reinterpret_cast<BYTE*>(pRecord); + StgPoolSeg **ppSeg = reinterpret_cast<StgPoolSeg**>(pContext); + + // Get the next record. + pbRec += m_cbRec; + + // Is the next record outside of the current segment? + if (static_cast<ULONG>(pbRec - (*ppSeg)->m_pSegData) >= (*ppSeg)->m_cbSegSize) + { + // Better be exactly one past current segment. + _ASSERTE(static_cast<ULONG>(pbRec - (*ppSeg)->m_pSegData) == (*ppSeg)->m_cbSegSize); + // Switch the context pointer. + *ppSeg = (*ppSeg)->m_pNextSeg; + // Next record is start of next segment. + if (*ppSeg) + return (*ppSeg)->m_pSegData; + else + return 0; + } + + return pbRec; +} // void *RecordPool::GetNextRecord() + +//***************************************************************************** +// Given a pointer to a record, determine the index corresponding to the +// record. +//***************************************************************************** +ULONG RecordPool::GetIndexForRecord( // 1-based index of Record in pool. + const void *pvRecord) // Pointer to Record in pool. +{ + ULONG iPrev = 0; // cumulative index of previous segments. + const StgPoolSeg *pSeg = this; + const BYTE *pRecord = reinterpret_cast<const BYTE*>(pvRecord); + const BYTE *pSegData = NULL; + ULONG ulSegSize; + for (;;) + { // Does the current segment contain the record? + pSegData = pSeg->GetSegData(); + ulSegSize = pSeg->GetSegSize(); + if (pRecord >= pSegData && pRecord < pSegData + ulSegSize) + { // The pointer should be to the start of a record. + _ASSERTE(((pRecord - pSegData) % m_cbRec) == 0); + return (ULONG)(1 + iPrev + (pRecord - pSegData) / m_cbRec); + } + _ASSERTE((ulSegSize % m_cbRec) == 0); + iPrev += ulSegSize / m_cbRec; + pSeg = pSeg->GetNextSeg(); + // If out of data, didn't find the record. + if (pSeg == 0) + return 0; + } +} // ULONG RecordPool::GetIndexForRecord() + +//***************************************************************************** +// Given a purported pointer to a record, determine if the pointer is valid. +//***************************************************************************** +int RecordPool::IsValidPointerForRecord(// true or false. + const void *pvRecord) // Pointer to Record in pool. +{ + const StgPoolSeg *pSeg; + const BYTE *pRecord = reinterpret_cast<const BYTE*>(pvRecord); + const BYTE *pSegData = NULL; + for (pSeg = this; (pSeg); pSeg = pSeg->GetNextSeg()) + { // Does the current segment contain the record? + pSegData = pSeg->GetSegData(); + if ((pRecord >= pSegData) && (pRecord < pSegData + pSeg->GetSegSize())) + { // The pointer should be to the start of a record. + return (((pRecord - pSegData) % m_cbRec) == 0); + } + _ASSERTE((pSeg->GetSegSize() % m_cbRec) == 0); + } + return 0; +} // int RecordPool::IsValidPointerForRecord() + +//***************************************************************************** +// Replace the contents of this pool with those from another pool. The other +// pool loses ownership of the memory. +//***************************************************************************** +HRESULT RecordPool::ReplaceContents( + RecordPool *pOther) // The other record pool. +{ + // Release any memory currently held. + Uninit(); + + // Grab the new data. + *this = *pOther; + + // If the other pool's curseg pointed to itself, make this pool point to itself. + if (pOther->m_pCurSeg == pOther) + m_pCurSeg = this; + + // Fix the other pool so it won't free the memory that this one + // just hijacked. + pOther->m_pSegData = (BYTE*)m_zeros; + pOther->m_pNextSeg = 0; + pOther->Uninit(); + + return S_OK; +} // HRESULT RecordPool::ReplaceContents() diff --git a/src/coreclr/md/runtime/stdafx.h b/src/coreclr/md/runtime/stdafx.h new file mode 100644 index 00000000000..7a196c6dd69 --- /dev/null +++ b/src/coreclr/md/runtime/stdafx.h @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// stdafx.h +// + +// +// Precompiled headers. +// +//***************************************************************************** +#ifndef __STDAFX_H_ +#define __STDAFX_H_ + +#include <crtwrap.h> +#include <winwrap.h> +#include <utilcode.h> + +#include <cor.h> +#include <corpriv.h> + +#include "../hotdata/hotheap.h" +#include <metamodelro.h> +#include <liteweightstgdb.h> + +#include "mdcommon.h" + +#endif // __STDAFX_H_ diff --git a/src/coreclr/md/runtime/strongnameinternal.cpp b/src/coreclr/md/runtime/strongnameinternal.cpp new file mode 100644 index 00000000000..a9e559b1054 --- /dev/null +++ b/src/coreclr/md/runtime/strongnameinternal.cpp @@ -0,0 +1,333 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Strong name APIs which are not exposed publicly but are used by CLR code +// + +#include "stdafx.h" +#include "strongnameinternal.h" +#include "thekey.h" +#include "ecmakey.h" +#include "sha1.h" + +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the ECMA public key blob +// +// Arguments: +// pbKey - public key blob to check +// cbKey - size in bytes of pbKey +// + +bool StrongNameIsEcmaKey(__in_ecount(cbKey) const BYTE *pbKey, DWORD cbKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The key should be the same size as the ECMA key + if (cbKey != sizeof(g_rbNeutralPublicKey)) + { + return false; + } + + const PublicKeyBlob *pKeyBlob = reinterpret_cast<const PublicKeyBlob *>(pbKey); + return StrongNameIsEcmaKey(*pKeyBlob); +} + +//--------------------------------------------------------------------------------------- +// +// Check to see if a public key blob is the ECMA public key blob +// +// Arguments: +// keyPublicKey - Key to check to see if it matches the ECMA key +// + +bool StrongNameIsEcmaKey(const PublicKeyBlob &keyPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return StrongNameSizeOfPublicKey(keyPublicKey) == sizeof(g_rbNeutralPublicKey) && + memcmp(reinterpret_cast<const BYTE *>(&keyPublicKey), g_rbNeutralPublicKey, sizeof(g_rbNeutralPublicKey)) == 0; +} + +//--------------------------------------------------------------------------------------- +// +// Verify that a public key blob looks like a reasonable public key +// +// Arguments: +// pbBuffer - buffer to verify the format of +// cbBuffer - size of pbBuffer +// + +bool StrongNameIsValidPublicKey(__in_ecount(cbBuffer) const BYTE *pbBuffer, DWORD cbBuffer) +{ + CONTRACTL + { + PRECONDITION(CheckPointer(pbBuffer)); + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The buffer must be at least as large as the public key structure + if (cbBuffer < sizeof(PublicKeyBlob)) + { + return false; + } + + // The buffer must be the same size as the structure header plus the trailing key data + const PublicKeyBlob *pkeyPublicKey = reinterpret_cast<const PublicKeyBlob *>(pbBuffer); + if (GET_UNALIGNED_VAL32(&pkeyPublicKey->cbPublicKey) != cbBuffer - offsetof(PublicKeyBlob, PublicKey)) + { + return false; + } + + // The buffer itself looks reasonable, but the public key structure needs to be validated as well + return StrongNameIsValidPublicKey(*pkeyPublicKey); +} + +//--------------------------------------------------------------------------------------- +// +// Verify that a public key blob looks like a reasonable public key. +// +// Arguments: +// keyPublicKey - key blob to verify +// +// Notes: +// This can be a very expensive operation, since it involves importing keys. +// + +bool StrongNameIsValidPublicKey(const PublicKeyBlob &keyPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The ECMA key doesn't look like a valid key so it will fail the below checks. If we were passed that + // key, then we can skip them + if (StrongNameIsEcmaKey(keyPublicKey)) + { + return true; + } + + // If a hash algorithm is specified, it must be a sensible value + bool fHashAlgorithmValid = GET_ALG_CLASS(GET_UNALIGNED_VAL32(&keyPublicKey.HashAlgID)) == ALG_CLASS_HASH && + GET_ALG_SID(GET_UNALIGNED_VAL32(&keyPublicKey.HashAlgID)) >= ALG_SID_SHA1; + if (keyPublicKey.HashAlgID != 0 && !fHashAlgorithmValid) + { + return false; + } + + // If a signature algorithm is specified, it must be a sensible value + bool fSignatureAlgorithmValid = GET_ALG_CLASS(GET_UNALIGNED_VAL32(&keyPublicKey.SigAlgID)) == ALG_CLASS_SIGNATURE; + if (keyPublicKey.SigAlgID != 0 && !fSignatureAlgorithmValid) + { + return false; + } + + // The key blob must indicate that it is a PUBLICKEYBLOB + if (keyPublicKey.PublicKey[0] != PUBLICKEYBLOB) + { + return false; + } + + return true; +} + + +//--------------------------------------------------------------------------------------- +// +// Determine the number of bytes that a public key blob occupies, including the key portion +// +// Arguments: +// keyPublicKey - key blob to calculate the size of +// + +DWORD StrongNameSizeOfPublicKey(const PublicKeyBlob &keyPublicKey) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return offsetof(PublicKeyBlob, PublicKey) + // Size of the blob header plus + GET_UNALIGNED_VAL32(&keyPublicKey.cbPublicKey); // the number of bytes in the key +} + + + + +// Size in bytes of strong name token. +#define SN_SIZEOF_TOKEN 8 + +// Determine the size of a PublicKeyBlob structure given the size of the key +// portion. +#define SN_SIZEOF_KEY(_pKeyBlob) (offsetof(PublicKeyBlob, PublicKey) + GET_UNALIGNED_VAL32(&(_pKeyBlob)->cbPublicKey)) + +// We allow a special abbreviated form of the Microsoft public key (16 bytes +// long: 0 for both alg ids, 4 for key length and 4 bytes of 0 for the key +// itself). This allows us to build references to system libraries that are +// platform neutral (so a 3rd party can build mscorlib replacements). The +// special zero PK is just shorthand for the local runtime's real system PK, +// which is always used to perform the signature verification, so no security +// hole is opened by this. Therefore we need to store a copy of the real PK (for +// this platform) here. + +// the actual definition of the microsoft key is in separate file to allow custom keys +#include "thekey.h" + + +#define SN_THE_KEY() ((PublicKeyBlob*)g_rbTheKey) +#define SN_SIZEOF_THE_KEY() sizeof(g_rbTheKey) + +#define SN_THE_KEYTOKEN() ((PublicKeyBlob*)g_rbTheKeyToken) + +// Determine if the given public key blob is the neutral key. +#define SN_IS_NEUTRAL_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbNeutralPublicKey) && \ + memcmp((_pk), g_rbNeutralPublicKey, sizeof(g_rbNeutralPublicKey)) == 0) + +#define SN_IS_THE_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheKey) && \ + memcmp((_pk), g_rbTheKey, sizeof(g_rbTheKey)) == 0) + + +// Silverlight platform key +#define SN_THE_SILVERLIGHT_PLATFORM_KEYTOKEN() ((PublicKeyBlob*)g_rbTheSilverlightPlatformKeyToken) +#define SN_IS_THE_SILVERLIGHT_PLATFORM_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheSilverlightPlatformKey) && \ + memcmp((_pk), g_rbTheSilverlightPlatformKey, sizeof(g_rbTheSilverlightPlatformKey)) == 0) + +// Silverlight key +#define SN_IS_THE_SILVERLIGHT_KEY(_pk) (SN_SIZEOF_KEY((PublicKeyBlob*)(_pk)) == sizeof(g_rbTheSilverlightKey) && \ + memcmp((_pk), g_rbTheSilverlightKey, sizeof(g_rbTheSilverlightKey)) == 0) + +#define SN_THE_SILVERLIGHT_KEYTOKEN() ((PublicKeyBlob*)g_rbTheSilverlightKeyToken) + + +// Free buffer allocated by routines below. +VOID StrongNameFreeBuffer(BYTE *pbMemory) // [in] address of memory to free +{ + if (pbMemory != (BYTE*)SN_THE_KEY() && pbMemory != g_rbNeutralPublicKey) + delete [] pbMemory; +} + + +// Create a strong name token from a public key blob. +HRESULT StrongNameTokenFromPublicKey(BYTE *pbPublicKeyBlob, // [in] public key blob + ULONG cbPublicKeyBlob, + BYTE **ppbStrongNameToken, // [out] strong name token + ULONG *pcbStrongNameToken) +{ + HRESULT hr = S_OK; + +#ifndef DACCESS_COMPILE + + SHA1Hash sha1; + BYTE *pHash = NULL; + DWORD i; + PublicKeyBlob *pPublicKey = NULL; + DWORD dwHashLenMinusTokenSize = 0; + + if (!StrongNameIsValidPublicKey(pbPublicKeyBlob, cbPublicKeyBlob)) + { + hr = CORSEC_E_INVALID_PUBLICKEY; + goto Exit; + } + + // Allocate a buffer for the output token. + *ppbStrongNameToken = new (nothrow) BYTE[SN_SIZEOF_TOKEN]; + if (*ppbStrongNameToken == NULL) { + hr = E_OUTOFMEMORY; + goto Exit; + } + *pcbStrongNameToken = SN_SIZEOF_TOKEN; + + // We cache a couple of common cases. + if (SN_IS_NEUTRAL_KEY(pbPublicKeyBlob)) { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, g_rbNeutralPublicKeyToken, SN_SIZEOF_TOKEN); + goto Exit; + } + if (cbPublicKeyBlob == SN_SIZEOF_THE_KEY() && + memcmp(pbPublicKeyBlob, SN_THE_KEY(), cbPublicKeyBlob) == 0) { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_KEYTOKEN(), SN_SIZEOF_TOKEN); + goto Exit; + } + + if (SN_IS_THE_SILVERLIGHT_PLATFORM_KEY(pbPublicKeyBlob)) + { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_SILVERLIGHT_PLATFORM_KEYTOKEN(), SN_SIZEOF_TOKEN); + goto Exit; + } + + if (SN_IS_THE_SILVERLIGHT_KEY(pbPublicKeyBlob)) + { + memcpy_s(*ppbStrongNameToken, *pcbStrongNameToken, SN_THE_SILVERLIGHT_KEYTOKEN(), SN_SIZEOF_TOKEN); + goto Exit; + } + + // To compute the correct public key token, we need to make sure the public key blob + // was not padded with extra bytes that CAPI CryptImportKey would've ignored. + // Without this round trip, we would blindly compute the hash over the padded bytes + // which could make finding a public key token collision a significantly easier task + // since an attacker wouldn't need to work hard on generating valid key pairs before hashing. + if (cbPublicKeyBlob <= sizeof(PublicKeyBlob)) { + hr = CORSEC_E_INVALID_PUBLICKEY; + goto Error; + } + + // Check that the blob type is PUBLICKEYBLOB. + pPublicKey = (PublicKeyBlob*) pbPublicKeyBlob; + + if (pPublicKey->PublicKey + GET_UNALIGNED_VAL32(&pPublicKey->cbPublicKey) < pPublicKey->PublicKey) { + hr = CORSEC_E_INVALID_PUBLICKEY; + goto Error; + } + + if (cbPublicKeyBlob < SN_SIZEOF_KEY(pPublicKey)) { + hr = CORSEC_E_INVALID_PUBLICKEY; + goto Error; + } + + if (*(BYTE*) pPublicKey->PublicKey /* PUBLICKEYSTRUC->bType */ != PUBLICKEYBLOB) { + hr = CORSEC_E_INVALID_PUBLICKEY; + goto Error; + } + + // Compute a hash over the public key. + sha1.AddData(pbPublicKeyBlob, cbPublicKeyBlob); + pHash = sha1.GetHash(); + static_assert(SHA1_HASH_SIZE >= SN_SIZEOF_TOKEN, "SN_SIZEOF_TOKEN must be smaller or equal to the SHA1_HASH_SIZE"); + dwHashLenMinusTokenSize = SHA1_HASH_SIZE - SN_SIZEOF_TOKEN; + + // Take the last few bytes of the hash value for our token. (These are the + // low order bytes from a network byte order point of view). Reverse the + // order of these bytes in the output buffer to get host byte order. + for (i = 0; i < SN_SIZEOF_TOKEN; i++) + (*ppbStrongNameToken)[SN_SIZEOF_TOKEN - (i + 1)] = pHash[i + dwHashLenMinusTokenSize]; + + goto Exit; + + Error: + if (*ppbStrongNameToken) { + delete [] *ppbStrongNameToken; + *ppbStrongNameToken = NULL; + } +Exit: +#else + DacNotImpl(); +#endif // #ifndef DACCESS_COMPILE + + return hr; +} diff --git a/src/coreclr/md/staticmd/CMakeLists.txt b/src/coreclr/md/staticmd/CMakeLists.txt new file mode 100644 index 00000000000..99612f824ab --- /dev/null +++ b/src/coreclr/md/staticmd/CMakeLists.txt @@ -0,0 +1,16 @@ +add_definitions(-DNO_COR) + +set(STATICMD_SOURCES + apis.cpp +) + +convert_to_absolute_path(STATICMD_SOURCES ${STATICMD_SOURCES}) + +add_definitions(-DFEATURE_METADATA_EMIT_ALL) +add_definitions(-DFEATURE_METADATA_EMIT) +add_definitions(-DFEATURE_METADATA_INTERNAL_APIS) + +add_library_clr(mdstaticapi ${STATICMD_SOURCES}) + +add_library_clr(mdstaticapi_ppdb ${STATICMD_SOURCES}) +target_compile_definitions(mdstaticapi_ppdb PRIVATE FEATURE_METADATA_EMIT_PORTABLE_PDB)
\ No newline at end of file diff --git a/src/coreclr/md/staticmd/apis.cpp b/src/coreclr/md/staticmd/apis.cpp new file mode 100644 index 00000000000..b13fd8cb7e1 --- /dev/null +++ b/src/coreclr/md/staticmd/apis.cpp @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "stdafx.h" + +#include <utilcode.h> // Utility helpers. +#include <posterror.h> // Error handlers +#define INIT_GUIDS +#include <corpriv.h> +#include <winwrap.h> +#include <mscoree.h> +#include "shimload.h" +#include "metadataexports.h" +#include "ex.h" + +// --------------------------------------------------------------------------- +// %%Function: MetaDataGetDispenser +// This function gets the Dispenser interface given the CLSID and REFIID. +// --------------------------------------------------------------------------- +STDAPI DLLEXPORT MetaDataGetDispenser( // Return HRESULT + REFCLSID rclsid, // The class to desired. + REFIID riid, // Interface wanted on class factory. + LPVOID FAR *ppv) // Return interface pointer here. +{ + + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + PRECONDITION(CheckPointer(ppv)); + } CONTRACTL_END; + + NonVMComHolder<IClassFactory> pcf(NULL); + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(MetaDataDllGetClassObject(rclsid, IID_IClassFactory, (void **) &pcf)); + hr = pcf->CreateInstance(NULL, riid, ppv); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return (hr); +} + +// --------------------------------------------------------------------------- +// %%Function: GetMetaDataInternalInterface +// This function gets the IMDInternalImport given the metadata on memory. +// --------------------------------------------------------------------------- +STDAPI DLLEXPORT GetMetaDataInternalInterface( + LPVOID pData, // [IN] in memory metadata section + ULONG cbData, // [IN] size of the metadata section + DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC + REFIID riid, // [IN] desired interface + void **ppv) // [OUT] returned interface +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + PRECONDITION(CheckPointer(pData)); + PRECONDITION(CheckPointer(ppv)); + } CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = GetMDInternalInterface(pData, cbData, flags, riid, ppv); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// %%Function: GetMetaDataInternalInterfaceFromPublic +// This function gets the internal scopeless interface given the public +// scopeless interface. +// --------------------------------------------------------------------------- +STDAPI DLLEXPORT GetMetaDataInternalInterfaceFromPublic( + IUnknown *pv, // [IN] Given interface. + REFIID riid, // [IN] desired interface + void **ppv) // [OUT] returned interface +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + PRECONDITION(CheckPointer(pv)); + PRECONDITION(CheckPointer(ppv)); + } CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = GetMDInternalInterfaceFromPublic(pv, riid, ppv); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// %%Function: GetMetaDataPublicInterfaceFromInternal +// This function gets the public scopeless interface given the internal +// scopeless interface. +// --------------------------------------------------------------------------- +STDAPI DLLEXPORT GetMetaDataPublicInterfaceFromInternal( + void *pv, // [IN] Given interface. + REFIID riid, // [IN] desired interface. + void **ppv) // [OUT] returned interface +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pv)); + PRECONDITION(CheckPointer(ppv)); + ENTRY_POINT; + } CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = GetMDPublicInterfaceFromInternal(pv, riid, ppv); + + END_ENTRYPOINT_NOTHROW; + return hr; +} diff --git a/src/coreclr/md/staticmd/stdafx.h b/src/coreclr/md/staticmd/stdafx.h new file mode 100644 index 00000000000..65812f46ffa --- /dev/null +++ b/src/coreclr/md/staticmd/stdafx.h @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include <crtwrap.h> +#include <winwrap.h> // Windows wrappers. + +#include <ole2.h> // OLE definitions + + +#include "intrinsic.h" // Functions to make intrinsic. diff --git a/src/coreclr/md/tables/export.h b/src/coreclr/md/tables/export.h new file mode 100644 index 00000000000..16ffef8651a --- /dev/null +++ b/src/coreclr/md/tables/export.h @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: export.h +// + +// +// Popular types defined in MetaData\Tables directory. +// It's supposed to be included from other (MetaData) subcomponents, not from this directory. +// +// ====================================================================================== + +#pragma once + +#include "table.h" diff --git a/src/coreclr/md/tables/external.h b/src/coreclr/md/tables/external.h new file mode 100644 index 00000000000..6c2f4fde6b5 --- /dev/null +++ b/src/coreclr/md/tables/external.h @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: external.h +// + +// +// External types used in MetaData\Storage subcomponent classes. +// This file is used for precompiled headers, so it has to be included at the beginning of every .cpp in +// this directory. +// +// ====================================================================================== + +#pragma once + +#include "../external.h" +#include "../export.h" + +#include "../inc/recordpool.h" +#include "../hotdata/export.h" +#include "../hotdata/hotdataformat.h" diff --git a/src/coreclr/md/tables/table.h b/src/coreclr/md/tables/table.h new file mode 100644 index 00000000000..37897b07e51 --- /dev/null +++ b/src/coreclr/md/tables/table.h @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: Table.h +// + +// +// Class code:MetaData::Table represents a MetaData table. +// +// ====================================================================================== + +#pragma once + +#include "external.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// This class represents a read-only MetaData table (a continuous chunk of data). +// +class TableRO +{ + friend class TableRW; + +private: + // + // Private data + // + + BYTE *m_pData; + +public: + // + // Initialization + // + + __checkReturn + inline HRESULT Initialize( + __range(2, UINT32_MAX) UINT32 cbRecordSize, + DataBlob sourceData, + BOOL fCopyData) + { + _ASSERTE(!fCopyData); + _ASSERTE((cbRecordSize == 0) || (sourceData.GetSize() % cbRecordSize == 0)); + m_pData = sourceData.GetDataPointer(); + return S_OK; + } + + // Destroys the table and all its allocated data. Can run on uninitialized table. + inline void Delete() + { + m_pData = NULL; + } + +public: + // + // Getters + // + + __checkReturn + inline HRESULT GetRecord( + UINT32 nRowIndex, + __deref_out_opt BYTE **ppRecord, + UINT32 cbRecordSize, + UINT32 cRecordCount, +#ifdef FEATURE_PREJIT + struct HotTablesDirectory *pHotTablesDirectory, +#endif //FEATURE_PREJIT + UINT32 nTableIndex) + { + if ((nRowIndex == 0) || (nRowIndex > cRecordCount)) + { + Debug_ReportError("Invalid record index."); + *ppRecord = NULL; + return CLDB_E_INDEX_NOTFOUND; + } +#ifdef FEATURE_PREJIT + if ((pHotTablesDirectory != NULL) && (pHotTablesDirectory->m_rgTableHeader_SignedOffset[nTableIndex] != 0)) + { + HRESULT hr = HotTable::GetData( + nRowIndex, + ppRecord, + cbRecordSize, + HotTable::GetTableHeader(pHotTablesDirectory, nTableIndex)); + + if (hr == S_OK) + { + _ASSERTE(memcmp( + *ppRecord, + m_pData + (nRowIndex - 1) * cbRecordSize, + cbRecordSize) == 0); + return S_OK; + } + if (FAILED(hr)) + { + *ppRecord = NULL; + return hr; + } + _ASSERTE(hr == S_FALSE); + } +#endif //FEATURE_PREJIT + *ppRecord = m_pData + (nRowIndex - 1) * cbRecordSize; + return S_OK; + } // TableRO::GetRecord + +}; // class TableRO + +// -------------------------------------------------------------------------------------- +// +// This class represents a read-write MetaData table. +// +class TableRW +{ +private: + // + // Private data + // + + // The storage of table records. + RecordPool m_RecordStorage; + +public: + // + // Initialization + // + + // Initializes (empty) table of record size (cbRecordSize) with new allocated data for cRecordCount + // records. + __checkReturn + inline HRESULT InitializeEmpty_WithRecordCount( + __range(2, UINT32_MAX) UINT32 cbRecordSize, + __range(0, UINT32_MAX) UINT32 cRecordCount + COMMA_INDEBUG_MD( BOOL debug_fIsReadWrite)) + { + return m_RecordStorage.InitNew(cbRecordSize, cRecordCount); + } + + __checkReturn + inline HRESULT Initialize( + __range(2, UINT32_MAX) UINT32 cbRecordSize, + DataBlob sourceData, + BOOL fCopyData) + { + return m_RecordStorage.InitOnMem(cbRecordSize, sourceData.GetDataPointer(), sourceData.GetSize(), !fCopyData); + } + + __checkReturn + inline HRESULT InitializeFromTable( + const TableRO *pSourceTable, + UINT32 cbRecordSize, + UINT32 cRecordCount, + BOOL fCopyData) + { + return m_RecordStorage.InitOnMem(cbRecordSize, pSourceTable->m_pData, cbRecordSize * cRecordCount, !fCopyData); + } + __checkReturn + inline HRESULT InitializeFromTable( + const TableRW *pSourceTable, + BOOL fCopyData) + { + _ASSERTE(fCopyData); + return m_RecordStorage.ReplaceContents(const_cast<RecordPool *>(&pSourceTable->m_RecordStorage)); + } + + // Destroys the table and all its allocated data. Can run on uninitialized table. + inline void Delete() + { + return m_RecordStorage.Uninit(); + } + +public: + // + // Getters + // + + inline UINT32 GetRecordCount() const + { + return const_cast<RecordPool &>(m_RecordStorage).Count(); + } + inline HRESULT GetRecordsDataSize(UINT32 *pcbSize) const + { + return m_RecordStorage.GetSaveSize(pcbSize); + } + + __checkReturn + inline HRESULT GetRecord( + UINT32 nIndex, + __deref_out_opt BYTE **ppRecord) + { + return m_RecordStorage.GetRecord(nIndex, ppRecord); + } + + __checkReturn + inline HRESULT SaveToStream( + IStream *pStream) const + { + return const_cast<RecordPool &>(m_RecordStorage).PersistToStream(pStream); + } + +public: + // + // Setters + // + + __checkReturn + inline HRESULT AddRecord( + __out BYTE **ppbRecord, + __out UINT32 *pnIndex) + { + return m_RecordStorage.AddRecord(ppbRecord, pnIndex); + } + __checkReturn + inline HRESULT InsertRecord( + UINT32 nIndex, + BYTE **ppbRecord) + { + return m_RecordStorage.InsertRecord(nIndex, ppbRecord); + } + + // Makes table data writable, i.e. copies them into newly allocated chunk of memory. The caller + // guarantees that the table is not writable yet (otherwise this method asserts). + // + // Returns S_OK (even if the table is empty). + // Returns METADATA_E_INTERNAL_ERROR error code if the table has more segments. + __checkReturn + inline HRESULT MakeWritable() + { + return m_RecordStorage.ConvertToRW(); + } + +#ifdef _DEBUG_METADATA + // Sets table information for debugging. + void Debug_SetTableInfo(const char *szTableName, UINT32 nTableIndex) + { + } +#endif //_DEBUG_METADATA + +}; // class TableRW + +}; // namespace MetaData |