diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
commit | 4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch) | |
tree | 98110734c91668dfdbb126fcc0e15ddbd93738ca /src/md/compiler/regmeta.cpp | |
parent | fa45f57ed55137c75ac870356a1b8f76c84b229c (diff) | |
download | coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2 coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip |
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/md/compiler/regmeta.cpp')
-rw-r--r-- | src/md/compiler/regmeta.cpp | 1588 |
1 files changed, 1588 insertions, 0 deletions
diff --git a/src/md/compiler/regmeta.cpp b/src/md/compiler/regmeta.cpp new file mode 100644 index 0000000000..230d1e4ff0 --- /dev/null +++ b/src/md/compiler/regmeta.cpp @@ -0,0 +1,1588 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// 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), +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + m_pCorHost(NULL), +#endif // FEATURE_INCLUDE_ALL_INTERFACES + 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_pVEHandler(0), + 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 (m_pVEHandler) + m_pVEHandler->Release(); + + // If This RegMeta spun up the runtime (probably to process security + // attributes), shut it down now. + if (m_fStartedEE) + { + m_pAppDomain->Release(); +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + m_pCorHost->Stop(); + m_pCorHost->Release(); +#endif // FEATURE_INCLUDE_ALL_INTERFACES + } + + 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);) + } + +#ifdef FEATURE_METADATA_EMIT_ALL + // initialize the embedded merger + m_newMerger.Init(this); +#endif //FEATURE_METADATA_EMIT_ALL + +ErrExit: + return hr; +} // RegMeta::CreateNewMD + +#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)) + { +#ifdef FEATURE_METADATA_EMIT_ALL + // initialize the embedded merger + m_newMerger.Init(this); +#endif //FEATURE_METADATA_EMIT_ALL + + // 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)) + { +#ifdef FEATURE_METADATA_EMIT_ALL + // initialize the embedded merger + m_newMerger.Init(this); +#endif //FEATURE_METADATA_EMIT_ALL + + // 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); + } + +#ifndef FEATURE_METADATA_STANDALONE_WINRT + else if (riid == IID_IMetaDataInfo) + { + *ppUnk = static_cast<IMetaDataInfo *>(this); + } +#endif //!FEATURE_METADATA_STANDALONE_WINRT + +#ifdef FEATURE_METADATA_EMIT + else if (riid == IID_IMetaDataEmit) + { + *ppUnk = (IMetaDataEmit2 *)this; + fIsInterfaceRW = true; + } + else if (riid == IID_IMetaDataEmit2) + { + *ppUnk = (IMetaDataEmit2 *)this; + fIsInterfaceRW = true; + } + else if (riid == IID_IMetaDataAssemblyEmit) + { + *ppUnk = (IMetaDataAssemblyEmit *)this; + fIsInterfaceRW = true; + } +#endif //FEATURE_METADATA_EMIT + +#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) + else if (riid == IID_IMetaDataValidate) + { + *ppUnk = (IMetaDataValidate *)this; + } +#endif //defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) + +#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 + +#ifndef FEATURE_METADATA_STANDALONE_WINRT + +//--------------------------------------------------------------------------------------- +// +// 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 + +#endif //!FEATURE_METADATA_STANDALONE_WINRT + +//------------------------------------------------------------------------------ +// 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; + count = _vsnprintf((char *)m_output.Ptr(), STRING_BUFFER_LEN * i, str, marker); + 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. + //From what I can tell the only caller to this method is the code that tests if we need to apply the WinMD adapter + //and it checks if pVer == "WindowsRuntime". We don't support the debugger custom metadata source scenario with WinMDs (yet?) + //so we intend for that check to return FALSE. + *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 + |