Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomáš Rylek <trylek@microsoft.com>2020-12-08 05:19:44 +0300
committerGitHub <noreply@github.com>2020-12-08 05:19:44 +0300
commit69e114c1abf91241a0eeecf1ecceab4711b8aa62 (patch)
treeb81a0b35748f5e598412bcc504335cdbd322cd43 /src/coreclr/md
parent0ec07945a9759a72a689edbb01e69b232e26e05a (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')
-rw-r--r--src/coreclr/md/CMakeLists.txt21
-rw-r--r--src/coreclr/md/ceefilegen/CMakeLists.txt35
-rw-r--r--src/coreclr/md/ceefilegen/blobfetcher.cpp398
-rw-r--r--src/coreclr/md/ceefilegen/cceegen.cpp715
-rw-r--r--src/coreclr/md/ceefilegen/ceegentokenmapper.cpp159
-rw-r--r--src/coreclr/md/ceefilegen/ceesectionstring.cpp132
-rw-r--r--src/coreclr/md/ceefilegen/pesectionman.cpp427
-rw-r--r--src/coreclr/md/ceefilegen/stdafx.h29
-rw-r--r--src/coreclr/md/compiler/CMakeLists.txt85
-rw-r--r--src/coreclr/md/compiler/assemblymd.cpp726
-rw-r--r--src/coreclr/md/compiler/assemblymd_emit.cpp808
-rw-r--r--src/coreclr/md/compiler/classfactory.cpp154
-rw-r--r--src/coreclr/md/compiler/classfactory.h94
-rw-r--r--src/coreclr/md/compiler/custattr.h116
-rw-r--r--src/coreclr/md/compiler/custattr_emit.cpp2003
-rw-r--r--src/coreclr/md/compiler/custattr_import.cpp281
-rw-r--r--src/coreclr/md/compiler/disp.cpp969
-rw-r--r--src/coreclr/md/compiler/disp.h143
-rw-r--r--src/coreclr/md/compiler/emit.cpp3306
-rw-r--r--src/coreclr/md/compiler/filtermanager.cpp1457
-rw-r--r--src/coreclr/md/compiler/filtermanager.h86
-rw-r--r--src/coreclr/md/compiler/helper.cpp500
-rw-r--r--src/coreclr/md/compiler/import.cpp3808
-rw-r--r--src/coreclr/md/compiler/importhelper.cpp3406
-rw-r--r--src/coreclr/md/compiler/importhelper.h367
-rw-r--r--src/coreclr/md/compiler/mdperf.cpp95
-rw-r--r--src/coreclr/md/compiler/mdperf.h241
-rw-r--r--src/coreclr/md/compiler/mdutil.cpp506
-rw-r--r--src/coreclr/md/compiler/mdutil.h83
-rw-r--r--src/coreclr/md/compiler/regmeta.cpp1605
-rw-r--r--src/coreclr/md/compiler/regmeta.h2116
-rw-r--r--src/coreclr/md/compiler/regmeta_compilersupport.cpp231
-rw-r--r--src/coreclr/md/compiler/regmeta_emit.cpp2042
-rw-r--r--src/coreclr/md/compiler/regmeta_imetadatatables.cpp718
-rw-r--r--src/coreclr/md/compiler/regmeta_import.cpp1200
-rw-r--r--src/coreclr/md/compiler/regmeta_vm.cpp288
-rw-r--r--src/coreclr/md/compiler/stdafx.h29
-rw-r--r--src/coreclr/md/compiler/verifylayouts.cpp13
-rw-r--r--src/coreclr/md/compressedinteger.h86
-rw-r--r--src/coreclr/md/compressedinteger.inl98
-rw-r--r--src/coreclr/md/datablob.h230
-rw-r--r--src/coreclr/md/datablob.inl580
-rw-r--r--src/coreclr/md/databuffer.h155
-rw-r--r--src/coreclr/md/databuffer.inl273
-rw-r--r--src/coreclr/md/datasource/CMakeLists.txt25
-rw-r--r--src/coreclr/md/datasource/api.cpp30
-rw-r--r--src/coreclr/md/datasource/datatargetreader.cpp203
-rw-r--r--src/coreclr/md/datasource/datatargetreader.h57
-rw-r--r--src/coreclr/md/datasource/remotemdinternalrwsource.cpp240
-rw-r--r--src/coreclr/md/datasource/remotemdinternalrwsource.h69
-rw-r--r--src/coreclr/md/datasource/stdafx.h25
-rw-r--r--src/coreclr/md/datasource/targettypes.cpp531
-rw-r--r--src/coreclr/md/datasource/targettypes.h359
-rw-r--r--src/coreclr/md/debug_metadata.h100
-rw-r--r--src/coreclr/md/enc/CMakeLists.txt71
-rw-r--r--src/coreclr/md/enc/liteweightstgdbrw.cpp1323
-rw-r--r--src/coreclr/md/enc/mdinternalrw.cpp4069
-rw-r--r--src/coreclr/md/enc/metamodelenc.cpp445
-rw-r--r--src/coreclr/md/enc/metamodelrw.cpp8906
-rw-r--r--src/coreclr/md/enc/pdbheap.cpp72
-rw-r--r--src/coreclr/md/enc/peparse.cpp147
-rw-r--r--src/coreclr/md/enc/rwutil.cpp1290
-rw-r--r--src/coreclr/md/enc/stdafx.h30
-rw-r--r--src/coreclr/md/enc/stgio.cpp1401
-rw-r--r--src/coreclr/md/enc/stgtiggerstorage.cpp977
-rw-r--r--src/coreclr/md/enc/stgtiggerstream.cpp136
-rw-r--r--src/coreclr/md/errors_metadata.h60
-rw-r--r--src/coreclr/md/export.h17
-rw-r--r--src/coreclr/md/external.h16
-rw-r--r--src/coreclr/md/heaps/blobheap.h323
-rw-r--r--src/coreclr/md/heaps/export.h17
-rw-r--r--src/coreclr/md/heaps/external.h21
-rw-r--r--src/coreclr/md/heaps/guidheap.h258
-rw-r--r--src/coreclr/md/heaps/stringheap.h306
-rw-r--r--src/coreclr/md/hotdata/CMakeLists.txt52
-rw-r--r--src/coreclr/md/hotdata/export.h24
-rw-r--r--src/coreclr/md/hotdata/external.h19
-rw-r--r--src/coreclr/md/hotdata/heapindex.h67
-rw-r--r--src/coreclr/md/hotdata/hotdataformat.h154
-rw-r--r--src/coreclr/md/hotdata/hotheap.cpp184
-rw-r--r--src/coreclr/md/hotdata/hotheap.h68
-rw-r--r--src/coreclr/md/hotdata/hotheapsdirectoryiterator.cpp109
-rw-r--r--src/coreclr/md/hotdata/hotheapsdirectoryiterator.h70
-rw-r--r--src/coreclr/md/hotdata/hotheapwriter.cpp304
-rw-r--r--src/coreclr/md/hotdata/hotheapwriter.h83
-rw-r--r--src/coreclr/md/hotdata/hotmetadata.cpp84
-rw-r--r--src/coreclr/md/hotdata/hotmetadata.h42
-rw-r--r--src/coreclr/md/hotdata/hottable.cpp137
-rw-r--r--src/coreclr/md/hotdata/hottable.h56
-rw-r--r--src/coreclr/md/inc/VerifyLayouts.inc344
-rw-r--r--src/coreclr/md/inc/assemblymdinternaldisp.h17
-rw-r--r--src/coreclr/md/inc/cahlprinternal.h93
-rw-r--r--src/coreclr/md/inc/liteweightstgdb.h264
-rw-r--r--src/coreclr/md/inc/mdcolumndescriptors.h66
-rw-r--r--src/coreclr/md/inc/mdinternalrw.h815
-rw-r--r--src/coreclr/md/inc/mdlog.h24
-rw-r--r--src/coreclr/md/inc/metadatahash.h206
-rw-r--r--src/coreclr/md/inc/metamodel.h2165
-rw-r--r--src/coreclr/md/inc/metamodelro.h240
-rw-r--r--src/coreclr/md/inc/metamodelrw.h1456
-rw-r--r--src/coreclr/md/inc/pdbheap.h33
-rw-r--r--src/coreclr/md/inc/portablepdbmdds.h78
-rw-r--r--src/coreclr/md/inc/portablepdbmdi.h96
-rw-r--r--src/coreclr/md/inc/recordpool.h161
-rw-r--r--src/coreclr/md/inc/rwutil.h344
-rw-r--r--src/coreclr/md/inc/stgio.h288
-rw-r--r--src/coreclr/md/inc/stgtiggerstorage.h327
-rw-r--r--src/coreclr/md/inc/stgtiggerstream.h111
-rw-r--r--src/coreclr/md/inc/streamutil.h220
-rw-r--r--src/coreclr/md/inc/verifylayouts.h183
-rw-r--r--src/coreclr/md/runtime/CMakeLists.txt68
-rw-r--r--src/coreclr/md/runtime/liteweightstgdb.cpp261
-rw-r--r--src/coreclr/md/runtime/mdcolumndescriptors.cpp263
-rw-r--r--src/coreclr/md/runtime/mdfileformat.cpp192
-rw-r--r--src/coreclr/md/runtime/mdinternaldisp.cpp209
-rw-r--r--src/coreclr/md/runtime/mdinternaldisp.h35
-rw-r--r--src/coreclr/md/runtime/mdinternalro.cpp3481
-rw-r--r--src/coreclr/md/runtime/mdinternalro.h805
-rw-r--r--src/coreclr/md/runtime/metamodel.cpp1204
-rw-r--r--src/coreclr/md/runtime/metamodelcolumndefs.h454
-rw-r--r--src/coreclr/md/runtime/metamodelro.cpp443
-rw-r--r--src/coreclr/md/runtime/recordpool.cpp387
-rw-r--r--src/coreclr/md/runtime/stdafx.h27
-rw-r--r--src/coreclr/md/runtime/strongnameinternal.cpp333
-rw-r--r--src/coreclr/md/staticmd/CMakeLists.txt16
-rw-r--r--src/coreclr/md/staticmd/apis.cpp126
-rw-r--r--src/coreclr/md/staticmd/stdafx.h10
-rw-r--r--src/coreclr/md/tables/export.h15
-rw-r--r--src/coreclr/md/tables/external.h21
-rw-r--r--src/coreclr/md/tables/table.h242
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, &sectionIdx);
+ 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, &section);
+ 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, &section);
+ 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 = &sectStart[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 = &sectStart[curLen];
+ sectEnd = &sectStart[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, &paramRid));
+ 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, &paramRid));
+ 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