diff options
Diffstat (limited to 'src/md/compiler')
52 files changed, 42701 insertions, 0 deletions
diff --git a/src/md/compiler/.gitmirror b/src/md/compiler/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/md/compiler/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/md/compiler/CMakeLists.txt b/src/md/compiler/CMakeLists.txt new file mode 100644 index 0000000000..4d99d11edc --- /dev/null +++ b/src/md/compiler/CMakeLists.txt @@ -0,0 +1,32 @@ +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 + verifylayouts.cpp +) + +convert_to_absolute_path(MDCOMPILER_SOURCES ${MDCOMPILER_SOURCES}) + +if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIC) +endif(CLR_CMAKE_PLATFORM_UNIX) + +add_subdirectory(dac) +add_subdirectory(wks) +add_subdirectory(dbi) +add_subdirectory(crossgen) diff --git a/src/md/compiler/Compiler.settings.targets b/src/md/compiler/Compiler.settings.targets new file mode 100644 index 0000000000..97f2504796 --- /dev/null +++ b/src/md/compiler/Compiler.settings.targets @@ -0,0 +1,68 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + + <!-- + We build MetaData in several flavors: + - Full version (wks) - part of clr.dll/coreclr.dll. + - dac version - does not need Emit APIs. + - Standalone versions for: + * mscordbi.dll (hands the interfaces over to debugger client (e.g. VS) - does not need Emit APIs. + * CorDbg - does not need Emit APIs. + * WinRT + - Read-Only version (ships in Windows) - does not need Emit and Internal APIs. + - Read-Writer version (ships as private component of MidlRt.exe SDK tool) - does not need Internal APIs. + --> + + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\MD.props" /> + + <PropertyGroup> + <MDCompilerSrcDirectory>$(ClrSrcDirectory)\MD\Compiler\</MDCompilerSrcDirectory> + <UserIncludes> + $(UserIncludes); + $(ClrSrcDirectory)\md\inc; + $(ClrSrcDirectory)\vm; + $(ClrSrcDirectory)\strongname\inc + </UserIncludes> + <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions> + <!--OK to delete NO_NTDLL for devdiv builds.--> + <OutputPath>$(ClrLibDest)</OutputPath> + <TargetType>LIBRARY</TargetType> + <PCHHeader>stdafx.h</PCHHeader> + <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders> + <!--PCH: Both precompiled header and cpp are on the same $(MDCompilerSrcDirectory)\ path this is likely to be wrong.--> + <PCHCompile>$(MDCompilerSrcDirectory)\stdafx.cpp</PCHCompile> + <PCHObject>stdafx_compiler.obj</PCHObject> + <LinkUseCMT>false</LinkUseCMT> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj"> + <Comment>clrinternal.h</Comment> + </ProjectReference> + </ItemGroup> + + <ItemGroup> + <CppCompile Include="$(MDCompilerSrcDirectory)\AssemblyMD.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\AssemblyMD_Emit.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\ClassFactory.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\CustAttr_Import.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\CustAttr_Emit.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\Disp.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\Emit.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\FilterManager.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\Helper.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\Import.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\ImportHelper.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\MDPerf.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\MDUtil.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\MDValidator.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\NewMerger.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_CompilerSupport.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_Emit.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_Import.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_IMetaDataTables.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\RegMeta_VM.cpp" /> + <CppCompile Include="$(MDCompilerSrcDirectory)\VerifyLayouts.cpp" /> + </ItemGroup> +</Project> diff --git a/src/md/compiler/assemblymd.cpp b/src/md/compiler/assemblymd.cpp new file mode 100644 index 0000000000..8c0a22af4d --- /dev/null +++ b/src/md/compiler/assemblymd.cpp @@ -0,0 +1,758 @@ +// 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. +//***************************************************************************** +// 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" + +#include <strongname.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); + +#ifdef FEATURE_WINDOWSPHONE + // 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; +#else + if (ppbPublicKey) + { + if (pcbPublicKey && *pcbPublicKey) + *pdwAssemblyFlags |= afPublicKey; + } + else + { +#ifdef _DEBUG + // Assert that afPublicKey is set if PublicKey blob is not empty + DWORD cbPublicKey; + const BYTE *pbPublicKey; + IfFailGo(pMiniMd->getPublicKeyOfAssembly(pRecord, &pbPublicKey, &cbPublicKey)); + bool hasPublicKey = cbPublicKey != 0; + bool hasPublicKeyFlag = ( *pdwAssemblyFlags & afPublicKey ) != 0; + _ASSERTE( hasPublicKey == hasPublicKeyFlag ); +#endif + } +#endif // FEATURE_WINDOWSPHONE + } + // 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 + +extern HRESULT STDMETHODCALLTYPE + GetAssembliesByName(LPCWSTR szAppBase, + LPCWSTR szPrivateBin, + LPCWSTR szAssemblyName, + IUnknown *ppIUnk[], + ULONG cMax, + ULONG *pcAssemblies); + +//******************************************************************************* +// 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(GetAssembliesByName(szAppBase, szPrivateBin, + szAssemblyName, ppIUnk, cMax, pcAssemblies)); + +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/md/compiler/assemblymd_emit.cpp b/src/md/compiler/assemblymd_emit.cpp new file mode 100644 index 0000000000..72fb034221 --- /dev/null +++ b/src/md/compiler/assemblymd_emit.cpp @@ -0,0 +1,811 @@ +// 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. +//***************************************************************************** +// 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" + +#include <strongname.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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_MAX) + pRecord->SetOffset(dwOffset); + if (dwResourceFlags != ULONG_MAX) + pRecord->SetFlags(dwResourceFlags); + + IfFailGo(UpdateENCLog(mr)); + +ErrExit: + return hr; +} // RegMeta::_SetManifestResourceProps + +#endif //FEATURE_METADATA_EMIT diff --git a/src/md/compiler/cacheload.h b/src/md/compiler/cacheload.h new file mode 100644 index 0000000000..e1c3126fd9 --- /dev/null +++ b/src/md/compiler/cacheload.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. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// CacheLoad.h +// + +// +// Class for returning the memory image where the image lives +// +//***************************************************************************** +#ifndef __CACHELOAD__H__ +#define __CACHELOAD__H__ + + +#undef INTERFACE +#define INTERFACE ICacheLoad +DECLARE_INTERFACE_(ICacheLoad, IUnknown) +{ + STDMETHOD(GetCachedImaged)( + LPVOID* pImage); + + STDMETHOD(SetCachedImaged)( + LPVOID pImage); +}; + +#endif diff --git a/src/md/compiler/classfactory.cpp b/src/md/compiler/classfactory.cpp new file mode 100644 index 0000000000..603f7975aa --- /dev/null +++ b/src/md/compiler/classfactory.cpp @@ -0,0 +1,173 @@ +// 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. +//***************************************************************************** +// 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" + +#include "clrprivhosting.h" + +extern HRESULT TypeNameFactoryCreateObject(REFIID riid, void **ppUnk); + +#include <ndpversion.h> + + +//********** Locals. ********************************************************** +HINSTANCE GetModuleInst(); + +// @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 }, +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) // coreclr doesn't export these + { &CLSID_CorMetaDataDispenserRuntime, W("CorMetaDataDispenserRuntime"), Disp::CreateObject }, + + { &CLSID_CorRuntimeHost, W("CorRuntimeHost"), CorHost::CreateObject }, + { &CLSID_CLRRuntimeHost, W("CLRRuntimeHost"), CorHost2::CreateObject }, + { &__uuidof(CLRPrivRuntime), W("CLRPrivRuntime"), CorHost2::CreateObject }, + { &CLSID_TypeNameFactory, NULL, (PFN_CREATE_OBJ)TypeNameFactoryCreateObject }, +#endif // FEATURE_CORECLR && !CROSSGEN_COMPILE + { 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/md/compiler/classfactory.h b/src/md/compiler/classfactory.h new file mode 100644 index 0000000000..3430fed0f2 --- /dev/null +++ b/src/md/compiler/classfactory.h @@ -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. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// 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/md/compiler/crossgen/.gitmirror b/src/md/compiler/crossgen/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/md/compiler/crossgen/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/md/compiler/crossgen/CMakeLists.txt b/src/md/compiler/crossgen/CMakeLists.txt new file mode 100644 index 0000000000..7baf17448b --- /dev/null +++ b/src/md/compiler/crossgen/CMakeLists.txt @@ -0,0 +1,5 @@ +include(${CLR_DIR}/crossgen.cmake) +include(../../md_wks.cmake) + +add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES) +add_library_clr(mdcompiler_crossgen ${MDCOMPILER_SOURCES}) diff --git a/src/md/compiler/crossgen/MDCompiler_crossgen.nativeproj b/src/md/compiler/crossgen/MDCompiler_crossgen.nativeproj new file mode 100644 index 0000000000..8ea56d2cdc --- /dev/null +++ b/src/md/compiler/crossgen/MDCompiler_crossgen.nativeproj @@ -0,0 +1,15 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <PropertyGroup> + <!-- All features are set in file:..\..\MD.props --> + <BuildSysBinaries>true</BuildSysBinaries> + <MetadataFlavor>wks</MetadataFlavor> + <OutputName>mdcompiler_crossgen</OutputName> + </PropertyGroup> + + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Compiler\Compiler.settings.targets" /> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/md/compiler/custattr.h b/src/md/compiler/custattr.h new file mode 100644 index 0000000000..119c05922a --- /dev/null +++ b/src/md/compiler/custattr.h @@ -0,0 +1,117 @@ +// 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. + +// + +#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/md/compiler/custattr_emit.cpp b/src/md/compiler/custattr_emit.cpp new file mode 100644 index 0000000000..3e23600176 --- /dev/null +++ b/src/md/compiler/custattr_emit.cpp @@ -0,0 +1,2000 @@ +// 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. +//***************************************************************************** +// 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, otherrwise 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. + 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. + 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. + 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)); + } + 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 = ULONG_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!=ULONG_MAX || ulSize!=ULONG_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/md/compiler/custattr_import.cpp b/src/md/compiler/custattr_import.cpp new file mode 100644 index 0000000000..7710eb3790 --- /dev/null +++ b/src/md/compiler/custattr_import.cpp @@ -0,0 +1,282 @@ +// 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. +//***************************************************************************** + +// +// 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/md/compiler/dac/.gitmirror b/src/md/compiler/dac/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/md/compiler/dac/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/md/compiler/dac/CMakeLists.txt b/src/md/compiler/dac/CMakeLists.txt new file mode 100644 index 0000000000..dda76e1cd9 --- /dev/null +++ b/src/md/compiler/dac/CMakeLists.txt @@ -0,0 +1,6 @@ + +include(${CLR_DIR}/dac.cmake) +include(../../md_dac.cmake) + +add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES) +add_library_clr(mdcompiler_dac ${MDCOMPILER_SOURCES}) diff --git a/src/md/compiler/dac/dirs.proj b/src/md/compiler/dac/dirs.proj new file mode 100644 index 0000000000..cf39ab9ea2 --- /dev/null +++ b/src/md/compiler/dac/dirs.proj @@ -0,0 +1,19 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="HostLocal\mdcompiler_dac.nativeproj" /> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/md/compiler/dbi/.gitmirror b/src/md/compiler/dbi/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/md/compiler/dbi/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/md/compiler/dbi/CMakeLists.txt b/src/md/compiler/dbi/CMakeLists.txt new file mode 100644 index 0000000000..b870984309 --- /dev/null +++ b/src/md/compiler/dbi/CMakeLists.txt @@ -0,0 +1,4 @@ +include(../../md_dbi.cmake) + +add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES) +add_library_clr(mdcompiler-dbi ${MDCOMPILER_SOURCES})
\ No newline at end of file diff --git a/src/md/compiler/dbi/MDCompiler-dbi.props b/src/md/compiler/dbi/MDCompiler-dbi.props new file mode 100644 index 0000000000..a9c469c98e --- /dev/null +++ b/src/md/compiler/dbi/MDCompiler-dbi.props @@ -0,0 +1,9 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <PropertyGroup> + <!-- All features are set in file:..\..\MD.props --> + <MetadataFlavor>mscordbi</MetadataFlavor> + </PropertyGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Compiler\Compiler.settings.targets" /> + +</Project> diff --git a/src/md/compiler/dbi/dirs.proj b/src/md/compiler/dbi/dirs.proj new file mode 100644 index 0000000000..89a842727c --- /dev/null +++ b/src/md/compiler/dbi/dirs.proj @@ -0,0 +1,19 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\mdcompiler-dbi.nativeproj" /> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/md/compiler/dirs.proj b/src/md/compiler/dirs.proj new file mode 100644 index 0000000000..2e97d62490 --- /dev/null +++ b/src/md/compiler/dirs.proj @@ -0,0 +1,27 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1--> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="wks\mdcompiler_wks.nativeproj" /> + <ProjectFile Include="dac\dirs.proj" /> + <ProjectFile Include="dbi\dirs.proj" /> + </ItemGroup> + + <!--The following projects will build during PHASE 1 of the Desktop build --> + <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreClr)' != 'true'"> + <ProjectFile Include="winrt-ro\mdcompiler-winrt-ro.nativeproj" /> + <ProjectFile Include="winrt-rw\mdcompiler-winrt-rw.nativeproj" /> + </ItemGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/md/compiler/disp.cpp b/src/md/compiler/disp.cpp new file mode 100644 index 0000000000..b091729744 --- /dev/null +++ b/src/md/compiler/disp.cpp @@ -0,0 +1,939 @@ +// 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. +//***************************************************************************** +// 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 FEATURE_COMINTEROP_TLB_SUPPORT +#include <imptlb.h> +#endif + +#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; + + // Allow Avalon to use the SecurityCriticalAttribute + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_FORCE_ASSEMREF_DUPCHECK)) + { + m_OptionValue.m_DupCheck = (CorCheckDuplicatesFor)(m_OptionValue.m_DupCheck|MDDupAssemblyRef); + } + +} // 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; + DWORD len; + 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) + { +#ifdef FEATURE_METADATA_STANDALONE_WINRT + IfFailGo(E_NOTIMPL); +#else + optionForNewScope.m_MetadataVersion = MDVersion1; +#endif //!FEATURE_METADATA_STANDALONE_WINRT + } + 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. + + 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 guy. + 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 (this may +// involve wrapping a WinMD adapter.) +//***************************************************************************** +static HRESULT DeliverScope(IMDCommon *pMDCommon, REFIID riid, DWORD dwOpenFlags, IUnknown **ppIUnk) +{ + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + +#if !defined(FEATURE_METADATA_STANDALONE_WINRT) && defined(FEATURE_COMINTEROP) + IfFailGo((dwOpenFlags & ofNoTransform) ? S_FALSE : CheckIfWinMDAdapterNeeded(pMDCommon)); + if (hr == S_OK) + { + IfFailGo(CreateWinMDImport(pMDCommon, riid, (void**)ppIUnk)); + } + else +#endif + { + 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 + +#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + +#include <metahost.h> +// Pointer to the activated CLR interface provided by the shim. +extern ICLRRuntimeInfo * g_pCLRRuntime; + +#endif + +//***************************************************************************** +// 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 +{ +#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + // This implies a machine-wide CLR install root, which may not exist for some CLR + // skus using standalone metadata. + *pcchBuffer = cchBuffer; + hr = g_pCLRRuntime->GetRuntimeDirectory(szBuffer, pcchBuffer); + + END_ENTRYPOINT_NOTHROW; + + return hr; +#else //!FEATURE_METADATA_IN_VM || FEATURE_CORECLR + +#ifdef FEATURE_CORECLR + UNREACHABLE_MSG("Calling IMetaDataDispenser::GetCORSystemDirectory! This code should not be " + "reachable or needs to be reimplemented for CoreCLR!"); +#endif //FEATURE_CORECLR + + return E_NOTIMPL; +#endif //!FEATURE_METADATA_IN_VM || FEATURE_CORECLR +} // 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_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_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) || defined(FEATURE_METADATA_STANDALONE_WINRT) + +//--------------------------------------------------------------------------------------- +// +// Process detach destruction. +// Called from DllMain of clr.dll/RoMetadata.dll/MidlrtMd.dll. +// +void DeleteMetaData() +{ + LOADEDMODULES::DeleteStatics(); +} + +#endif //FEATURE_METADATA_IN_VM || FEATURE_METADATA_STANDALONE_WINRT + +// +// 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/md/compiler/disp.h b/src/md/compiler/disp.h new file mode 100644 index 0000000000..76a49e0d24 --- /dev/null +++ b/src/md/compiler/disp.h @@ -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. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// 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 : + public IMetaDataDispenserEx +#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_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/md/compiler/emit.cpp b/src/md/compiler/emit.cpp new file mode 100644 index 0000000000..c77e28fa0f --- /dev/null +++ b/src/md/compiler/emit.cpp @@ -0,0 +1,3001 @@ +// 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. +//***************************************************************************** +// 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 != ULONG_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 != ULONG_MAX) + pMethodRec->SetImplFlags(static_cast<USHORT>(dwImplFlags)); + + IfFailGo(UpdateENCLog(tk)); + } + else // TypeFromToken(tk) == mdtFieldDef + { + _ASSERTE(dwImplFlags==0 || dwImplFlags==ULONG_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 != ULONG_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 != ULONG_MAX) + pClassLayout->SetPackingSize(static_cast<USHORT>(dwPackSize)); + if (ulClassSize != ULONG_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, ULONG_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 i; // Loop counter. + BOOL bIs80Plus = false; // Is there an 80+ WCHAR. + ULONG ulMemSize; // Size of memory taken by the string passed in. + PBYTE pb; // Pointer into memory allocated by qb. + WCHAR c; // Temporary used during comparison; + + + + 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 != ULONG_MAX); + + // + // Walk the entire string looking for characters that would block us from doing + // a fast comparison of the string. These characters include anything greater than + // 0x80 or an apostrophe or a hyphen. Apostrophe and hyphen are excluded because + // they would prevent words like coop and co-op from sorting together in a culture-aware + // comparison. We also need to exclude some set of the control characters. This check + // is more restrictive + // + for (i=0; i<cchString; i++) { + c = szString[i]; + if (c>=0x80 || HighCharHelper::IsHighChar((int)c)) { + bIs80Plus = true; + break; + } + } + + // 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); + // Set the last byte of memory to indicate whether there is a 80+ character. + *(pb + ulMemSize) = bIs80Plus ? 1 : 0; + + 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 + +//***************************************************************************** +// 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 != ULONG_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 == ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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/md/compiler/filtermanager.cpp b/src/md/compiler/filtermanager.cpp new file mode 100644 index 0000000000..c8e9ac3e07 --- /dev/null +++ b/src/md/compiler/filtermanager.cpp @@ -0,0 +1,1458 @@ +// 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. +//***************************************************************************** +// 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 == false) + { + 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 == false) + { + 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); + + memset(&hEnum, 0, sizeof(HENUMInternal)); + 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/md/compiler/filtermanager.h b/src/md/compiler/filtermanager.h new file mode 100644 index 0000000000..374afb7dfd --- /dev/null +++ b/src/md/compiler/filtermanager.h @@ -0,0 +1,87 @@ +// 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. +//***************************************************************************** +// 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/md/compiler/helper.cpp b/src/md/compiler/helper.cpp new file mode 100644 index 0000000000..0727176f07 --- /dev/null +++ b/src/md/compiler/helper.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. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// 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 == ULONG_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 + +#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/md/compiler/import.cpp b/src/md/compiler/import.cpp new file mode 100644 index 0000000000..9d1dfc8116 --- /dev/null +++ b/src/md/compiler/import.cpp @@ -0,0 +1,3809 @@ +// 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. +//***************************************************************************** +// 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(); + + memset(&hEnum, 0, sizeof(HENUMInternal)); + + 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); + + memset(&hEnum, 0, sizeof(HENUMInternal)); + 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; + memset(&hEnum, 0, sizeof(HENUMInternal)); + + // 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 != ULONG_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); + + memset(&hEnum, 0, sizeof(HENUMInternal)); + 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/md/compiler/importhelper.cpp b/src/md/compiler/importhelper.cpp new file mode 100644 index 0000000000..abebe1e805 --- /dev/null +++ b/src/md/compiler/importhelper.cpp @@ -0,0 +1,3415 @@ +// 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. +//***************************************************************************** +// ImportHelper.cpp +// + +// +// contains utility code to MD directory +// +//***************************************************************************** +#include "stdafx.h" +#include "importhelper.h" +#include "mdutil.h" +#include "rwutil.h" +#include "mdlog.h" +#include "strongname.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 (used in .winmd files as TypeRef scope for self-references) + 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. + if (!StrongNameTokenFromPublicKey((BYTE*)pbTmp, + cbTmp, + (BYTE**)&pbTmpToken, + &cbTmpToken)) + { + return StrongNameErrorInfo(); + } + 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 + if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKeyOrToken, + cbPublicKeyOrToken, + (BYTE**)&pbToken, + &cbToken)) + { + return StrongNameErrorInfo(); + } +#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; + if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKey, + cbPublicKey, + (BYTE**)&pbToken, + &cbToken)) + IfFailGo(StrongNameErrorInfo()); + } + 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. + if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKey2, + cbPublicKey2, + (BYTE**)&pbToken, + &cbToken)) + return StrongNameErrorInfo(); + + 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/md/compiler/importhelper.h b/src/md/compiler/importhelper.h new file mode 100644 index 0000000000..ea152f53ef --- /dev/null +++ b/src/md/compiler/importhelper.h @@ -0,0 +1,368 @@ +// 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. +//***************************************************************************** +// 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/md/compiler/mdperf.cpp b/src/md/compiler/mdperf.cpp new file mode 100644 index 0000000000..4c63af84a9 --- /dev/null +++ b/src/md/compiler/mdperf.cpp @@ -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. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// 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/md/compiler/mdperf.h b/src/md/compiler/mdperf.h new file mode 100644 index 0000000000..77def32d21 --- /dev/null +++ b/src/md/compiler/mdperf.h @@ -0,0 +1,243 @@ +// 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. +//***************************************************************************** +// 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(ProcessFilter)\ + 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/md/compiler/mdsighelper.h b/src/md/compiler/mdsighelper.h new file mode 100644 index 0000000000..0d089729bb --- /dev/null +++ b/src/md/compiler/mdsighelper.h @@ -0,0 +1,128 @@ +// 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. +//***************************************************************************** + +// +// MDSigHelp.h +// +// contains utility code for signature parsing and comparisons. +// +// This is needed for validator support, especially because it may need to compare signatures +// across multiple metadata scopes. +//***************************************************************************** + +#ifndef __mdsighelper_h_ +#define __mdsighelper_h_ + +#include "regmeta.h" + + +//**************************************************************************** +//**************************************************************************** +class MDSigParser : public SigParser +{ + friend class MDSigComparer; + + public: + //------------------------------------------------------------------------ + // Constructor. + //------------------------------------------------------------------------ + FORCEINLINE MDSigParser(PCCOR_SIGNATURE ptr, DWORD len) + : SigParser(ptr, len) + { } + + FORCEINLINE MDSigParser(const MDSigParser &sig) + : SigParser(sig.m_ptr, sig.m_dwLen) + { } +}; + +//**************************************************************************** +//**************************************************************************** +class MDSigComparer +{ + public: + //------------------------------------------------------------------------ + // This is the base type used to provide callback comparison functionality. + //------------------------------------------------------------------------ + class MDSigComparerBaseType + { + public: + //------------------------------------------------------------------------ + // Returns S_OK if the tokens are equivalent, E_FAIL if they are not, or + // error. + //------------------------------------------------------------------------ + virtual HRESULT CompareToken(const mdToken &tok1, const mdToken &tok2) = 0; + }; + + //------------------------------------------------------------------------ + // Ctor + //------------------------------------------------------------------------ + MDSigComparer(const MDSigParser &sig1, + const MDSigParser &sig2, + MDSigComparerBaseType &comparer) + : m_sig1(sig1), m_sig2(sig2), m_comparer(comparer) + { } + + //------------------------------------------------------------------------ + // Returns S_OK if the signatures are equivalent, E_FAIL if they are not, + // or error. + //------------------------------------------------------------------------ + HRESULT CompareMethodSignature(); + + protected: + MDSigParser m_sig1; + MDSigParser m_sig2; + MDSigComparerBaseType &m_comparer; + + // This will compare exactly one type in each signature to determine + // if they are equal + HRESULT _CompareMethodSignature(); + HRESULT _CompareExactlyOne(); + HRESULT _CompareData(ULONG *pulData); + HRESULT _CompareMethodSignatureHeader(ULONG &cArgs); +}; + +//**************************************************************************** +//**************************************************************************** +class UnifiedAssemblySigComparer : public MDSigComparer::MDSigComparerBaseType +{ + public: + //------------------------------------------------------------------------ + // Ctor + //------------------------------------------------------------------------ + UnifiedAssemblySigComparer(const RegMeta ®Meta) + : m_pRegMeta(const_cast<RegMeta*>(®Meta)) + { } + + //------------------------------------------------------------------------ + // Returns S_OK if the tokens are equivalent, E_FAIL if they are not, or + // error. + //------------------------------------------------------------------------ + virtual HRESULT CompareToken(const mdToken &tok1, const mdToken &tok2); + + protected: + RegMeta *m_pRegMeta; + +#ifdef FEATURE_FUSION + HRESULT _CreateIAssemblyNameFromAssemblyRef( + mdToken tkAsmRef, + IAssemblyName **ppAsmName); +#else + HRESULT _CompareAssemblies(mdToken tkAsmRef1,mdToken tkAsmRef2, BOOL* pfEquivalent); +#endif + + HRESULT _CreateTypeNameFromTypeRef( + mdToken tkTypeRef, + SString &ssName, + mdToken &tkParent); + + HRESULT _CreateFullyQualifiedTypeNameFromTypeRef( + mdToken tkTypeRef, + SString &ssFullName, + mdToken &tkParent); +}; + + +#endif // __mdsighelper_h_ + diff --git a/src/md/compiler/mdutil.cpp b/src/md/compiler/mdutil.cpp new file mode 100644 index 0000000000..2e01258bea --- /dev/null +++ b/src/md/compiler/mdutil.cpp @@ -0,0 +1,774 @@ +// 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. +//***************************************************************************** +// 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) || defined(FEATURE_METADATA_STANDALONE_WINRT) + +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 || FEATURE_METADATA_STANDALONE_WINRT + +#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 + +#if defined(FEATURE_METADATA_IN_VM) + +//***************************************************************************** +// This is a routine to try to find a class implementation given its fully +// qualified name by using the CORPATH environment variable. CORPATH is a list +// of directories (like PATH). Before checking CORPATH, this checks the current +// directory, then the directory that the exe lives in. The search is +// performed by parsing off one element at a time from the class name, +// appending it to the directory and looking for a subdirectory or image with +// that name. If the subdirectory exists, it drills down into that subdirectory +// and tries the next element of the class name. When it finally bottoms out +// but can't find the image it takes the rest of the fully qualified class name +// and appends them with intervening '.'s trying to find a matching DLL. +// Example: +// +// CORPATH=c:\bin;c:\prog +// classname = namespace.class +// +// checks the following things in order: +// c:\bin\namespace, (if <-exists) c:\bin\namespace\class.dll, +// c:\bin\namespace.dll, c:\bin\namespace.class.dll +// c:\prog\namespace, (if <-exists) c:\prog\namespace\class.dll, +// c:\prog\namespace.dll, c:\prog\namespace.class.dll +//***************************************************************************** +HRESULT CORPATHService::GetClassFromCORPath( + __in __in_z LPWSTR wzClassname, // [IN] fully qualified class name + mdTypeRef tr, // [IN] TypeRef to be resolved. + IMetaModelCommon *pCommon, // [IN] Scope in which the TypeRef is defined. + REFIID riid, // [IN] Interface type to be returned. + IUnknown **ppIScope, // [OUT] Scope in which the TypeRef resolves. + mdTypeDef *ptd) // [OUT] typedef corresponding the typeref +{ + PathString rcCorPath; // The CORPATH environment variable. + LPWSTR szCorPath; // Used to parse CORPATH. + int iLen; // Length of the directory. + PathString rcCorDir; // Buffer for the directory. + WCHAR *temp; // Used as a parsing temp. + WCHAR *szSemiCol; + + // Get the CORPATH environment variable. + if (WszGetEnvironmentVariable(W("CORPATH"), rcCorPath)) + { + NewArrayHolder<WCHAR> szCorPathHolder = rcCorPath.GetCopyOfUnicodeString(); + szCorPath = szCorPathHolder.GetValue(); + // Try each directory in the path. + for(;*szCorPath != W('\0');) + { + // Get the next directory off the path. + if ((szSemiCol = wcschr(szCorPath, W(';')))) + { + temp = szCorPath; + *szSemiCol = W('\0'); + szCorPath = szSemiCol + 1; + } + else + { + temp = szCorPath; + szCorPath += wcslen(temp); + } + + rcCorDir.Set(temp); + + // Check if we can find the class in the directory. + if (CORPATHService::GetClassFromDir(wzClassname, rcCorDir, tr, pCommon, riid, ppIScope, ptd) == S_OK) + return S_OK; + } + } + + //<TODO>These should go before the path search, but it will cause test + // some headaches right now, so we'll give them a little time to transition.</TODO> + + // Try the current directory first. + if ((iLen = WszGetCurrentDirectory( rcCorDir)) > 0 && + CORPATHService::GetClassFromDir(wzClassname, rcCorDir, tr, pCommon, riid, ppIScope, ptd) == S_OK) + { + return S_OK; + } + + // Try the app directory next. + if ((iLen = WszGetModuleFileName(NULL, rcCorDir)) > 0) + { + + if(SUCCEEDED(CopySystemDirectory(rcCorDir, rcCorDir)) && + CORPATHService::GetClassFromDir( + wzClassname, + rcCorDir, + tr, + pCommon, + riid, + ppIScope, + ptd) == S_OK) + { + return (S_OK); + } + } + + // Couldn't find the class. + return S_FALSE; +} // CORPATHService::GetClassFromCORPath + +//***************************************************************************** +// This is used in conjunction with GetClassFromCORPath. See it for details +// of the algorithm. +//***************************************************************************** +HRESULT CORPATHService::GetClassFromDir( + __in __in_z LPWSTR wzClassname, // Fully qualified class name. + __in SString& directory, // Directory to try. at most appended with a '\\' + mdTypeRef tr, // TypeRef to resolve. + IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined. + REFIID riid, + IUnknown **ppIScope, + mdTypeDef *ptd) // [OUT] typedef +{ + WCHAR *temp; // Used as a parsing temp. + int iTmp; + bool bContinue; // Flag to check if the for loop should end. + LPWSTR wzSaveClassname = NULL; // Saved offset into the class name string. + + // Process the class name appending each segment of the name to the + // directory until we find a DLL. + PathString dir; + if (!directory.EndsWith(DIRECTORY_SEPARATOR_CHAR_W)) + { + directory.Append(DIRECTORY_SEPARATOR_CHAR_W); + } + + for(;;) + { + bContinue = false; + dir.Set(directory); + + if ((temp = wcschr(wzClassname, NAMESPACE_SEPARATOR_WCHAR)) != NULL) + { + *temp = W('\0'); //terminate with null so that it can be appended + dir.Append(wzClassname); + *temp = NAMESPACE_SEPARATOR_WCHAR; //recover the '.' + + wzClassname = temp+1; + // Check if a directory by this name exists. + DWORD iAttrs = WszGetFileAttributes(dir); + if (iAttrs != 0xffffffff && (iAttrs & FILE_ATTRIBUTE_DIRECTORY)) + { + // Next element in the class spec. + bContinue = true; + wzSaveClassname = wzClassname; + } + } + else + { + dir.Append(wzClassname); + + // Advance past the class name. + iTmp = (int)wcslen(wzClassname); + wzClassname += iTmp; + } + + // Try to load the image. + dir.Append(W(".dll")); + + // OpenScope given the dll name and make sure that the class is defined in the module. + if ( SUCCEEDED( CORPATHService::FindTypeDef(dir, tr, pCommon, riid, ppIScope, ptd) ) ) + { + return (S_OK); + } + + // If we didn't find the dll, try some more. + while (*wzClassname != W('\0')) + { + // Find the length of the next class name element. + if ((temp = wcschr(wzClassname, NAMESPACE_SEPARATOR_WCHAR)) == NULL) + { + temp = wzClassname + wcslen(wzClassname); + } + + // Tack on ".element.dll" + SString::Iterator iter = dir.End(); + BOOL findperiod = dir.FindBack(iter, NAMESPACE_SEPARATOR_WCHAR); + _ASSERTE(findperiod); + iter++; + dir.Truncate(iter); + + WCHAR save = *temp; + *temp = W('\0'); + dir.Append(wzClassname); //element + *temp = save; + + // Try to load the image. + dir.Append(W(".dll")); + + // OpenScope given the dll name and make sure that the class is defined in the module. + if ( SUCCEEDED( CORPATHService::FindTypeDef(dir, tr, pCommon, riid, ppIScope, ptd) ) ) + { + return (S_OK); + } + + // Advance to the next class name element. + wzClassname = temp; + if (*wzClassname != '\0') + ++wzClassname; + } + if (bContinue) + { + + wzClassname = wzSaveClassname; + } + else + { + break; + } + } + return S_FALSE; +} // CORPATHService::GetClassFromDir + +//************************************************************* +// +// Open the file with anme wzModule and check to see if there is a type +// with namespace/class of wzNamespace/wzType. If so, return the RegMeta +// corresponding to the file and the mdTypeDef of the typedef +// +//************************************************************* +HRESULT CORPATHService::FindTypeDef( + __in __in_z LPCWSTR wzModule, // name of the module that we are going to open + mdTypeRef tr, // TypeRef to resolve. + IMetaModelCommon * pCommon, // Scope in which the TypeRef is defined. + REFIID riid, + IUnknown ** ppIScope, + mdTypeDef * ptd) // [OUT] the type that we resolve to +{ + HRESULT hr = NOERROR; + NewHolder<Disp> pDisp; + ReleaseHolder<IMetaDataImport2> pImport = NULL; + CQuickArray<mdTypeRef> cqaNesters; + CQuickArray<LPCUTF8> cqaNesterNamespaces; + CQuickArray<LPCUTF8> cqaNesterNames; + RegMeta * pRegMeta; + + _ASSERTE((ppIScope != NULL) && (ptd != NULL)); + + *ppIScope = NULL; + + pDisp = new (nothrow) Disp; + IfNullGo(pDisp); + + IfFailGo(pDisp->OpenScope(wzModule, 0, IID_IMetaDataImport2, (IUnknown **)&pImport)); + pRegMeta = static_cast<RegMeta *>(pImport.GetValue()); + + // Get the Nesting hierarchy. + IfFailGo(ImportHelper::GetNesterHierarchy(pCommon, tr, cqaNesters, + cqaNesterNamespaces, cqaNesterNames)); + + hr = ImportHelper::FindNestedTypeDef( + pRegMeta->GetMiniMd(), + cqaNesterNamespaces, + cqaNesterNames, + mdTokenNil, + ptd); + if (SUCCEEDED(hr)) + { + *ppIScope = pImport.Extract(); + } + +ErrExit: + return hr; +} // CORPATHService::FindTypeDef + +#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/md/compiler/mdutil.h b/src/md/compiler/mdutil.h new file mode 100644 index 0000000000..58cdbf108a --- /dev/null +++ b/src/md/compiler/mdutil.h @@ -0,0 +1,119 @@ +// 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. +//***************************************************************************** +// 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. + + +//********************************************************************* +// APIs to help look up TypeRef using CORPATH environment variable +//********************************************************************* +class CORPATHService +{ +public: + + static HRESULT GetClassFromCORPath( + __in __in_z LPWSTR wzClassname, // fully qualified class name + mdTypeRef tr, // TypeRef to be resolved + IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined. + REFIID riid, + IUnknown **ppIScope, + mdTypeDef *ptd); // [OUT] typedef corresponding the typeref + + static HRESULT GetClassFromDir( + __in __in_z LPWSTR wzClassname, // Fully qualified class name. + __in SString& dir, // Directory to try. + mdTypeRef tr, // TypeRef to resolve. + IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined. + REFIID riid, + IUnknown **ppIScope, + mdTypeDef *ptd); // [OUT] typedef + + static HRESULT FindTypeDef( + __in __in_z LPCWSTR wzModule, // name of the module that we are going to open + mdTypeRef tr, // TypeRef to resolve. + IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined. + REFIID riid, + IUnknown **ppIScope, + mdTypeDef *ptd ); // [OUT] the type that we resolve to +}; // class CORPATHService + + +#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT) + +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 || FEATURE_METADATA_STANDALONE_WINRT + +#endif // __MDUtil__h__ diff --git a/src/md/compiler/mdvalidator.cpp b/src/md/compiler/mdvalidator.cpp new file mode 100644 index 0000000000..adcfd51eb3 --- /dev/null +++ b/src/md/compiler/mdvalidator.cpp @@ -0,0 +1,7739 @@ +// 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. +//***************************************************************************** +// MDValidator.cpp +// + +// +// Implementation for the MetaData validator. +// Only supported for full mscorwks version. +// +//***************************************************************************** +#include "stdafx.h" + +#ifdef FEATURE_METADATA_VALIDATOR + +#include "regmeta.h" +#include "importhelper.h" +#include "pedecoder.h" +#include "stgio.h" +#include "corhost.h" +#ifdef FEATURE_FUSION +#include "fusion.h" +#endif +#include "sstring.h" +#include "nsutilpriv.h" +#include "holder.h" +#include "vererror.h" + +#include "mdsighelper.h" + +#ifdef DACCESS_COMPILE +#error Dac should be using standalone version of metadata, not Wks version. +#endif + +//----------------------------------------------------------------------------- +// Application specific debug macro. +#define IfBreakGo(EXPR) \ +do {if ((EXPR) != S_OK) IfFailGo(VLDTR_E_INTERRUPTED); } while (0) + +//----------------------------------------------------------------------------- + +//#define CACHE_IMPLMAP_VALIDATION_RESULT +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT +// To avoid multiple validation of the same thing: +struct ValidationResult +{ + mdToken tok; + HRESULT hr; +}; +ValidationResult* g_rValidated=NULL; // allocated in ValidateMetaData +unsigned g_nValidated=0; +#endif + +//----------------------------------------------------------------------------- + +#define BASE_OBJECT_CLASSNAME "Object" +#define BASE_NAMESPACE "System" +#define BASE_VTYPE_CLASSNAME "ValueType" +#define BASE_ENUM_CLASSNAME "Enum" +#define BASE_VALUE_FIELDNAME "value__" +#define BASE_CTOR_NAME ".ctor" +#define BASE_CCTOR_NAME ".cctor" +#define BASE_MCDELEGATE_CLASSNAME "MulticastDelegate" + +#define SYSTEM_OBJECT_TOSTRING_METHODNAME "ToString" +#define SYSTEM_OBJECT_GETHASHCODE_METHODNAME "GetHashCode" +#define SYSTEM_OBJECT_EQUALS_METHODNAME "Equals" + +// string ToString() +static const BYTE g_sigSystemObject_ToString[] = +{ + IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20 + 0, // 0x00 ... Param Count + ELEMENT_TYPE_STRING // 0x0e ... Return Type - string +}; + +// int GetHashCode() +static const BYTE g_sigSystemObject_GetHashCode[] = +{ + IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20 + 0, // 0x00 ... Param Count + ELEMENT_TYPE_I4 // 0x08 ... Return Type - I4 +}; + +// bool Equals(object) +static const BYTE g_sigSystemObject_Equals[] = +{ + IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20 + 1, // 0x01 ... Param Count + ELEMENT_TYPE_BOOLEAN, // 0x02 ... Return Type - bool + ELEMENT_TYPE_OBJECT // 0x1c ... Param #1 - object +}; + +// as defined in src\vm\vars.hpp +#define MAX_CLASSNAME_LENGTH 1024 +//----------------------------------------------------------------------------- +// Class names used in long form signatures (namespace is always "System") +unsigned g_NumSigLongForms = 19; +static const LPCSTR g_SigLongFormName[] = { + "String", + "______", // "Object", <REVISIT_TODO>// uncomment when EE handles ELEMENT_TYPE_OBJECT</REVISIT_TODO> + "Boolean", + "Char", + "Byte", + "SByte", + "UInt16", + "Int16", + "UInt32", + "Int32", + "UInt64", + "Int64", + "Single", + "Double", + "SysInt", // Review this. + "SysUInt", // Review this. + "SingleResult", + "Void", + "IntPtr" +}; + +// <REVISIT_TODO>: Why are these global variables?</REVISIT_TODO> +mdToken g_tkEntryPoint; +bool g_fValidatingMscorlib; +bool g_fIsDLL; + +//----------------------------------------------------------------------------- + +static HRESULT _FindClassLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdTypeDef tkParent, // [IN] the parent that ClassLayout is associated with + RID *clRid, // [OUT] rid for the ClassLayout. + RID rid); // [IN] rid to be ignored. + +static HRESULT _FindFieldLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdFieldDef tkParent, // [IN] the parent that FieldLayout is associated with + RID *flRid, // [OUT] rid for the FieldLayout record. + RID rid); // [IN] rid to be ignored. + +static BOOL _IsValidLocale(LPCUTF8 szLocale, + BOOL fIsV2Assembly); + + +#define REPORT_ERROR0(_VECode) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt)) +#define REPORT_ERROR1(_VECode, _Arg0) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0)) +#define REPORT_ERROR2(_VECode, _Arg0, _Arg1) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0, _Arg1)) +#define REPORT_ERROR3(_VECode, _Arg0, _Arg1, _Arg2) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0, _Arg1, _Arg2)) + +//***************************************************************************** +// Returns true if ixPtrTbl and ixParTbl are a valid parent-child combination +// in the pointer table scheme. +//***************************************************************************** +static inline bool IsTblPtr(ULONG ixPtrTbl, ULONG ixParTbl) +{ + if ((ixPtrTbl == TBL_Field && ixParTbl == TBL_TypeDef) || + (ixPtrTbl == TBL_Method && ixParTbl == TBL_TypeDef) || + (ixPtrTbl == TBL_Param && ixParTbl == TBL_Method) || + (ixPtrTbl == TBL_Property && ixParTbl == TBL_PropertyMap) || + (ixPtrTbl == TBL_Event && ixParTbl == TBL_EventMap)) + { + return true; + } + return false; +} // IsTblPtr() + +//***************************************************************************** +// This inline function is used to set the return hr value for the Validate +// functions to one of VLDTR_S_WRN, VLDTR_S_ERR or VLDTR_S_WRNERR based on +// the current hr value and the new success code. +// The general algorithm for error codes from the validation functions is: +// if (no warnings or errors found) +// return S_OK or S_FALSE +// else if (warnings found) +// return VLDTR_S_WRN +// else if (errors found) +// return VLDTR_S_ERR +// else if (warnings and errors found) +// return VLDTR_S_WRNERR +//***************************************************************************** +static inline void SetVldtrCode(HRESULT *phr, HRESULT successcode) +{ + _ASSERTE(successcode == S_OK || successcode == S_FALSE ||successcode == VLDTR_S_WRN || + successcode == VLDTR_S_ERR || successcode == VLDTR_S_WRNERR); + _ASSERTE(*phr == S_OK || *phr == VLDTR_S_WRN || *phr == VLDTR_S_ERR || + *phr == VLDTR_S_WRNERR); + if (successcode == S_OK || successcode == S_FALSE ||*phr == VLDTR_S_WRNERR) + return; + else if (*phr == S_OK || *phr == S_FALSE) + *phr = successcode; + else if (*phr != successcode) + *phr = VLDTR_S_WRNERR; +} // SetVldtrCode() + +//***************************************************************************** +// Initialize the Validator related structures in RegMeta. +//***************************************************************************** +HRESULT RegMeta::ValidatorInit( // S_OK or error. + DWORD dwModuleType, // [IN] Specifies whether the module is a PE file or an obj. + IUnknown *pUnk) // [IN] Validation error handler. +{ + HRESULT hr = S_OK; // Return value. + + BEGIN_ENTRYPOINT_NOTHROW; + + int i = 0; // Index into the function pointer table. + + // Initialize the array of function pointers to the validation function on + // each table. +#undef MiniMdTable +#define MiniMdTable(x) m_ValidateRecordFunctionTable[i++] = &RegMeta::Validate##x; + MiniMdTables() + + // Verify that the ModuleType passed in is a valid one. + if (dwModuleType < ValidatorModuleTypeMin || + dwModuleType > ValidatorModuleTypeMax) + { + IfFailGo(E_INVALIDARG); + } + + // Verify that the interface passed in supports IID_IVEHandler. + IfFailGo(pUnk->QueryInterface(IID_IVEHandler, (void **)&m_pVEHandler)); + + // Set the ModuleType class member. Do this last, this is used in + // ValidateMetaData to see if the validator is correctly initialized. + m_ModuleType = (CorValidatorModuleType)dwModuleType; +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::ValidatorInit() + + +//***************************************************************************** +// Public implementation for code:IMetaDataValidate::ValidateMetaData +// +// Validate the entire MetaData. Here is the basic algorithm. +// for each table +// for each record +// { +// Do generic validation - validate that the offsets into the blob +// pool are good, validate that all the rids are within range, +// validate that token encodings are consistent. +// } +// if (problems found in generic validation) +// return; +// for each table +// for each record +// Do semantic validation. +//****************************************************************************** +HRESULT RegMeta::ValidateMetaData() +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW * pMiniMd = &(m_pStgdb->m_MiniMd); + HRESULT hrSave = S_OK; // Saved hr from generic validation. + ULONG ulCount; // Count of records in the current table. + ULONG i; // Index to iterate over the tables. + ULONG j; // Index to iterate over the records in a given table. + IHostTaskManager * pHostTaskManager = NULL; + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + ULONG rValidatedSize=0; // Size of g_rValidated array +#endif + + // Verify that the validator is initialized correctly + if (m_ModuleType == ValidatorModuleTypeInvalid) + { + _ASSERTE(!"Validator not initialized, initialize with ValidatorInit()."); + IfFailGo(VLDTR_E_NOTINIT); + } + + // First do a validation pass to do some basic structural checks based on + // the Meta-Meta data. This'll validate all the offsets into the pools, + // rid value and coded token ranges. + for (i = 0; i < pMiniMd->GetCountTables(); i++) + { + ulCount = pMiniMd->GetCountRecs(i); + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + switch(i) + { + case TBL_ImplMap: + rValidatedSize += ulCount; + default: + ; + } +#endif + for (j = 1; j <= ulCount; j++) + { + IfFailGo(ValidateRecord(i, j)); + SetVldtrCode(&hrSave, hr); + } + } + // Validate that the size of the Ptr tables matches with the corresponding + // real tables. + + // Do not do semantic validation if structural validation failed. + if (hrSave != S_OK) + { + hr = hrSave; + goto ErrExit; + } + + // Verify the entry point (if any) + ::g_tkEntryPoint = 0; + ::g_fIsDLL = false; + if(m_pStgdb && m_pStgdb->m_pImage) + { + NewHolder<PEDecoder> pe; + + EX_TRY + { + // We need to use different PEDecoder constructors 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 (m_pStgdb->m_pStgIO->GetMemoryMappedType() == MTYPE_IMAGE) + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, false); + else + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, (COUNT_T)(m_pStgdb->m_dwImageSize)); + + hr = S_OK; + } + EX_CATCH + { + hr = COR_E_BADIMAGEFORMAT; + } + EX_END_CATCH(SwallowAllExceptions) + + if (SUCCEEDED(hr) && pe == NULL) + IfFailGo(E_OUTOFMEMORY); + + if(FAILED(hr) || !pe->CheckFormat()) + { + VEContext veCtxt; // Context structure. + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR0(COR_E_BADIMAGEFORMAT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if(!pe->IsILOnly()) + { + VEContext veCtxt; // Context structure. + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR0(VER_E_BAD_PE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if((pe->GetCorHeader()->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0) + g_tkEntryPoint = pe->GetEntryPointToken(); + g_fIsDLL = pe->IsDll() ? true : false; + + if(g_tkEntryPoint) + { + RID rid = RidFromToken(g_tkEntryPoint); + RID maxrid = 0; + switch(TypeFromToken(g_tkEntryPoint)) + { + case mdtMethodDef: maxrid = pMiniMd->getCountMethods(); break; + case mdtFile: maxrid = pMiniMd->getCountFiles(); break; + default: break; + } + if((rid == 0)||(rid > maxrid)) + { + VEContext veCtxt; // Context structure. + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = g_tkEntryPoint; + veCtxt.uOffset = 0; + REPORT_ERROR0(VLDTR_E_EP_BADTOKEN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(!g_fIsDLL) // exe must have an entry point + { + VEContext veCtxt; // Context structure. + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = g_tkEntryPoint; + veCtxt.uOffset = 0; + REPORT_ERROR0(VLDTR_E_EP_BADTOKEN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + g_fValidatingMscorlib = false; + if(pMiniMd->GetCountRecs(TBL_Assembly)) + { + AssemblyRec *pRecord; + IfFailGo(pMiniMd->GetAssemblyRecord(1, &pRecord)); + LPCSTR szName; + IfFailGo(pMiniMd->getNameOfAssembly(pRecord, &szName)); + g_fValidatingMscorlib = (0 == SString::_stricmp(szName,"mscorlib")); + } + // Verify there are no circular class hierarchies. + + // Do per record semantic validation on the MetaData. The function + // pointers to the per record validation are stored in the table by the + // ValidatorInit() function. + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + g_rValidated = NULL; + ::g_nValidated = 0; + if (rValidatedSize) + { + g_rValidated = new(nothrow) ValidationResult[rValidatedSize]; + IfNullGo(g_rValidated); + } +#endif + pHostTaskManager = CorHost2::GetHostTaskManager(); + +#ifdef Sleep +#undef Sleep +#endif + //DWORD cBegin=0,cEnd=0; + for (i = 0; i < pMiniMd->GetCountTables(); i++) + { + ulCount = pMiniMd->GetCountRecs(i); + //cBegin = GetTickCount(); + for (j = 1; j <= ulCount; j++) + { + IfFailGo((this->*m_ValidateRecordFunctionTable[i])(j)); + SetVldtrCode(&hrSave, hr); + if(pHostTaskManager) + { + // SwitchToTask forces the current thread to give up quantum, while a host can decide what + // to do with Sleep if the current thread has not run out of quantum yet. + ClrSleepEx(0, FALSE); + } + } + //cEnd = GetTickCount(); + //printf("Table %d, recs: %d, time: %d\n",i,ulCount,(cEnd-cBegin)); + } + hr = hrSave; +ErrExit: + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + if(g_rValidated) delete [] g_rValidated; +#endif + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMetaData() + +//***************************************************************************** +// Validate the Module record. +//***************************************************************************** +HRESULT RegMeta::ValidateModule(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ModuleRec *pRecord; // Module record. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + LPCSTR szName; + GUID GuidOfModule; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the Module record. + veCtxt.Token = TokenFromRid(rid, mdtModule); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetModuleRecord(rid, &pRecord)); + + // There can only be one Module record. + if (rid > 1) + { + REPORT_ERROR0(VLDTR_E_MOD_MULTI); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Verify the name + IfFailGo(pMiniMd->getNameOfModule(pRecord, &szName)); + if(szName && *szName) + { + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(strchr(szName,':') || strchr(szName,'\\')) + { + REPORT_ERROR0(VLDTR_E_MOD_NAMEFULLQLFD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + REPORT_ERROR0(VLDTR_E_MOD_NONAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Verify that the MVID is valid. + IfFailGo(pMiniMd->getMvidOfModule(pRecord, &GuidOfModule)); + if (GuidOfModule == GUID_NULL) + { + REPORT_ERROR0(VLDTR_E_MOD_NULLMVID); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateModule() + +//***************************************************************************** +// Validate the given TypeRef. +//***************************************************************************** +HRESULT RegMeta::ValidateTypeRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + TypeRefRec *pRecord; // TypeRef record. + mdToken tkRes; // Resolution scope. + LPCSTR szNamespace; // TypeRef Namespace. + LPCSTR szName; // TypeRef Name. + mdTypeRef tkTypeRef; // Duplicate TypeRef. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the TypeRef record. + veCtxt.Token = TokenFromRid(rid, mdtTypeRef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetTypeRefRecord(rid, &pRecord)); + + // Check name is not NULL. + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pRecord, &szNamespace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pRecord, &szName)); + if (!*szName) + { + REPORT_ERROR0(VLDTR_E_TR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + RID ridScope; + // Look for a Duplicate, this function reports only one duplicate. + tkRes = pMiniMd->getResolutionScopeOfTypeRef(pRecord); + hr = ImportHelper::FindTypeRefByName(pMiniMd, tkRes, szNamespace, szName, &tkTypeRef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_TR_DUP, tkTypeRef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + ULONG L = (ULONG)(strlen(szName)+strlen(szNamespace)); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + ridScope = RidFromToken(tkRes); + if(ridScope) + { + bool badscope = true; + //check if valid scope + switch(TypeFromToken(tkRes)) + { + case mdtAssemblyRef: + case mdtModuleRef: + case mdtModule: + case mdtTypeRef: + badscope = !IsValidToken(tkRes); + break; + default: + break; + } + if(badscope) + { + REPORT_ERROR1(VLDTR_E_TR_BADSCOPE, tkTypeRef); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + else + { + // check if there is a ExportedType + //hr = ImportHelper::FindExportedType(pMiniMd, szNamespace, szName, tkImpl, &tkExportedType, rid); + } + // Check if there is TypeDef with the same name + if(!ridScope) + { + if((TypeFromToken(tkRes) != mdtTypeRef) && + (S_OK == ImportHelper::FindTypeDefByName(pMiniMd, szNamespace, szName, mdTokenNil,&tkTypeRef, 0))) + { + REPORT_ERROR1(VLDTR_E_TR_HASTYPEDEF, tkTypeRef); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateTypeRef() + +//***************************************************************************** +// Validate the given TypeDef. +//***************************************************************************** +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::ValidateTypeDef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + TypeDefRec *pRecord; // TypeDef record. + TypeDefRec *pExtendsRec = 0; // TypeDef record for the parent class. + mdTypeDef tkTypeDef; // Duplicate TypeDef token. + DWORD dwFlags; // TypeDef flags. + DWORD dwExtendsFlags; // TypeDef flags of the parent class. + LPCSTR szName; // TypeDef Name. + LPCSTR szNameSpace; // TypeDef NameSpace. + LPCSTR szExtName = NULL; // Parent Name. + LPCSTR szExtNameSpace = NULL; // Parent NameSpace. + CQuickBytes qb; // QuickBytes for flexible allocation. + mdToken tkExtends; // TypeDef of the parent class. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkEncloser=mdTokenNil; // Encloser, if any + BOOL bIsEnum,bExtendsEnum,bExtendsVType,bIsVType,bExtendsObject,bIsObject,bExtendsMCDelegate; + BOOL bHasMethods=FALSE, bHasFields=FALSE; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Skip validating m_tdModule class. + if (rid == RidFromToken(m_tdModule)) + goto ErrExit; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the TypeDef record. + veCtxt.Token = TokenFromRid(rid, mdtTypeDef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetTypeDefRecord(rid, &pRecord)); + + // Do checks for name validity.. + IfFailGo(pMiniMd->getNameOfTypeDef(pRecord, &szName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pRecord, &szNameSpace)); + if (!*szName) + { + // TypeDef Name is null. + REPORT_ERROR0(VLDTR_E_TD_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (!IsDeletedName(szName)) + { + RID iRecord; + IfFailGo(pMiniMd->FindNestedClassHelper(TokenFromRid(rid, mdtTypeDef), &iRecord)); + + if (InvalidRid(iRecord)) + { + tkEncloser = mdTokenNil; + } + else + { + NestedClassRec *pNestedClassRec; + IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pNestedClassRec)); + tkEncloser = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec); + } + + // Check for duplicates based on Name/NameSpace. Do not do Dup checks + // on deleted records. + hr = ImportHelper::FindTypeDefByName(pMiniMd, szNameSpace, szName, tkEncloser, + &tkTypeDef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_TD_DUPNAME, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + ULONG L = (ULONG)(strlen(szName)+strlen(szNameSpace)); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the flag value for the TypeDef. + dwFlags = pMiniMd->getFlagsOfTypeDef(pRecord); + // Do semantic checks on the flags. + // RTSpecialName bit must be set on Deleted records. + if (IsDeletedName(szName)) + { + if(!IsTdRTSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_DLTNORTSPCL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + hr = hrSave; + goto ErrExit; + } + + // If RTSpecialName bit is set, the record must be a Deleted record. + if (IsTdRTSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_RTSPCLNOTDLT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + if(!IsTdSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_RTSPCLNOTSPCL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Check if flag value is valid + { + DWORD dwInvalidMask, dwExtraBits; + dwInvalidMask = (DWORD)~(tdVisibilityMask | tdLayoutMask | tdClassSemanticsMask | + tdAbstract | tdSealed | tdSpecialName | tdImport | tdSerializable | tdWindowsRuntime | + tdStringFormatMask | tdBeforeFieldInit | tdReservedMask); + // check for extra bits + dwExtraBits = dwFlags & dwInvalidMask; + if (dwExtraBits == 0) + { + // if no extra bits, check layout + dwExtraBits = dwFlags & tdLayoutMask; + if (dwExtraBits != tdLayoutMask) + { + // layout OK, check string format + dwExtraBits = dwFlags & tdStringFormatMask; + if (dwExtraBits != tdStringFormatMask) + dwExtraBits = 0; + } + } + if (dwExtraBits != 0) + { + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + + // Generic types may be specified to have only AutoLayout or SequentialLayout (never ExplicitLayout). + if (IsTdExplicitLayout(dwFlags)) + { + HENUMInternal hEnumTyPars; + ULONG ulTypeDefArity; + hr = pMiniMd->FindGenericParamHelper(TokenFromRid(rid, mdtTypeDef), &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulTypeDefArity)); + HENUMInternal::ClearEnum(&hEnumTyPars); + if (ulTypeDefArity != 0) + { + REPORT_ERROR0(VLDTR_E_TD_GENERICHASEXPLAYOUT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + } + + // Get the parent of the TypeDef. + tkExtends = pMiniMd->getExtendsOfTypeDef(pRecord); + + // Check if TypeDef extends itself + if (tkExtends == veCtxt.Token) + { + REPORT_ERROR0(VLDTR_E_TD_EXTENDSITSELF); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check if TypeDef extends one of its children + if (RidFromToken(tkExtends)&&(TypeFromToken(tkExtends)==mdtTypeDef)) + { + TypeDefRec *pRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pRec)); + mdToken tkExtends2 = pMiniMd->getExtendsOfTypeDef(pRec); + if( tkExtends2 == veCtxt.Token) + { + REPORT_ERROR0(VLDTR_E_TD_EXTENDSCHILD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + + + if (IsNilToken(tkEncloser) == IsTdNested(dwFlags)) + { + REPORT_ERROR0(IsNilToken(tkEncloser) ? VLDTR_E_TD_NESTEDNOENCL : VLDTR_E_TD_ENCLNOTNESTED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + bIsObject = bIsEnum = bIsVType = FALSE; + if(0 == strcmp(szNameSpace,BASE_NAMESPACE)) + { + bIsObject = (0 == strcmp(szName,BASE_OBJECT_CLASSNAME)); + if(!bIsObject) + { + bIsEnum = (0 == strcmp(szName,BASE_ENUM_CLASSNAME)); + if(!bIsEnum) + { + bIsVType = (0 == strcmp(szName,BASE_VTYPE_CLASSNAME)); + } + } + } + + if (IsNilToken(tkExtends)) + { + // If the parent token is nil, the class must be marked Interface, + // unless it's the System.Object class. + if ( !(bIsObject || IsTdInterface(dwFlags))) + { + REPORT_ERROR0(VLDTR_E_TD_NOTIFACEOBJEXTNULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + szExtName = ""; + szExtNameSpace = ""; + } + else + { + + // If tkExtends is a TypeSpec, extract the generic type and continue + if (TypeFromToken(tkExtends) == mdtTypeSpec) + { + //@GENERICSVER: TODO first validate the spec + + TypeSpecRec *pRec; + IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(tkExtends), &pRec)); + PCCOR_SIGNATURE pSig; + ULONG cSig; + + IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, &pSig, &cSig)); + + switch(CorSigUncompressElementType(pSig)) + { + default: + { + REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + szExtName = ""; + szExtNameSpace = ""; + break; + } + case ELEMENT_TYPE_GENERICINST: + { + switch(CorSigUncompressElementType(pSig)) + { + default: + { + REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + szExtName = ""; + szExtNameSpace = ""; + break; + } + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + tkExtends = CorSigUncompressToken(pSig); + break; + } + } + } + } + } + + // If tkExtends is a TypeRef try to resolve it to a corresponding + // TypeDef. If it resolves successfully, issue a warning. It means + // that the Ref to Def optimization didn't happen successfully. + if (TypeFromToken(tkExtends) == mdtTypeRef) + { + TypeRefRec *pTypeRefRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pTypeRefRec)); + + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szExtNameSpace)); + + BOOL fLookForDef = TRUE; + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); + if (TypeFromToken(tkResScope) == mdtAssemblyRef) + { // We will look for the TypeDef of the same name, only if the AssemblyRef has the same name as AssemblyDef + fLookForDef = FALSE; + RID ridResScope = RidFromToken(tkResScope); + if ((ridResScope > 0) && (ridResScope <= pMiniMd->GetCountRecs(TBL_AssemblyRef))) + { + if (pMiniMd->GetCountRecs(TBL_Assembly) > 0) + { + AssemblyRefRec * pAsmRefRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(ridResScope, &pAsmRefRec)); + AssemblyRec *pAsmRec; + IfFailGo(pMiniMd->GetAssemblyRecord(1, &pAsmRec)); + if ((pAsmRec != NULL) && (pAsmRefRec != NULL)) + { + LPCUTF8 szAsmName; + IfFailGo(pMiniMd->getNameOfAssembly(pAsmRec, &szAsmName)); + LPCUTF8 szAsmRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pAsmRefRec, &szAsmRefName)); + if ((szAsmName != NULL) && (szAsmRefName != NULL)) + fLookForDef = (strcmp(szAsmName,szAsmRefName) == 0); + } + } + } + } + + if (fLookForDef) + { + mdTypeDef tkResTd; + + if (ImportHelper::FindTypeDefByName(pMiniMd, + szExtNameSpace, + szExtName, + tkResScope, + &tkResTd) == S_OK) + { + // Ref to Def optimization is not expected to happen for Obj files. + /* + if (m_ModuleType != ValidatorModuleTypeObj) + { + REPORT_ERROR2(VLDTR_E_TD_EXTTRRES, tkExtends, tkResTd); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + */ + + // Set tkExtends to the new TypeDef, so we can continue + // with the validation. + tkExtends = tkResTd; + } + } + } + + // Continue validation, even for the case where TypeRef got resolved + // to a corresponding TypeDef in the same Module. + if (TypeFromToken(tkExtends) == mdtTypeDef) + { + // Extends must not be sealed. + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtendsRec)); + dwExtendsFlags = pMiniMd->getFlagsOfTypeDef(pExtendsRec); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtendsRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtendsRec, &szExtNameSpace)); + if (IsTdSealed(dwExtendsFlags)) + { + REPORT_ERROR1(VLDTR_E_TD_EXTENDSSEALED, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if (IsTdInterface(dwExtendsFlags)) + { + REPORT_ERROR1(VLDTR_E_TD_EXTENDSIFACE, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(TypeFromToken(tkExtends) == mdtTypeSpec) + { + //If we got here, the instantiated generic type is itself a type spec, which is illegal + REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + szExtName = ""; + szExtNameSpace = ""; + + } + // If the parent token is non-null, the class must not be System.Object. + if (bIsObject) + { + REPORT_ERROR1(VLDTR_E_TD_OBJEXTENDSNONNULL, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + bExtendsObject = bExtendsEnum = bExtendsVType = bExtendsMCDelegate = FALSE; + if(0 == strcmp(szExtNameSpace,BASE_NAMESPACE)) + { + bExtendsObject = (0 == strcmp(szExtName,BASE_OBJECT_CLASSNAME)); + if(!bExtendsObject) + { + bExtendsEnum = (0 == strcmp(szExtName,BASE_ENUM_CLASSNAME)); + if(!bExtendsEnum) + { + bExtendsVType = (0 == strcmp(szExtName,BASE_VTYPE_CLASSNAME)); + if(!bExtendsVType) + { + bExtendsMCDelegate = (0 == strcmp(szExtName,BASE_MCDELEGATE_CLASSNAME)); + } + } + } + } + + // System.ValueType must extend System.Object + if(bIsVType && !bExtendsObject) + { + REPORT_ERROR0(VLDTR_E_TD_SYSVTNOTEXTOBJ); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Validate rules for interface. Some of the VOS rules are verified as + // part of the validation for the corresponding Methods, fields etc. + if (IsTdInterface(dwFlags)) + { + // Interface type must be marked abstract. + if (!IsTdAbstract(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_IFACENOTABS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Interface must not be sealed + if(IsTdSealed(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_IFACESEALED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Interface must have parent Nil token. + if (!IsNilToken(tkExtends)) + { + REPORT_ERROR1(VLDTR_E_TD_IFACEPARNOTNIL, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + //Interface must have only static fields -- checked in ValidateField + //Interface must have only public fields -- checked in ValidateField + //Interface must have only abstract or static methods -- checked in ValidateMethod + //Interface must have only public methods -- checked in ValidateMethod + + // Interface must have GUID + /* + if (*pGuid == GUID_NULL) + { + REPORT_ERROR0(VLDTR_E_TD_IFACEGUIDNULL); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + */ + } + + + // Class must have valid method and field lists + { + ULONG ridStart,ridEnd; + ridStart = pMiniMd->getMethodListOfTypeDef(pRecord); + ridEnd = pMiniMd->getCountMethods() + 1; + if(ridStart > ridEnd) + { + REPORT_ERROR0(VLDTR_E_TD_BADMETHODLST); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(rid, &ridEnd)); + bHasMethods = (ridStart && (ridStart < ridEnd)); + } + + ridStart = pMiniMd->getFieldListOfTypeDef(pRecord); + ridEnd = pMiniMd->getCountFields() + 1; + if(ridStart > ridEnd) + { + REPORT_ERROR0(VLDTR_E_TD_BADFIELDLST); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + IfFailGo(pMiniMd->getEndFieldListOfTypeDef(rid, &ridEnd)); + bHasFields = (ridStart && (ridStart < ridEnd)); + } + } + + // Validate rules for System.Enum + if(bIsEnum) + { + if(!IsTdClass(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_SYSENUMNOTCLASS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!bExtendsVType) + { + REPORT_ERROR0(VLDTR_E_TD_SYSENUMNOTEXTVTYPE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + if(bExtendsVType || bExtendsEnum) + { + // ValueTypes and Enums must be sealed + if(!IsTdSealed(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_VTNOTSEAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Value class must have fields or size + if(!bHasFields) + { + ULONG ulClassSize = 0; + ClassLayoutRec *pRec; + RID ridClassLayout; + IfFailGo(pMiniMd->FindClassLayoutHelper(TokenFromRid(rid, mdtTypeDef), &ridClassLayout)); + + if (!InvalidRid(ridClassLayout)) + { + IfFailGo(pMiniMd->GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec)); + ulClassSize = pMiniMd->getClassSizeOfClassLayout(pRec); + } + if(ulClassSize == 0) + { + REPORT_ERROR0(VLDTR_E_TD_VTNOSIZE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else if(bExtendsMCDelegate) + { + // Delegates must be sealed + if(!IsTdSealed(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_VTNOTSEAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + // Enum-related checks + if (bExtendsEnum) + { + { + PCCOR_SIGNATURE pValueSig = NULL; + ULONG cbValueSig = 0; + mdFieldDef tkValueField=0, tkField, tkValue__Field = 0; + ULONG ridStart,ridEnd,index; + FieldRec *pFieldRecord; // Field record. + DWORD dwRecordFlags, dwTally, dwValueFlags, dwValue__Flags = 0; + RID ridField,ridValue=0,ridValue__ = 0; + + ridStart = pMiniMd->getFieldListOfTypeDef(pRecord); + IfFailGo(pMiniMd->getEndFieldListOfTypeDef(rid, &ridEnd)); + // check the instance (value__) field(s) + dwTally = 0; + for (index = ridStart; index < ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetFieldRid(index, &ridField)); + IfFailGo(pMiniMd->GetFieldRecord(ridField, &pFieldRecord)); + dwRecordFlags = pFieldRecord->GetFlags(); + if(!IsFdStatic(dwRecordFlags)) + { + dwTally++; + if(ridValue == 0) + { + ridValue = ridField; + tkValueField = TokenFromRid(ridField, mdtFieldDef); + IfFailGo(pMiniMd->getSignatureOfField(pFieldRecord, &pValueSig, &cbValueSig)); + dwValueFlags = dwRecordFlags; + } + } + LPCSTR szFieldName; + IfFailGo(pMiniMd->getNameOfField(pFieldRecord, &szFieldName)); + if(!strcmp(szFieldName, BASE_VALUE_FIELDNAME)) + { + ridValue__ = ridField; + dwValue__Flags = dwRecordFlags; + tkValue__Field = TokenFromRid(ridField, mdtFieldDef); + } + } + // Enum must have one (and only one) inst.field + if(dwTally == 0) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMNOINSTFLD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if(dwTally > 1) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMMULINSTFLD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // inst.field name must be "value__" (CLS) + if(ridValue__ == 0) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMNOVALUE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + else + { + // if "value__" field is present ... + // ... it must be 1st instance field + if(ridValue__ != ridValue) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMVALNOT1ST, tkValue__Field); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ... it must not be static + if(IsFdStatic(dwValue__Flags)) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMVALSTATIC, tkValue__Field); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ... it must be fdRTSpecialName + if(!IsFdRTSpecialName(dwValue__Flags)) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMVALNOTSN, tkValueField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ... its type must be integral + if(cbValueSig && pValueSig) + { + //ULONG ulCurByte = CorSigUncompressedDataSize(pValueSig); + //CorSigUncompressData(pValueSig); + //ULONG ulElemSize,ulElementType; + //ulCurByte += (ulElemSize = CorSigUncompressedDataSize(pValueSig)); + //ulElementType = CorSigUncompressData(pValueSig); + //switch (ulElementType) + BYTE* pB = (BYTE*)pValueSig; + pB++; // skip the calling convention + while((*pB == ELEMENT_TYPE_CMOD_OPT)|| + (*pB == ELEMENT_TYPE_CMOD_REQD)) + { + mdToken tok; + pB++; // move from E_T_... to compressed token + pB += CorSigUncompressToken((PCOR_SIGNATURE)pB,&tok); + } + switch(*pB) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + break; + default: + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDBADTYPE, tkValue__Field); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + // check all the fields + dwTally = 0; + for (index = ridStart; index < ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetFieldRid(index, &ridField)); + if(ridField == ridValue) continue; + IfFailGo(pMiniMd->GetFieldRecord(ridField, &pFieldRecord)); + LPCSTR szFieldName; + IfFailGo(pMiniMd->getNameOfField(pFieldRecord, &szFieldName)); + if(IsFdRTSpecialName(pFieldRecord->GetFlags()) + && IsDeletedName(szFieldName)) continue; + dwTally++; + tkField = TokenFromRid(ridField, mdtFieldDef); + if(!IsFdStatic(pFieldRecord->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDNOTST, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsFdLiteral(pFieldRecord->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDNOTLIT, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + /* + IfFailGo(pMiniMd->getSignatureOfField(pFieldRecord, &pvSigTmp, &cbSig)); + if(!(pvSigTmp && (cbSig==cbValueSig) &&(memcmp(pvSigTmp,pValueSig,cbSig)==0))) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDSIGMISMATCH, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + */ + } + if(dwTally == 0) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMNOLITFLDS); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + // Enum must have no methods + if (bHasMethods) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMHASMETHODS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Enum must implement no interfaces + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountInterfaceImpls() + 1; + ULONG index; + for (index = ridStart; index < ridEnd; index ++ ) + { + InterfaceImplRec *pInterfaceImplRecord; + IfFailGo(pMiniMd->GetInterfaceImplRecord(index, &pInterfaceImplRecord)); + if (veCtxt.Token == pMiniMd->getClassOfInterfaceImpl(pInterfaceImplRecord)) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMIMPLIFACE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + // Enum must have no properties + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountPropertys() + 1; + ULONG index; + mdToken tkClass; + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->FindParentOfPropertyHelper(index | mdtProperty, &tkClass)); + if (veCtxt.Token == tkClass) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMHASPROP); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + // Enum must have no events + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountEvents() + 1; + ULONG index; + mdToken tkClass; + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->FindParentOfEventHelper(index | mdtEvent, &tkClass)); + if (veCtxt.Token == tkClass) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMHASEVENT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + } // end if(bExtendsEnum) + // Class having security must be marked tdHasSecurity and vice versa + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + ULONG index; + BOOL bHasSecurity = FALSE; + for (index = ridStart; index < ridEnd; index ++ ) + { + DeclSecurityRec *pDeclSecurityRecord; + IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord)); + if (veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord)) + { + bHasSecurity = TRUE; + break; + } + } + if (!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute" + { + bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token, + "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL)); + } + if(bHasSecurity != (IsTdHasSecurity(pRecord->GetFlags())!=0)) + { + REPORT_ERROR0(bHasSecurity ? VLDTR_E_TD_SECURNOTMARKED : VLDTR_E_TD_MARKEDNOSECUR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateTypeDef() +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +// Validate the given FieldPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateFieldPtr() + + +//***************************************************************************** +// Validate the given Field. +//***************************************************************************** +HRESULT RegMeta::ValidateField(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + FieldRec *pRecord; // Field record. + mdTypeDef tkTypeDef; // Parent TypeDef token. + mdFieldDef tkFieldDef; // Duplicate FieldDef token. + LPCSTR szName; // FieldDef name. + PCCOR_SIGNATURE pbSig; // FieldDef signature. + ULONG cbSig; // Signature size in bytes. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL bIsValueField; + BOOL bIsGlobalField = FALSE; + BOOL bHasValidRVA = FALSE; + DWORD dwInvalidFlags; + DWORD dwFlags; + RID tempRid; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the FieldDef record. + veCtxt.Token = TokenFromRid(rid, mdtFieldDef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetFieldRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfField(pRecord, &szName)); + if (!*szName) + { + // Field name is NULL. + REPORT_ERROR0(VLDTR_E_FD_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + bIsValueField = (strcmp(szName,BASE_VALUE_FIELDNAME)==0); + // If field is RTSpecialName, its name must be 'value__' and vice versa + if((IsFdRTSpecialName(pRecord->GetFlags())!=0) != bIsValueField) + { + REPORT_ERROR1(bIsValueField ? VLDTR_E_TD_ENUMVALNOTSN : VLDTR_E_FD_NOTVALUERTSN, veCtxt.Token); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate flags + dwFlags = pRecord->GetFlags(); + dwInvalidFlags = ~(fdFieldAccessMask | fdStatic | fdInitOnly | fdLiteral | fdNotSerialized | fdSpecialName + | fdPinvokeImpl | fdReservedMask); + if(dwFlags & dwInvalidFlags) + { + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwFlags & dwInvalidFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate access + if((dwFlags & fdFieldAccessMask) == fdFieldAccessMask) + { + REPORT_ERROR0(VLDTR_E_FMD_BADACCESSFLAG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Literal : Static, !InitOnly + if(IsFdLiteral(dwFlags)) + { + if(IsFdInitOnly(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_INITONLYANDLITERAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_LITERALNOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsFdHasDefault(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_LITERALNODEFAULT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // RTSpecialName => SpecialName + if(IsFdRTSpecialName(dwFlags) && !IsFdSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_RTSNNOTSN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate Field signature. + IfFailGo(pMiniMd->getSignatureOfField(pRecord, &pbSig, &cbSig)); + IfFailGo(ValidateFieldSig(TokenFromRid(rid, mdtFieldDef), pbSig, cbSig)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + + // Validate Field RVA + if(IsFdHasFieldRVA(dwFlags)) + { + ULONG iFieldRVARid; + IfFailGo(pMiniMd->FindFieldRVAHelper(TokenFromRid(rid, mdtFieldDef), &iFieldRVARid)); + if((iFieldRVARid==0) || (iFieldRVARid > pMiniMd->getCountFieldRVAs())) + { + REPORT_ERROR0(VLDTR_E_FD_RVAHASNORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + /* + FieldRVARec *pRVARec; + IfFailGo(pMiniMd->GetFieldRVARecord(iFieldRVARid, &pRVARec)); + if(pRVARec->GetRVA() == 0) + { + REPORT_ERROR0(VLDTR_E_FD_RVAHASZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + */ + bHasValidRVA = TRUE; + } + } + + // Get the parent of the Field. + IfFailGo(pMiniMd->FindParentOfFieldHelper(TokenFromRid(rid, mdtFieldDef), &tkTypeDef)); + // Validate that the parent is not nil. + if (IsNilToken(tkTypeDef)) + { + REPORT_ERROR0(VLDTR_E_FD_PARNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (RidFromToken(tkTypeDef) != RidFromToken(m_tdModule)) + { + if(IsValidToken(tkTypeDef) && (TypeFromToken(tkTypeDef) == mdtTypeDef)) + { + TypeDefRec *pParentRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pParentRec)); + // If the name is "value__" ... + if(bIsValueField) + { + // parent must be Enum + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pParentRec); + RID ridExtends = RidFromToken(tkExtends); + LPCSTR szExtName="",szExtNameSpace=""; + if(ridExtends) + { + if(TypeFromToken(tkExtends) == mdtTypeRef) + { + TypeRefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeRefRecord(ridExtends, &pExtRec)); + IfFailGo(pMiniMd->getNameOfTypeRef(pExtRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtRec, &szExtNameSpace)); + } + else if(TypeFromToken(tkExtends) == mdtTypeDef) + { + TypeDefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeDefRecord(ridExtends, &pExtRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtRec, &szExtNameSpace)); + } + } + if(strcmp(szExtName,BASE_ENUM_CLASSNAME) || strcmp(szExtNameSpace,BASE_NAMESPACE)) + { + REPORT_ERROR0(VLDTR_E_FD_VALUEPARNOTENUM); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // field must be instance - checked in ValidateTypeDef + // must be no other instance fields - checked in ValidateTypeDef + // must be first field - checked in ValidateTypeDef + // must be RTSpecialName -- checked in ValidateTypeDef + } + if(IsTdInterface(pMiniMd->getFlagsOfTypeDef(pParentRec))) + { + // Fields in interface are not CLS compliant + REPORT_ERROR0(VLDTR_E_FD_FLDINIFACE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + + // If field is not static, verify parent is not interface. + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_INSTINIFACE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + // If field is not public, verify parent is not interface. + if(!IsFdPublic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_NOTPUBINIFACE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } // end if Valid and TypeDef + else + { + REPORT_ERROR1(VLDTR_E_FD_BADPARENT, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else // i.e. if (RidFromToken(tkTypeDef) == RidFromToken(m_tdModule)) + { + bIsGlobalField = TRUE; + // Globals are not CLS-compliant + REPORT_ERROR0(VLDTR_E_FMD_GLOBALITEM); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + // Validate global field: + // Must be static + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_GLOBALNOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must have a non-zero RVA + /* + if(!bHasValidRVA) + { + REPORT_ERROR0(VLDTR_E_FD_GLOBALNORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + */ + } + + // Check for duplicates, except global fields with PrivateScope. + if (*szName && cbSig && !IsFdPrivateScope(dwFlags)) + { + hr = ImportHelper::FindField(pMiniMd, tkTypeDef, szName, pbSig, cbSig, &tkFieldDef, rid); + if (hr == S_OK) + { + if(!IsFdPrivateScope(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_FD_DUP, tkFieldDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else hr = S_OK; + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + { + hr = S_OK; + } + else + { + IfFailGo(hr); + } + } + // Field having security must be marked fdHasSecurity and vice versa + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + ULONG index; + BOOL bHasSecurity = FALSE; + for (index = ridStart; index < ridEnd; index ++ ) + { + DeclSecurityRec *pDeclSecurityRecord; + IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord)); + if ( veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord)) + { + bHasSecurity = TRUE; + break; + } + } + if(!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute" + { + bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token, + "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL)); + } + if(bHasSecurity) + { + REPORT_ERROR0(VLDTR_E_FMD_SECURNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Field having marshaling must be marked fdHasFieldMarshal and vice versa + IfFailGo(pMiniMd->FindFieldMarshalHelper(veCtxt.Token, &tempRid)); + if (InvalidRid(tempRid) == (IsFdHasFieldMarshal(dwFlags) != 0)) + { + REPORT_ERROR0(IsFdHasFieldMarshal(dwFlags)? VLDTR_E_FD_MARKEDNOMARSHAL : VLDTR_E_FD_MARSHALNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Field having const value must be marked fdHasDefault and vice versa + IfFailGo(pMiniMd->FindConstantHelper(veCtxt.Token, &tempRid)); + if(InvalidRid(tempRid) == (IsFdHasDefault(dwFlags) != 0)) + { + REPORT_ERROR0(IsFdHasDefault(dwFlags)? VLDTR_E_FD_MARKEDNODEFLT : VLDTR_E_FD_DEFLTNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check the field's impl.map + { + ULONG iRecord; + IfFailGo(pMiniMd->FindImplMapHelper(veCtxt.Token, &iRecord)); + if(IsFdPinvokeImpl(dwFlags)) + { + // must be static + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // must have ImplMap + if (InvalidRid(iRecord)) + { + REPORT_ERROR0(VLDTR_E_FMD_MARKEDNOPINVOKE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + // must have no ImplMap + if (!InvalidRid(iRecord)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if (!InvalidRid(iRecord)) + { + hr = ValidateImplMap(iRecord); + if(hr != S_OK) + { + REPORT_ERROR0(VLDTR_E_FMD_BADIMPLMAP); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateField() + +//***************************************************************************** +// Validate the given MethodPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateMethodPtr() + + +//***************************************************************************** +// Validate the given Method. +//***************************************************************************** +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::ValidateMethod(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + MethodRec *pRecord = NULL; // Method record. + mdTypeDef tkTypeDef; // Parent TypeDef token. + mdMethodDef tkMethodDef; // Duplicate MethodDef token. + LPCSTR szName; // MethodDef name. + DWORD dwFlags = 0; // Method flags. + DWORD dwImplFlags = 0; // Method impl.flags. + PCCOR_SIGNATURE pbSig; // MethodDef signature. + ULONG cbSig; // Signature size in bytes. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL bIsCtor=FALSE; + BOOL bIsCctor=FALSE; + BOOL bIsGlobal=FALSE; + BOOL bIsParentImport = FALSE; + BOOL bIsGeneric = FALSE; + unsigned retType; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the MethodDef record. + veCtxt.Token = TokenFromRid(rid, mdtMethodDef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetMethodRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfMethod(pRecord, &szName)); + if (!*szName) + { + // Method name is NULL. + REPORT_ERROR0(VLDTR_E_MD_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; + bIsCtor = (0 == strcmp(szName,BASE_CTOR_NAME)); + bIsCctor = (0 == strcmp(szName,BASE_CCTOR_NAME)); + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the parent, flags and signature of the Method. + IfFailGo(pMiniMd->FindParentOfMethodHelper(TokenFromRid(rid, mdtMethodDef), &tkTypeDef)); + dwFlags = pMiniMd->getFlagsOfMethod(pRecord); + dwImplFlags = pMiniMd->getImplFlagsOfMethod(pRecord); + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + + // Check for duplicates. + if (*szName && cbSig && !IsNilToken(tkTypeDef) && !IsMdPrivateScope(dwFlags)) + { + hr = ImportHelper::FindMethod(pMiniMd, tkTypeDef, szName, pbSig, cbSig, &tkMethodDef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MD_DUP, tkMethodDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + // No further error checking for VtblGap methods. + if (IsVtblGapName(szName)) + { + hr = hrSave; + goto ErrExit; + } + + // Validate Method signature. + IfFailGo(ValidateMethodSig(TokenFromRid(rid, mdtMethodDef), pbSig, cbSig, + dwFlags)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + + // Validate that the parent is not nil. + if (IsNilToken(tkTypeDef)) + { + REPORT_ERROR0(VLDTR_E_MD_PARNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (RidFromToken(tkTypeDef) != RidFromToken(m_tdModule)) + { + if(TypeFromToken(tkTypeDef) == mdtTypeDef) + { + TypeDefRec *pTDRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pTDRec)); + DWORD dwTDFlags = pTDRec->GetFlags(); + LPCSTR szTDName; + IfFailGo(pMiniMd->getNameOfTypeDef(pTDRec, &szTDName)); + LPCSTR szTDNameSpace; + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTDRec, &szTDNameSpace)); + BOOL fIsTdValue=FALSE, fIsTdEnum=FALSE; + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTDRec); + + if(0 == strcmp(szTDNameSpace,BASE_NAMESPACE)) + { + fIsTdEnum = (0 == strcmp(szTDName,BASE_ENUM_CLASSNAME)); + if(!fIsTdEnum) + { + fIsTdValue = (0 == strcmp(szTDName,BASE_VTYPE_CLASSNAME)); + } + } + if(fIsTdEnum || fIsTdValue) + { + fIsTdEnum = fIsTdValue = FALSE; // System.Enum and System.ValueType themselves are classes + } + else if(RidFromToken(tkExtends)) + { + if(TypeFromToken(tkExtends) == mdtTypeDef) + { + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pTDRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTDRec, &szTDName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTDRec, &szTDNameSpace)); + } + else if(TypeFromToken(tkExtends) == mdtTypeSpec) + { + fIsTdEnum = fIsTdValue = FALSE; // a type extending a spec cannot be an enum or value type + // the assignments are redundant, but clear. + } + else + { + TypeRefRec *pTRRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pTRRec)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTRRec, &szTDName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTRRec, &szTDNameSpace)); + } + + if(0 == strcmp(szTDNameSpace,BASE_NAMESPACE)) + { + fIsTdEnum = (0 == strcmp(szTDName,BASE_ENUM_CLASSNAME)); + if(!fIsTdEnum) + { + fIsTdValue = (0 == strcmp(szTDName,BASE_VTYPE_CLASSNAME)); + } + else fIsTdValue = FALSE; + } + } + + // If Method is abstract, verify parent is abstract. + if(IsMdAbstract(dwFlags) && !IsTdAbstract(dwTDFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_ABSTPARNOTABST, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // If parent is import, method must have zero RVA, otherwise it depends... + if(IsTdImport(dwTDFlags)) bIsParentImport = TRUE; + if(IsTdInterface(dwTDFlags)) + { + if(!IsMdStatic(dwFlags)) + { + // No non-abstract instance methods in interface. + if(!IsMdAbstract(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_NOTSTATABSTININTF, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // No non-public instance methods in interface. + if(!IsMdPublic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_NOTPUBININTF, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // If Method is constructor, verify parent is not interface. + if(bIsCtor) + { + REPORT_ERROR1(VLDTR_E_MD_CTORININTF, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + }//end if(interface) + if((fIsTdValue || fIsTdEnum) && IsMiSynchronized(dwImplFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_SYNCMETHODINVTYPE, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(bIsCtor) + { + // .ctor must be instance + if(IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORSTATIC, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + }//end if .ctor + else if(bIsCctor) + { + // .cctor must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CCTORNOTSTATIC, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ..cctor must have default callconv + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + if(IMAGE_CEE_CS_CALLCONV_DEFAULT != CorSigUncompressData(pbSig)) + { + REPORT_ERROR0(VLDTR_E_MD_CCTORCALLCONV); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .cctor must have no arguments + if(0 != CorSigUncompressData(pbSig)) + { + REPORT_ERROR0(VLDTR_E_MD_CCTORHASARGS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + + }//end if .cctor + if(bIsCtor || bIsCctor) + { + // .ctor, .cctor must be SpecialName and RTSpecialName + if(!(IsMdSpecialName(dwFlags) && IsMdRTSpecialName(dwFlags))) + { + REPORT_ERROR1(VLDTR_E_MD_CTORNOTSNRTSN, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } +#ifdef NO_SUCH_CHECKS_NEEDED_SPEC_TO_BE_UODATED + // .ctor, .cctor must not be virtual + if(IsMdVirtual(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORVIRT, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .ctor, .cctor must not be abstract + if(IsMdAbstract(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORABST, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .ctor, .cctor must not be PInvoke + if(IsMdPinvokeImpl(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORPINVOKE, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .ctor,.cctor must have RVA!=0 + if(pRecord->GetRVA()==0) + { + REPORT_ERROR0(VLDTR_E_MD_CTORZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } +#endif + }//end if .ctor or .cctor + }// end if(parent == TypeDef) + }// end if not Module + else // i.e. if (RidFromToken(tkTypeDef) == RidFromToken(m_tdModule)) + { + bIsGlobal = TRUE; + // Globals are not CLS-compliant + REPORT_ERROR0(VLDTR_E_FMD_GLOBALITEM); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + // Validate global method: + // Must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_GLOBALNOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must not be abstract or virtual + if(IsMdAbstract(dwFlags) || IsMdVirtual(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_GLOBALABSTORVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must be not .ctor or .cctor + if(bIsCtor) + { + REPORT_ERROR0(VLDTR_E_MD_GLOBALCTORCCTOR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } //end if Module + + // Signature specifics: .ctor, .cctor, entrypoint + if(bIsCtor || bIsCctor) + { + // .ctor, .cctor must return void + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + CorSigUncompressData(pbSig); // get call conv out of the way + CorSigUncompressData(pbSig); // get num args out of the way + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + if(retType != ELEMENT_TYPE_VOID) + { + REPORT_ERROR0(VLDTR_E_MD_CTORNOTVOID); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if(g_tkEntryPoint == veCtxt.Token) + { + ULONG ulCallConv; + // EP must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_EP_INSTANCE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // EP can't belong to generic class or nested in generic class + mdToken tkTypeDefCur; + for(tkTypeDefCur = tkTypeDef; tkTypeDefCur != mdTokenNil;) + { + HENUMInternal hEnumTyPars; + ULONG ulTypeDefArity = 0; + hr = pMiniMd->FindGenericParamHelper(tkTypeDefCur, &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulTypeDefArity)); + HENUMInternal::ClearEnum(&hEnumTyPars); + if (ulTypeDefArity != 0) + { + REPORT_ERROR0(VLDTR_E_EP_GENERIC_TYPE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if(ulTypeDefArity == 0) + { + // This class is not generic, how about the encloser? + RID iRecord; + IfFailGo(pMiniMd->FindNestedClassHelper(tkTypeDefCur, &iRecord)); + + if (InvalidRid(iRecord)) + { + tkTypeDefCur = mdTokenNil; + } + else + { + NestedClassRec *pNestedClassRec; + IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pNestedClassRec)); + tkTypeDefCur = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec); + } + } + else + tkTypeDefCur = mdTokenNil; + } + + // EP must have a predetermined signature (different for DLL and EXE + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + ulCallConv = CorSigUncompressData(pbSig); // get call conv out of the way + // EP can't be generic + if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // Skip the arity + CorSigUncompressData(pbSig); + REPORT_ERROR0(VLDTR_E_EP_GENERIC_METHOD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // EP must have 0 or 1 argument + unsigned nArgs = CorSigUncompressData(pbSig); + if(g_fIsDLL) + { + if(nArgs != 3) + { + REPORT_ERROR1(VLDTR_E_EP_TOOMANYARGS, 3); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + //EP must return I4 + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + if(retType != ELEMENT_TYPE_I4) + { + REPORT_ERROR0(VLDTR_E_EP_BADRET); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Arguments must be VOID*, U4, VOID* + if(nArgs) + { + unsigned jj; + bool badarg; + for(jj=0; jj<nArgs;jj++) + { + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + switch(jj) + { + case 0: + case 2: + badarg = (retType != ELEMENT_TYPE_PTR) + ||(CorSigUncompressData(pbSig) != ELEMENT_TYPE_VOID); + break; + + case 1: + badarg = (retType != ELEMENT_TYPE_U4); + break; + + default: + badarg = true; + } + if(badarg) + { + REPORT_ERROR1(VLDTR_E_EP_BADARG, jj+1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + else + { + if(nArgs > 1) + { + REPORT_ERROR1(VLDTR_E_EP_TOOMANYARGS, 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + //EP must return VOID, I4 or U4 + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + if((retType != ELEMENT_TYPE_VOID)&&(retType != ELEMENT_TYPE_I4)&&(retType != ELEMENT_TYPE_U4)) + { + REPORT_ERROR0(VLDTR_E_EP_BADRET); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Argument (if any) must be vector of strings + if(nArgs) + { + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + if((retType != ELEMENT_TYPE_SZARRAY)||(CorSigUncompressData(pbSig) != ELEMENT_TYPE_STRING)) + { + REPORT_ERROR1(VLDTR_E_EP_BADARG, 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } // end if(IsDll)--else + } // end if (IsEntryPoint) + + + // Check method RVA + if(pRecord->GetRVA()==0) + { + if(!(IsMdPinvokeImpl(dwFlags) || IsMdAbstract(dwFlags) + || IsMiRuntime(dwImplFlags) || IsMiInternalCall(dwImplFlags) + || bIsParentImport)) + { + REPORT_ERROR0(VLDTR_E_MD_ZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + if(m_pStgdb && m_pStgdb->m_pImage) + { + NewHolder<PEDecoder> pe; + + EX_TRY + { + // We need to use different PEDecoder constructors 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 (m_pStgdb->m_pStgIO->GetMemoryMappedType() == MTYPE_IMAGE) + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, false); + else + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, (COUNT_T)(m_pStgdb->m_dwImageSize)); + + } + EX_CATCH + { + hr = COR_E_BADIMAGEFORMAT; + } + EX_END_CATCH(SwallowAllExceptions) + + IfFailGo(hr); + IfNullGo(pe); + + if (!pe->CheckRva(pRecord->GetRVA())) + { + REPORT_ERROR1(VLDTR_E_MD_BADRVA, pRecord->GetRVA()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(IsMiManaged(dwImplFlags) && (IsMiIL(dwImplFlags) || IsMiOPTIL(dwImplFlags))) + { + HRESULT hrTemp = S_OK; + // validate locals signature token + EX_TRY + { + COR_ILMETHOD_DECODER method((COR_ILMETHOD*) pe->GetRvaData(pRecord->GetRVA())); + if (method.LocalVarSigTok) + { + if((TypeFromToken(method.GetLocalVarSigTok()) != mdtSignature) || + (!IsValidToken(method.GetLocalVarSigTok())) || (RidFromToken(method.GetLocalVarSigTok())==0)) + { + hrTemp = _ValidateErrorHelper(VLDTR_E_MD_BADLOCALSIGTOK, veCtxt, method.GetLocalVarSigTok()); + if (SUCCEEDED(hrTemp)) + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + EX_CATCH + { + hrTemp = _ValidateErrorHelper(VLDTR_E_MD_BADHEADER, veCtxt); + if (SUCCEEDED(hrTemp)) + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + EX_END_CATCH(SwallowAllExceptions) + + IfFailGo(hrTemp); + } + } + } + + if(IsMdAbstract(dwFlags) || bIsParentImport + || IsMiRuntime(dwImplFlags) || IsMiInternalCall(dwImplFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Check the method flags + // Validate access + if((dwFlags & mdMemberAccessMask) == mdMemberAccessMask) + { + REPORT_ERROR0(VLDTR_E_FMD_BADACCESSFLAG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Final/NewSlot must be virtual + if((IsMdFinal(dwFlags)||IsMdNewSlot(dwFlags)||IsMdCheckAccessOnOverride(dwFlags)) + && !IsMdVirtual(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_FINNOTVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Static can't be final or virtual + if(IsMdStatic(dwFlags)) + { + if(IsMdFinal(dwFlags) || IsMdVirtual(dwFlags) || IsMdNewSlot(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_STATANDFINORVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else // non-static can't be an entry point + { + if(g_tkEntryPoint == veCtxt.Token) + { + REPORT_ERROR0(VLDTR_E_EP_INSTANCE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if(IsMdAbstract(dwFlags)) + { + // Can't be both abstract and final + if(IsMdFinal(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTANDFINAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // If abstract, must be not miForwardRef, not Pinvoke, and must be virtual + if(IsMiForwardRef(dwImplFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTANDIMPL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(IsMdPinvokeImpl(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTANDPINVOKE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsMdVirtual(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTNOTVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // If PrivateScope, must have RVA!=0 + if(IsMdPrivateScope(dwFlags) && (pRecord->GetRVA() ==0)) + { + REPORT_ERROR0(VLDTR_E_MD_PRIVSCOPENORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // RTSpecialName => SpecialName + if(IsMdRTSpecialName(dwFlags) && !IsMdSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_RTSNNOTSN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Method having security must be marked mdHasSecurity and vice versa + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + ULONG index; + BOOL bHasSecurity = FALSE; + for (index = ridStart; index < ridEnd; index ++ ) + { + DeclSecurityRec *pDeclSecurityRecord; + IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord)); + if ( veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord)) + { + bHasSecurity = TRUE; + break; + } + } + if(!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute" + { + bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token, + "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL)); + } + if(bHasSecurity != (IsMdHasSecurity(dwFlags)!=0)) + { + REPORT_ERROR0(bHasSecurity ? VLDTR_E_FMD_SECURNOTMARKED : VLDTR_E_FMD_MARKEDNOSECUR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Validate method semantics + { + MethodSemanticsRec *pRec; + ULONG ridEnd; + ULONG index; + unsigned uTally = 0; + mdToken tkEventProp; + ULONG iCount; + DWORD dwSemantic; + // 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) == veCtxt.Token ) + { + uTally++; + if(uTally > 1) + { + REPORT_ERROR0(VLDTR_E_MD_MULTIPLESEMANTICS); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + tkEventProp = pMiniMd->getAssociationOfMethodSemantics(pRec); + if((TypeFromToken(tkEventProp) == mdtEvent)||(TypeFromToken(tkEventProp) == mdtProperty)) + { + iCount = (TypeFromToken(tkEventProp) == mdtEvent) ? pMiniMd->getCountEvents() : + pMiniMd->getCountPropertys(); + if(RidFromToken(tkEventProp) > iCount) + { + REPORT_ERROR1(VLDTR_E_MD_SEMANTICSNOTEXIST, tkEventProp); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + else + { + REPORT_ERROR1(VLDTR_E_MD_INVALIDSEMANTICS, tkEventProp); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + // One and only one semantics flag must be set + iCount = 0; + dwSemantic = pRec->GetSemantic(); + if(IsMsSetter(dwSemantic)) iCount++; + if(IsMsGetter(dwSemantic)) iCount++; + if(IsMsOther(dwSemantic)) iCount++; + if(IsMsAddOn(dwSemantic)) iCount++; + if(IsMsRemoveOn(dwSemantic)) iCount++; + if(IsMsFire(dwSemantic)) iCount++; + if(iCount != 1) + { + REPORT_ERROR1(iCount ? VLDTR_E_MD_MULTSEMANTICFLAGS : VLDTR_E_MD_NOSEMANTICFLAGS, tkEventProp); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + }// end for(index) + } + // Check the method's impl.map + { + RID iRecord; + IfFailGo(pMiniMd->FindImplMapHelper(veCtxt.Token, &iRecord)); + if(IsMdPinvokeImpl(dwFlags)) + { + // must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // must have either ImplMap or RVA == 0 + if (InvalidRid(iRecord)) + { + if(pRecord->GetRVA()==0) + { + REPORT_ERROR0(VLDTR_E_FMD_MARKEDNOPINVOKE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + if(pRecord->GetRVA()!=0) + { + // C++ emits ImplMaps for IJW methods, + // with resolution=ModuleRef with name "" + ImplMapRec *pIMRecord; + mdToken tkModuleRef; + IfFailGo(pMiniMd->GetImplMapRecord(iRecord, &pIMRecord)); + tkModuleRef = pMiniMd->getImportScopeOfImplMap(pIMRecord); + if((TypeFromToken(tkModuleRef) == mdtModuleRef) && (!IsNilToken(tkModuleRef))) + { + ModuleRefRec *pMRRecord; // ModuleRef record. + LPCUTF8 szMRName; // ModuleRef name. + // Get the ModuleRef record. + IfFailGo(pMiniMd->GetModuleRefRecord(RidFromToken(tkModuleRef), &pMRRecord)); + // Check ModuleRef name is "". + IfFailGo(pMiniMd->getNameOfModuleRef(pMRRecord, &szMRName)); + if (*szMRName) + { + REPORT_ERROR0(VLDTR_E_MD_RVAANDIMPLMAP); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { + hr = ValidateImplMap(iRecord); + if(hr != S_OK) + { + REPORT_ERROR0(VLDTR_E_FMD_BADIMPLMAP); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + } + + } + else + { + // must have no ImplMap + if (!InvalidRid(iRecord)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + // Validate params + { + ULONG ridStart = pMiniMd->getParamListOfMethod(pRecord); + ULONG ridEnd; + IfFailGo(pMiniMd->getEndParamListOfMethod(rid, &ridEnd)); + ParamRec* pRec; + ULONG cbSigT; + PCCOR_SIGNATURE typePtr; + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &typePtr, &cbSigT)); + unsigned callConv = CorSigUncompressData(typePtr); // get the calling convention out of the way + unsigned numTyArgs = 0; + if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + bIsGeneric = TRUE; + numTyArgs = CorSigUncompressData(typePtr); + } + + unsigned numArgs = CorSigUncompressData(typePtr); + USHORT usPrevSeq = 0; + + for(ULONG ridP = ridStart; ridP < ridEnd; ridP++) + { + RID tempRid; + IfFailGo(pMiniMd->GetParamRecord(ridP, &pRec)); + // Sequence order must be ascending + if(ridP > ridStart) + { + if(pRec->GetSequence() <= usPrevSeq) + { + REPORT_ERROR2(VLDTR_E_MD_PARAMOUTOFSEQ, ridP-ridStart,pRec->GetSequence()); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + usPrevSeq = pRec->GetSequence(); + // Sequence value must not exceed num of arguments + if(usPrevSeq > numArgs) + { + REPORT_ERROR2(VLDTR_E_MD_PARASEQTOOBIG, ridP-ridStart,usPrevSeq); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Param having marshaling must be marked pdHasFieldMarshal and vice versa + IfFailGo(pMiniMd->FindFieldMarshalHelper(TokenFromRid(ridP,mdtParamDef), &tempRid)); + if (InvalidRid(tempRid) == (IsPdHasFieldMarshal(pRec->GetFlags()) != 0)) + { + REPORT_ERROR1(IsPdHasFieldMarshal(pRec->GetFlags()) ? VLDTR_E_MD_PARMMARKEDNOMARSHAL + : VLDTR_E_MD_PARMMARSHALNOTMARKED, ridP-ridStart); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Param having const value must be marked pdHasDefault and vice versa + IfFailGo(pMiniMd->FindConstantHelper(TokenFromRid(ridP,mdtParamDef), &tempRid)); + if (InvalidRid(tempRid) == (IsPdHasDefault(pRec->GetFlags()) != 0)) + { + REPORT_ERROR1(IsPdHasDefault(pRec->GetFlags()) ? VLDTR_E_MD_PARMMARKEDNODEFLT + : VLDTR_E_MD_PARMDEFLTNOTMARKED, ridP-ridStart); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + // Generic Method related checks + if (bIsGeneric) + { + if (bIsCctor) + { + REPORT_ERROR0(VLDTR_E_MD_GENERIC_CCTOR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (bIsCtor) + { + REPORT_ERROR0(VLDTR_E_MD_GENERIC_CTOR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (bIsParentImport) + { + REPORT_ERROR0(VLDTR_E_MD_GENERIC_IMPORT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethod() +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +// Validate the given ParamPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateParamPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateParamPtr() + +//***************************************************************************** +// Validate the given Param. +//***************************************************************************** +HRESULT RegMeta::ValidateParam(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + ParamRec *pRecord; // Param record + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + LPCSTR szName; // Param name. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the InterfaceImpl record. + veCtxt.Token = TokenFromRid(rid, mdtParamDef); + veCtxt.uOffset = 0; + + DWORD dwBadFlags = 0; + DWORD dwFlags = 0; + IfFailGo(pMiniMd->GetParamRecord(rid, &pRecord)); + // Name, if any, must not exceed MAX_CLASSNAME_LENGTH + IfFailGo(pMiniMd->getNameOfParam(pRecord, &szName)); + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Flags must be as defined in CorHdr.h + dwBadFlags = ~(pdIn | pdOut | pdOptional | pdHasDefault | pdHasFieldMarshal); + dwFlags = pRecord->GetFlags(); + if(dwFlags & dwBadFlags) + { + REPORT_ERROR1(VLDTR_E_PD_BADFLAGS, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateParam() + +//***************************************************************************** +// Helper function for ValidateInterfaceImpl +//***************************************************************************** +int IsMethodImplementedByClass(CMiniMdRW *pMiniMd, + mdToken tkMethod, + LPCUTF8 szName, + PCCOR_SIGNATURE pSig, + ULONG cbSig, + mdToken tkClass) +{ + HRESULT hr; + int numImpl = 0; + if(TypeFromToken(tkMethod) == mdtMethodDef) + { + if(TypeFromToken(tkClass) == mdtTypeSpec) + { + // We are trying to find out if an interface method is implemented in the generic class tkClass. + // Simple signature comparison doesn't work here, because "int Method()" in the interface might + // be implemented by "T Type.Method()" in the generic type. + // Therefore we assume it is implemented. Atlernatively we could implement better signature + // comparison which would match T with any other type, etc. + numImpl = 1; + } + else if(TypeFromToken(tkClass) == mdtTypeDef) + { + TypeDefRec *pClass; + IfFailRet(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pClass)); + RID ridClsStart = pMiniMd->getMethodListOfTypeDef(pClass); + RID ridClsEnd; + IfFailRet(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(tkClass), &ridClsEnd)); + mdMethodDef tkFoundMethod = 0; + DWORD dwFoundMethodFlags = 0; + // Check among methods + hr = ImportHelper::FindMethod(pMiniMd, tkClass, szName, pSig, cbSig, &tkFoundMethod, 0); + if(SUCCEEDED(hr)) + { + MethodRec * pMethod; + IfFailRet(pMiniMd->GetMethodRecord(RidFromToken(tkFoundMethod), &pMethod)); + if(pMethod) + { + dwFoundMethodFlags = pMiniMd->getFlagsOfMethod(pMethod); + if(IsMdVirtual(dwFoundMethodFlags)) //&&!IsMdNewSlot(dwFoundMethodFlags)) + numImpl = 1; + } + } + if (numImpl==0) //if(hr == CLDB_E_RECORD_NOTFOUND) + { // Check among MethodImpls + RID ridImpl; + for(RID idxCls = ridClsStart; idxCls < ridClsEnd; idxCls++) + { + RID ridCls; + IfFailRet(pMiniMd->GetMethodRid(idxCls, &ridCls)); + + hr = ImportHelper::FindMethodImpl(pMiniMd,tkClass,TokenFromRid(ridCls,mdtMethodDef), + tkMethod,&ridImpl); + if(hr != CLDB_E_RECORD_NOTFOUND) + { + if(SUCCEEDED(hr)) numImpl++; + break; + } + } + if(numImpl == 0) + { + // Check if parent class implements this method + mdToken tkParent = pMiniMd->getExtendsOfTypeDef(pClass); + if(RidFromToken(tkParent)) + numImpl = IsMethodImplementedByClass(pMiniMd,tkMethod,szName,pSig,cbSig,tkParent); + } + } + } + else if (TypeFromToken(tkClass) == mdtTypeRef) + { + TypeRefRec *pRecord; // TypeRef record. + LPCSTR szTRNamespace; // TypeRef Namespace. + LPCSTR szTRName; // TypeRef Name. + + // Get the TypeRef record. + IfFailRet(pMiniMd->GetTypeRefRecord(RidFromToken(tkClass), &pRecord)); + + // Check name is not NULL. + IfFailRet(pMiniMd->getNamespaceOfTypeRef(pRecord, &szTRNamespace)); + IfFailRet(pMiniMd->getNameOfTypeRef(pRecord, &szTRName)); + + mdToken tkRefScope = pMiniMd->getResolutionScopeOfTypeRef(pRecord); + if (tkRefScope == TokenFromRid(1, mdtModule)) + { + // if the typeref is referring to a type in this module then + // we should check the type definition it is referring to + mdTypeDef tkTypeDef; + hr = ImportHelper::FindTypeDefByName(pMiniMd, szTRNamespace, szTRName, tkRefScope, &tkTypeDef); + if (SUCCEEDED(hr)) + numImpl = IsMethodImplementedByClass(pMiniMd, tkMethod, szName, pSig, cbSig, tkTypeDef); + } + else if ((strcmp(szTRNamespace, BASE_NAMESPACE) == 0) && + ((strcmp(szTRName, BASE_OBJECT_CLASSNAME) == 0) || + (strcmp(szTRName, BASE_VTYPE_CLASSNAME) == 0) || + (strcmp(szTRName, BASE_ENUM_CLASSNAME) == 0))) + { + if (((strcmp(szName, SYSTEM_OBJECT_TOSTRING_METHODNAME) == 0) && + (cbSig == _countof(g_sigSystemObject_ToString)) && + (memcmp(pSig, g_sigSystemObject_ToString, cbSig) == 0)) || + ((strcmp(szName, SYSTEM_OBJECT_GETHASHCODE_METHODNAME) == 0) && + (cbSig == _countof(g_sigSystemObject_GetHashCode)) && + (memcmp(pSig, g_sigSystemObject_GetHashCode, cbSig) == 0)) || + ((strcmp(szName, SYSTEM_OBJECT_EQUALS_METHODNAME) == 0) && + (cbSig == _countof(g_sigSystemObject_Equals)) && + (memcmp(pSig, g_sigSystemObject_Equals, cbSig) == 0))) + { + numImpl = 1; // Method signature matches one of System.Object's virtual methods + } + else + { + numImpl = 0; // These classes (System.Object, System.ValueType and System.Enum) don't implement any other virtual methods + } + } + else + { + numImpl = -1; // The method is defined in another module, we cannot verify it (no external modules are loaded) + } + } + } + return numImpl; +} + +//***************************************************************************** +// Validate the given InterfaceImpl. +//***************************************************************************** +//@todo GENERICS: complete logic for type specs +// - for now, we just allow them, but we should be checking more properties +HRESULT RegMeta::ValidateInterfaceImpl(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + InterfaceImplRec *pRecord; // InterfaceImpl record. + mdTypeDef tkClass; // Class implementing the interface. + mdToken tkInterface; // TypeDef for the interface. + mdInterfaceImpl tkInterfaceImpl; // Duplicate InterfaceImpl. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL fCheckTheMethods=TRUE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the InterfaceImpl record. + veCtxt.Token = TokenFromRid(rid, mdtInterfaceImpl); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetInterfaceImplRecord(rid, &pRecord)); + + // Get implementing Class and the TypeDef for the interface. + tkClass = pMiniMd->getClassOfInterfaceImpl(pRecord); + + // No validation needs to be done on deleted records. + if (IsNilToken(tkClass)) + goto ErrExit; + + tkInterface = pMiniMd->getInterfaceOfInterfaceImpl(pRecord); + + // Validate that the Class is TypeDef. + if((!IsValidToken(tkClass))||(TypeFromToken(tkClass) != mdtTypeDef)/*&&(TypeFromToken(tkClass) != mdtTypeRef)*/) + { + REPORT_ERROR1(VLDTR_E_IFACE_BADIMPL, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + fCheckTheMethods = FALSE; + } + // Validate that the Interface is TypeDef or TypeRef or TypeSpec + if((!IsValidToken(tkInterface))||(TypeFromToken(tkInterface) != mdtTypeDef)&&(TypeFromToken(tkInterface) != mdtTypeRef) + &&(TypeFromToken(tkInterface) != mdtTypeSpec)) + { + REPORT_ERROR1(VLDTR_E_IFACE_BADIFACE, tkInterface); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + fCheckTheMethods = FALSE; + } + // Validate that Interface is marked tdInterface. + else if(TypeFromToken(tkInterface) == mdtTypeDef) + { + TypeDefRec *pTDRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkInterface), &pTDRec)); + if(!IsTdInterface(pTDRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_IFACE_NOTIFACE, tkInterface); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + } + + // Look for duplicates. + hr = ImportHelper::FindInterfaceImpl(pMiniMd, tkClass, tkInterface, + &tkInterfaceImpl, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_IFACE_DUP, tkInterfaceImpl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + + // Validate that the Class (if not interface or abstract) implements all the methods of Interface + if((TypeFromToken(tkInterface) == mdtTypeDef) && fCheckTheMethods && (tkInterface != tkClass)) + { + TypeDefRec *pClass; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pClass)); + if(!(IsTdAbstract(pClass->GetFlags()) + ||IsTdImport(pClass->GetFlags()) + ||IsTdInterface(pClass->GetFlags()))) + { + TypeDefRec *pInterface; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkInterface), &pInterface)); + RID ridIntStart = pMiniMd->getMethodListOfTypeDef(pInterface); + RID ridIntEnd; + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(tkInterface), &ridIntEnd)); + MethodRec* pIntMethod; + for(RID idxInt = ridIntStart; idxInt < ridIntEnd; idxInt++) + { + RID ridInt; + IfFailGo(pMiniMd->GetMethodRid(idxInt, &ridInt)); + IfFailGo(pMiniMd->GetMethodRecord(ridInt, &pIntMethod)); + const char* szName; + IfFailGo(pMiniMd->getNameOfMethod(pIntMethod, &szName)); + if(!IsMdStatic(pIntMethod->GetFlags()) + && !IsDeletedName(szName) + && !IsVtblGapName(szName)) + { + ULONG cbSig; + PCCOR_SIGNATURE pSig; + IfFailGo(pMiniMd->getSignatureOfMethod(pIntMethod, &pSig, &cbSig)); + if(cbSig) + { + int num = IsMethodImplementedByClass(pMiniMd,TokenFromRid(ridInt,mdtMethodDef),szName,pSig,cbSig,tkClass); + if(num == 0) + { // Error: method not implemented + REPORT_ERROR3(VLDTR_E_IFACE_METHNOTIMPL, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(num == -1) + { + // Traced to a TypeRef, which might implement the method, give warning + REPORT_ERROR3(VLDTR_E_IFACE_METHNOTIMPLTHISMOD, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef)); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + if(num > 1) + { // Error: multiple method implementation + REPORT_ERROR3(VLDTR_E_IFACE_METHMULTIMPL, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateInterfaceImpl() + +//***************************************************************************** +// Validate the given GenericParam. +//***************************************************************************** +HRESULT RegMeta::ValidateGenericParam(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + GenericParamRec *pRecord; // GenericParam record. + LPCSTR szName; // GenericParam name field. + mdToken tkOwner; // GenericParam owner field. + ULONG ulNumber; // GenericParam number field. + DWORD dwFlags; // GenericParam flags field + + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the GenericParam record. + veCtxt.Token = TokenFromRid(rid, mdtGenericParam); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetGenericParamRecord(rid, &pRecord)); + + // 1. GenericParam may contain zero or more rows. + // (Nothing to check.) + + tkOwner = pMiniMd->getOwnerOfGenericParam(pRecord); + // 2. Owner must be a valid token and a type def or method def + // (Already checked by ValidateRecord) + + // CLR tolerates Nil owners, ECMA does not + if(IsNilToken(tkOwner)) + { + REPORT_ERROR0(VLDTR_E_GP_OWNERNIL); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + + //3. Every generic type shall own one row in the Generic Param table for each of its type parameters. [ERROR] + // (Nothing to check, as the arity of a generic type is, by definition, the number of generic param entries). + + //4. Every generic method shall own one row in the Generic Param table for each of its type parameters. [ERROR] + // (This is checked in ValidateMethodSig, error VLDTR_E_MD_GPMISMATCH). + + ulNumber = pMiniMd->getNumberOfGenericParam(pRecord); + + // 5. Flags must be valid + { + DWORD dwInvalidMask, dwExtraBits; + + dwFlags = pMiniMd->getFlagsOfGenericParam(pRecord); + + + // check for extra bits + dwInvalidMask = (DWORD)~(gpVarianceMask|gpSpecialConstraintMask); + dwExtraBits = dwFlags & dwInvalidMask; + if(dwExtraBits) + { + //@GENERICS: we could use a custom error, + // but this is one is already used in more than one context. + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + //Check Variance + { + DWORD dwVariance = dwFlags & gpVarianceMask; + switch (dwVariance) + { + case gpNonVariant: + // always ok + break; + case gpCovariant: + case gpContravariant: + if (TypeFromToken(tkOwner)==mdtTypeDef) + { + if (IsNilToken(tkOwner)) + break; + TypeDefRec *pTypeDefRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkOwner), &pTypeDefRec)); + // co-contra variance only legal on interfaces and delegates + // If owner is not an interface and does not extend MultiCastDelegate, report an error + if(!IsTdInterface(pTypeDefRec->GetFlags())) + { + // Get the parent of the TypeDef. + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTypeDefRec); + LPCSTR szExtName = NULL; // Parent Name. + LPCSTR szExtNameSpace = NULL; // Parent NameSpace. + BOOL bExtendsMCDelegate = FALSE; + + // Determine if the parent is MCDelegate + if (TypeFromToken(tkExtends) != mdtTypeSpec) + { + if (TypeFromToken(tkExtends) == mdtTypeRef) + { + TypeRefRec *pExtTypeRefRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pExtTypeRefRec)); + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pExtTypeRefRec); + if (RidFromToken(tkResScope) && (TypeFromToken(tkResScope) == mdtAssemblyRef)) + { + AssemblyRefRec * pARRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec)); + LPCSTR szAssemblyRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName)); + if ((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName))) + // otherwise don't even bother extracting the name + { + IfFailGo(pMiniMd->getNameOfTypeRef(pExtTypeRefRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtTypeRefRec, &szExtNameSpace)); + } + } + } + else if (TypeFromToken(tkExtends) == mdtTypeDef) + { + if (g_fValidatingMscorlib) // otherwise don't even bother extracting the name + { + TypeDefRec * pExtTypeRefRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtTypeRefRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtTypeRefRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtTypeRefRec, &szExtNameSpace)); + } + } + + bExtendsMCDelegate = + szExtNameSpace && szExtName && + (0 == strcmp(szExtNameSpace,BASE_NAMESPACE)) && + (0 == strcmp(szExtName,BASE_MCDELEGATE_CLASSNAME)); + } + + // Report any error + if (!bExtendsMCDelegate) + { + REPORT_ERROR1(VLDTR_E_GP_UNEXPECTED_OWNER_FOR_VARIANT_VAR,tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { + // co-contra variance never legal on MVARs + REPORT_ERROR0(VLDTR_E_GP_ILLEGAL_VARIANT_MVAR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + default: + REPORT_ERROR1(VLDTR_E_GP_ILLEGAL_VARIANCE_FLAGS,dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + + // Check special constraints + { + DWORD dwSpecialConstraints = dwFlags & gpSpecialConstraintMask; + // It is illegal go declare both gpNotNullableValueTypeConstraint + // and gpReferenceTypeConstraint, but gpDefaultConstructorConstraint + // is legal with either (or neither). + if ((dwSpecialConstraints & (gpReferenceTypeConstraint | gpNotNullableValueTypeConstraint)) == + (gpReferenceTypeConstraint | gpNotNullableValueTypeConstraint)) + { + REPORT_ERROR1(VLDTR_E_GP_REFANDVALUETYPE,dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + // 6. Number shall have a value >=0 and < number of type parameters in owner type or method. + // 7. Successive rows of the GenericParam table that are owned by the same method (sic) (owner?) shall + // be ordered by increasing Number value; there can be no gaps in the Number sequence. + { + if(ulNumber>0) + { if(rid==1) + { + REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_NUMBER); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + GenericParamRec *pPredRecord; + IfFailGo(pMiniMd->GetGenericParamRecord(rid-1, &pPredRecord)); + mdToken tkPredOwner = pMiniMd->getOwnerOfGenericParam(pPredRecord); + ULONG ulPredNumber = pMiniMd->getNumberOfGenericParam(pPredRecord); + if (tkPredOwner != tkOwner) + { + REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_OWNER); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if (ulPredNumber != ulNumber-1) + { + REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_NUMBER); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + // 8. Name must be non-null and not too long + IfFailGo(pMiniMd->getNameOfGenericParam(pRecord, &szName)); + if (!*szName) + { + // name is NULL. + REPORT_ERROR0(VLDTR_E_GP_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; //@GENERICS: do we allow parameters to be deleted? + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + +#ifdef THIS_RULE_IS_DISABLED_BECAUSE_CSHARP_EMITS_DUP_NAMES_AND_DOESNT_WANT_TO_STOP + // 9. There shall be no duplicates based upon Owner and Name + if (szName) + { + mdGenericParam tkDupGenericParam; + hr = ImportHelper::FindGenericParamByOwner(pMiniMd, tkOwner, szName, NULL, &tkDupGenericParam, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_GP_DUPNAME, tkDupGenericParam); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } +#endif + + // 10. There shall be no duplicates based upon Owner and Number + { + mdGenericParam tkDupGenericParam; + hr = ImportHelper::FindGenericParamByOwner(pMiniMd, tkOwner, NULL, &ulNumber, &tkDupGenericParam, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_GP_DUPNUMBER, tkDupGenericParam); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + hr = hrSave; + +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateGenericParam() + +//***************************************************************************** +// Validate the given MemberRef. +//***************************************************************************** +HRESULT RegMeta::ValidateMemberRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + MemberRefRec *pRecord; // MemberRef record. + mdMemberRef tkMemberRef; // Duplicate MemberRef. + mdToken tkClass; // MemberRef parent. + LPCSTR szName; // MemberRef name. + PCCOR_SIGNATURE pbSig; // MemberRef signature. + PCCOR_SIGNATURE pbSigTmp; // Temp copy of pbSig, so that can be changed. + ULONG cbSig; // Size of sig in bytes. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the MemberRef record. + veCtxt.Token = TokenFromRid(rid, mdtMemberRef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetMemberRefRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfMemberRef(pRecord, &szName)); + if (!*szName) + { + REPORT_ERROR0(VLDTR_E_MR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if (IsVtblGapName(szName)) + { + REPORT_ERROR0(VLDTR_E_MR_VTBLNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (IsDeletedName(szName)) + { + REPORT_ERROR0(VLDTR_E_MR_DELNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // MemberRef parent should never be nil in a PE file. + tkClass = pMiniMd->getClassOfMemberRef(pRecord); + if (m_ModuleType == ValidatorModuleTypePE && IsNilToken(tkClass)) + { + REPORT_ERROR0(VLDTR_E_MR_PARNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Verify that the signature is a valid signature as per signature spec. + IfFailGo(pMiniMd->getSignatureOfMemberRef(pRecord, &pbSig, &cbSig)); + + // Do some semantic checks based on the signature. + if (hr == S_OK) + { + ULONG ulCallingConv; + ULONG ulArgCount; + ULONG ulTyArgCount = 0; + ULONG ulCurByte = 0; + + // Extract calling convention. + pbSigTmp = pbSig; + ulCurByte += CorSigUncompressedDataSize(pbSigTmp); + ulCallingConv = CorSigUncompressData(pbSigTmp); + + // Get the type argument count + if (ulCallingConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + ulCurByte += CorSigUncompressedDataSize(pbSigTmp); + ulTyArgCount = CorSigUncompressData(pbSigTmp); + } + + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pbSigTmp); + ulArgCount = CorSigUncompressData(pbSigTmp); + + // Calling convention must be one of IMAGE_CEE_CS_CALLCONV_DEFAULT, + // IMAGE_CEE_CS_CALLCONV_VARARG or IMAGE_CEE_CS_CALLCONV_FIELD. + if (!isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_DEFAULT) && + !isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG) && + !isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + REPORT_ERROR1(VLDTR_E_MR_BADCALLINGCONV, ulCallingConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // [CLS] Calling convention must not be VARARG + if(isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG)) + { + REPORT_ERROR0(VLDTR_E_MR_VARARGCALLINGCONV); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + + // If the parent is a MethodDef... + if (TypeFromToken(tkClass) == mdtMethodDef) + { + if (RidFromToken(tkClass) != 0) + { + // The MethodDef must be the same name and the fixed part of the + // vararg signature must be the same. + MethodRec *pMethodRecord; // Method Record. + LPCSTR szMethodName; // Method name. + PCCOR_SIGNATURE pbMethodSig; // Method signature. + ULONG cbMethodSig; // Size in bytes of signature. + + // Get Method record, name and signature. + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkClass), &pMethodRecord)); + IfFailGo(pMiniMd->getNameOfMethod(pMethodRecord, &szMethodName)); + IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRecord, &pbMethodSig, &cbMethodSig)); + + // Verify that the name of the Method is the same as the MemberRef. + if (strcmp(szName, szMethodName)) + { + REPORT_ERROR1(VLDTR_E_MR_NAMEDIFF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG)) + { // It's VARARG calling convention + CQuickBytes qbFixedSig; // Quick bytes to hold the fixed part of the variable signature. + ULONG cbFixedSig; // Size in bytes of the fixed part. + + // Get the fixed part of the vararg signature of the MemberRef. + hr = _GetFixedSigOfVarArg(pbSig, cbSig, &qbFixedSig, &cbFixedSig); + if (FAILED(hr) || cbFixedSig != cbMethodSig || + memcmp(pbMethodSig, qbFixedSig.Ptr(), cbFixedSig)) + { + UnifiedAssemblySigComparer uasc(*this); + MDSigComparer sc(MDSigParser(pbMethodSig, cbMethodSig), + MDSigParser((PCCOR_SIGNATURE)qbFixedSig.Ptr(), cbFixedSig), + uasc); + + hr = sc.CompareMethodSignature(); + if (FAILED(hr)) + { + hr = S_OK; + REPORT_ERROR1(VLDTR_E_MR_SIGDIFF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { // It's not VARARG calling convention - a MemberRef is referencing MethodDef (part of + // NoPIA) + UnifiedAssemblySigComparer uasc(*this); + MDSigComparer sc(MDSigParser(pbMethodSig, cbMethodSig), + MDSigParser(pbSig, cbSig), + uasc); + + // Compare signatures + hr = sc.CompareMethodSignature(); + if (FAILED(hr)) + { + hr = S_OK; + REPORT_ERROR1(VLDTR_E_MR_SIGDIFF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + // There should be no duplicate MemberRefs. + if (*szName && pbSig && cbSig) + { + hr = ImportHelper::FindMemberRef(pMiniMd, tkClass, szName, pbSig, + cbSig, &tkMemberRef, rid, + ImportHelper::CreateHash); // Optimize for multiple calls + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MR_DUP, tkMemberRef); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + { + hr = S_OK; + } + else + { + IfFailGo(hr); + } + } + + if (!isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + hr = ValidateMethodSig(veCtxt.Token,pbSig, cbSig,0); + SetVldtrCode(&hrSave,hr); + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMemberRef() + +//***************************************************************************** +// Validate the given Constant. +//***************************************************************************** +HRESULT RegMeta::ValidateConstant(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + ConstantRec *pRecord; // Constant record. + mdToken tkParent; // Constant parent. + const VOID* pbBlob; // Constant value blob ptr + DWORD cbBlob; // Constant value blob size + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the MemberRef record. + veCtxt.Token = rid; + veCtxt.uOffset = 0; + + ULONG maxrid = 0; + ULONG typ = 0; + IfFailGo(pMiniMd->GetConstantRecord(rid, &pRecord)); + IfFailGo(pMiniMd->getValueOfConstant(pRecord, (const BYTE **)&pbBlob, &cbBlob)); + switch(pRecord->GetType()) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R8: + if(pbBlob == NULL) + { + REPORT_ERROR1(VLDTR_E_CN_BLOBNULL, pRecord->GetType()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + case ELEMENT_TYPE_STRING: + break; + + case ELEMENT_TYPE_CLASS: + if(GET_UNALIGNED_32(pbBlob) != 0) + { + REPORT_ERROR1(VLDTR_E_CN_BLOBNOTNULL, pRecord->GetType()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + + default: + REPORT_ERROR1(VLDTR_E_CN_BADTYPE, pRecord->GetType()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + tkParent = pMiniMd->getParentOfConstant(pRecord); + typ = TypeFromToken(tkParent); + switch(typ) + { + case mdtFieldDef: + maxrid = pMiniMd->getCountFields(); + break; + case mdtParamDef: + maxrid = pMiniMd->getCountParams(); + break; + case mdtProperty: + maxrid = pMiniMd->getCountPropertys(); + break; + } + switch(typ) + { + case mdtFieldDef: + case mdtParamDef: + case mdtProperty: + { + ULONG rid_p = RidFromToken(tkParent); + if((0==rid_p)||(rid_p > maxrid)) + { + REPORT_ERROR1(VLDTR_E_CN_PARENTRANGE, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + break; + } + + default: + REPORT_ERROR1(VLDTR_E_CN_PARENTTYPE, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + break; + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateConstant() + +//***************************************************************************** +// Validate the given CustomAttribute. +//***************************************************************************** +HRESULT RegMeta::ValidateCustomAttribute(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + CustomAttributeRec *pRecord; + IfFailGo(pMiniMd->GetCustomAttributeRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + + veCtxt.Token = TokenFromRid(rid,mdtCustomAttribute); + veCtxt.uOffset = 0; + + if (pRecord != NULL) + { + mdToken tkOwner = pMiniMd->getParentOfCustomAttribute(pRecord); + if(RidFromToken(tkOwner)) + { // if 0, it's deleted CA, don't pay attention + mdToken tkCAType = pMiniMd->getTypeOfCustomAttribute(pRecord); + DWORD cbValue=0; + const BYTE *pbValue; + IfFailGo(pMiniMd->getValueOfCustomAttribute(pRecord, &pbValue, &cbValue)); + if((TypeFromToken(tkOwner)==mdtCustomAttribute)||(!IsValidToken(tkOwner))) + { + REPORT_ERROR1(VLDTR_E_CA_BADPARENT, tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(((TypeFromToken(tkCAType)!=mdtMethodDef)&&(TypeFromToken(tkCAType)!=mdtMemberRef)) + ||(!IsValidToken(tkCAType))||(RidFromToken(tkCAType)==0)) + { + REPORT_ERROR1(VLDTR_E_CA_BADTYPE, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { //i.e. Type is valid MethodDef or MemberRef + LPCUTF8 szName; + PCCOR_SIGNATURE pSig=NULL; + DWORD cbSig=0; + DWORD dwFlags=0; + if (TypeFromToken(tkCAType) == mdtMethodDef) + { + MethodRec *pTypeRec; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkCAType), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfMethod(pTypeRec, &szName)); + IfFailGo(pMiniMd->getSignatureOfMethod(pTypeRec, &pSig, &cbSig)); + dwFlags = pTypeRec->GetFlags(); + } + else // it can be only MemberRef, otherwise we wouldn't be here + { + MemberRefRec *pTypeRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCAType), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfMemberRef(pTypeRec, &szName)); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pTypeRec, &pSig, &cbSig)); + } + if (strcmp(szName, ".ctor") != 0) + { + REPORT_ERROR1(VLDTR_E_CA_NOTCTOR, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if ((cbSig > 0) && (pSig != NULL)) + { + if(FAILED(ValidateMethodSig(tkCAType, pSig,cbSig,dwFlags)) + || (!((*pSig) & IMAGE_CEE_CS_CALLCONV_HASTHIS))) + { + REPORT_ERROR1(VLDTR_E_CA_BADSIG, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { // sig seems to be OK + if ((pbValue != NULL) && (cbValue > 0)) + { + // Check if prolog is OK + WORD pW = *((UNALIGNED WORD*)pbValue); + if(pW != 0x0001) + { + REPORT_ERROR1(VLDTR_E_CA_BADPROLOG, pW); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check if blob corresponds to the signature + } + } + + } + else + { + REPORT_ERROR1(VLDTR_E_CA_NOSIG, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } // end if bad Type - else + } // end if RidFromToken(tkOwner) + } // end if pRecord + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateCustomAttribute() + +//***************************************************************************** +// Validate the given FieldMarshal. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldMarshal(RID rid) +{ + return S_OK; +} // RegMeta::ValidateFieldMarshal() + +//***************************************************************************** +// Validate the given DeclSecurity. +//***************************************************************************** +HRESULT RegMeta::ValidateDeclSecurity(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + DeclSecurityRec *pRecord; + mdToken tkOwner; // Owner of the decl security + DWORD dwAction; // action flags + BOOL bIsValidOwner = FALSE; + + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(pMiniMd->GetDeclSecurityRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + + veCtxt.Token = TokenFromRid(rid,mdtPermission); + veCtxt.uOffset = 0; + + // Must have a valid owner + tkOwner = pMiniMd->getParentOfDeclSecurity(pRecord); + if(RidFromToken(tkOwner)==0) goto ErrExit; // deleted record, no need to validate + switch(TypeFromToken(tkOwner)) + { + case mdtModule: + case mdtAssembly: + case mdtTypeDef: + case mdtMethodDef: + case mdtFieldDef: + case mdtInterfaceImpl: + bIsValidOwner = IsValidToken(tkOwner); + break; + default: + break; + } + if(!bIsValidOwner) + { + REPORT_ERROR1(VLDTR_E_DS_BADOWNER, tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must have one and only one flag set + dwAction = pRecord->GetAction() & dclActionMask; + if(dwAction > dclMaximumValue) // the flags are 0,1,2,3,...,dclMaximumValue + { + REPORT_ERROR1(VLDTR_E_DS_BADFLAGS, dwAction); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // If field has DeclSecurity, verify its parent is not an interface.-- checked in ValidateField + // If method has DeclSecurity, verify its parent is not an interface.-- checked in ValidateMethod + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateDeclSecurity() + +//***************************************************************************** +// Validate the given ClassLayout. +//***************************************************************************** +HRESULT RegMeta::ValidateClassLayout(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + ClassLayoutRec *pRecord; // ClassLayout record. + TypeDefRec *pTypeDefRec; // Parent TypeDef record. + DWORD dwPackingSize; // Packing size. + mdTypeDef tkParent; // Parent TypeDef token. + DWORD dwTypeDefFlags; // Parent TypeDef flags. + RID clRid; // Duplicate ClassLayout rid. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + memset(&veCtxt, 0, sizeof(VEContext)); + + BEGIN_ENTRYPOINT_NOTHROW; + + // Extract the record. + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetClassLayoutRecord(rid, &pRecord)); + + // Get the parent, if parent is nil its a deleted record. Skip validation. + tkParent = pMiniMd->getParentOfClassLayout(pRecord); + if (IsNilToken(tkParent)) + goto ErrExit; + + // Parent should not have AutoLayout set on it. + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec)); + dwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec); + if (IsTdAutoLayout(dwTypeDefFlags)) + { + REPORT_ERROR1(VLDTR_E_CL_TDAUTO, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Parent must not be an Interface + if(IsTdInterface(dwTypeDefFlags)) + { + REPORT_ERROR1(VLDTR_E_CL_TDINTF, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate the PackingSize. + dwPackingSize = pMiniMd->getPackingSizeOfClassLayout(pRecord); + if((dwPackingSize > 128)||((dwPackingSize & (dwPackingSize-1)) !=0 )) + { + REPORT_ERROR2(VLDTR_E_CL_BADPCKSZ, tkParent, dwPackingSize); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate that there are no duplicates. + hr = _FindClassLayout(pMiniMd, tkParent, &clRid, rid); + if (hr == S_OK) + { + REPORT_ERROR2(VLDTR_E_CL_DUP, tkParent, clRid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateClassLayout() + +//***************************************************************************** +// Validate the given FieldLayout. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldLayout(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + FieldLayoutRec *pRecord; // FieldLayout record. + mdFieldDef tkField; // Field token. + ULONG ulOffset; // Field offset. + FieldRec *pFieldRec; // Field record. + TypeDefRec *pTypeDefRec; // Parent TypeDef record. + mdTypeDef tkTypeDef; // Parent TypeDef token. + RID clRid; // Corresponding ClassLayout token. + RID flRid = 0; // Duplicate FieldLayout rid. + DWORD dwTypeDefFlags; // Parent TypeDef flags. + DWORD dwFieldFlags; // Field flags. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Extract the record. + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetFieldLayoutRecord(rid, &pRecord)); + + // Get the field, if it's nil it's a deleted record, so just skip it. + tkField = pMiniMd->getFieldOfFieldLayout(pRecord); + if (IsNilToken(tkField)) + goto ErrExit; + + // Validate the Offset value. + ulOffset = pMiniMd->getOffSetOfFieldLayout(pRecord); + if (ulOffset == ULONG_MAX) + { + REPORT_ERROR2(VLDTR_E_FL_BADOFFSET, tkField, ulOffset); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Get the parent of the Field. + IfFailGo(pMiniMd->FindParentOfFieldHelper(tkField, &tkTypeDef)); + // Validate that the parent is not nil. + if (IsNilToken(tkTypeDef)) + { + REPORT_ERROR1(VLDTR_E_FL_TDNIL, tkField); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + // Validate that there exists a ClassLayout record associated with + // this TypeDef. + IfFailGo(pMiniMd->FindClassLayoutHelper(tkTypeDef, &clRid)); + if (InvalidRid(rid)) + { + REPORT_ERROR2(VLDTR_E_FL_NOCL, tkField, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate that ExplicitLayout is set on the TypeDef flags. + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pTypeDefRec)); + dwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec); + if (IsTdAutoLayout(dwTypeDefFlags)) + { + REPORT_ERROR2(VLDTR_E_FL_TDNOTEXPLCT, tkField, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Extract Field record. + IfFailGo(pMiniMd->GetFieldRecord(RidFromToken(tkField), &pFieldRec)); + // Validate that the field is non-static. + dwFieldFlags = pMiniMd->getFlagsOfField(pFieldRec); + if (IsFdStatic(dwFieldFlags)) + { + REPORT_ERROR1(VLDTR_E_FL_FLDSTATIC, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Look for duplicates. + hr = _FindFieldLayout(pMiniMd, tkField, &flRid, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_FL_DUP, flRid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFieldLayout() + +//***************************************************************************** +// Validate the given StandAloneSig. +//***************************************************************************** +HRESULT RegMeta::ValidateStandAloneSig(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + StandAloneSigRec *pRecord; // FieldLayout record. + PCCOR_SIGNATURE pbSig; // Signature. + ULONG cbSig; // Size in bytes of the signature. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArgCount; // Count of arguments. + ULONG ulTyArgCount = 0; // Count of type arguments. + ULONG i; // Looping index. + ULONG ulNSentinels = 0; // Number of sentinels in the signature + BOOL bNoVoidAllowed=TRUE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Extract the record. + veCtxt.Token = TokenFromRid(rid,mdtSignature); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetStandAloneSigRecord(rid, &pRecord)); + IfFailGo(pMiniMd->getSignatureOfStandAloneSig(pRecord, &pbSig, &cbSig)); + + // Validate the signature is well-formed with respect to the compression + // scheme. If this fails, no further validation needs to be done. + if ( (hr = ValidateSigCompression(veCtxt.Token, pbSig, cbSig)) != S_OK) + goto ErrExit; + + //_ASSERTE((rid != 0x2c2)&&(rid!=0x2c8)&&(rid!=0x2c9)&&(rid!=0x2d6)&&(rid!=0x38b)); + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK; + if(i == IMAGE_CEE_CS_CALLCONV_FIELD) // <REVISIT_TODO>it's a temporary bypass (VB bug)</REVISIT_TODO> + ulArgCount = 1; + else + { + if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli + { + if((i >= IMAGE_CEE_CS_CALLCONV_FIELD) + ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) + &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) + { + REPORT_ERROR1(VLDTR_E_MD_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + bNoVoidAllowed = FALSE; + } + // Is there any sig left for arguments? + _ASSERTE(ulCurByte <= cbSig); + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MD_NOARGCNT, ulCurByte+1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + // Get the type argument count. + if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulTyArgCount = CorSigUncompressData(pbSig); + } + + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCount = CorSigUncompressData(pbSig); + } + // Validate the the arguments. + if(ulArgCount) + { + for(i=1; ulCurByte < cbSig; i++) + { + hr = ValidateOneArg(veCtxt.Token, pbSig, cbSig, &ulCurByte,&ulNSentinels,bNoVoidAllowed); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i); + } + SetVldtrCode(&hr, hrSave); + hrSave = hr; + break; + } + bNoVoidAllowed = TRUE; // whatever it was for the 1st arg, it must be TRUE for the rest + } + if((ulNSentinels != 0) && (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_VARARG ))) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTMUSTVARARG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(ulNSentinels > 1) + { + REPORT_ERROR0(VLDTR_E_SIG_MULTSENTINELS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateStandAloneSig() + +//***************************************************************************** +// Validate the given EventMap. +//***************************************************************************** +HRESULT RegMeta::ValidateEventMap(RID rid) +{ + return S_OK; +} // RegMeta::ValidateEventMap() + +//***************************************************************************** +// Validate the given EventPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateEventPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateEventPtr() + +//***************************************************************************** +// Validate the given Event. +//***************************************************************************** +HRESULT RegMeta::ValidateEvent(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkClass; // Declaring TypeDef + mdToken tkEventType; // Event Type (TypeDef/TypeRef) + EventRec *pRecord; + HENUMInternal hEnum; + + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(pMiniMd->GetEventRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + memset(&hEnum, 0, sizeof(HENUMInternal)); + veCtxt.Token = TokenFromRid(rid,mdtEvent); + veCtxt.uOffset = 0; + + // The scope must be a valid TypeDef + if (FAILED(pMiniMd->FindParentOfEventHelper(veCtxt.Token, &tkClass)) || + (TypeFromToken(tkClass) != mdtTypeDef) || + !IsValidToken(tkClass)) + { + REPORT_ERROR1(VLDTR_E_EV_BADSCOPE, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + tkClass = 0; + } + // Must have name + { + LPCUTF8 szName; + IfFailGo(pMiniMd->getNameOfEvent(pRecord, &szName)); + + if (*szName == 0) + { + REPORT_ERROR0(VLDTR_E_EV_NONAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if (strcmp(szName, COR_DELETED_NAME_A) == 0) + goto ErrExit; + if (tkClass != 0) // Must be no duplicates + { + RID ridEventMap; + EventMapRec *pEventMapRec; + EventRec *pRec; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + + IfFailGo(pMiniMd->FindEventMapFor(RidFromToken(tkClass), &ridEventMap)); + if (!InvalidRid(ridEventMap)) + { + IfFailGo(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec)); + ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec); + IfFailGo(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd)); + + for (i = ridStart; i < ridEnd; i++) + { + if (i == rid) + continue; + IfFailGo(pMiniMd->GetEventRecord(i, &pRec)); + + LPCSTR szEventName; + IfFailGo(pMiniMd->getNameOfEvent(pRec, &szEventName)); + if (strcmp(szName, szEventName) != 0) + continue; + + REPORT_ERROR1(VLDTR_E_EV_DUP, TokenFromRid(i, mdtEvent)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + }// end of name block + // EventType must be Nil or valid TypeDef, TypeRef or TypeSpec representing an instantiated generic type + tkEventType = pMiniMd->getEventTypeOfEvent(pRecord); + if (!IsNilToken(tkEventType)) + { + if(IsValidToken(tkEventType) && + ((TypeFromToken(tkEventType)==mdtTypeDef)|| + (TypeFromToken(tkEventType)==mdtTypeRef)|| + (TypeFromToken(tkEventType)==mdtTypeSpec))) + { + // TypeSpecs can be many things, we only handle instantiated generic types currently. + if (TypeFromToken(tkEventType)==mdtTypeSpec) + { + TypeSpecRec *pRec; + IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(tkEventType), &pRec)); + PCCOR_SIGNATURE pSig; + ULONG cSig; + + IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, &pSig, &cSig)); + + if (CorSigUncompressElementType(pSig) == ELEMENT_TYPE_GENERICINST && + CorSigUncompressElementType(pSig) == ELEMENT_TYPE_CLASS) + { + // Just update the event type token variable and fall through to the validation code below (it doesn't care + // whether the type is generic or not). + tkEventType = CorSigUncompressToken(pSig); + } + else + { + REPORT_ERROR1(VLDTR_E_EV_BADEVTYPE, tkEventType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // EventType must not be Interface or ValueType + if(TypeFromToken(tkEventType)==mdtTypeDef) // can't say anything about TypeRef: no flags available! + { + TypeDefRec *pTypeDefRecord; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkEventType), &pTypeDefRecord)); + DWORD dwFlags = pTypeDefRecord->GetFlags(); + if(!IsTdClass(dwFlags)) + { + REPORT_ERROR2(VLDTR_E_EV_EVTYPENOTCLASS, tkEventType, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { + REPORT_ERROR1(VLDTR_E_EV_BADEVTYPE, tkEventType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Validate related methods + { + MethodSemanticsRec *pSemantics; + RID ridCur; + ULONG ulSemantics; + mdMethodDef tkMethod; + bool bHasAddOn = false; + bool bHasRemoveOn = false; + + IfFailGo( pMiniMd->FindMethodSemanticsHelper(veCtxt.Token, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur)) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics)); + ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics); + tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef ); + // Semantics must be Setter, Getter or Other + switch (ulSemantics) + { + case msAddOn: + bHasAddOn = true; + break; + case msRemoveOn: + bHasRemoveOn = true; + break; + case msFire: + case msOther: + break; + default: + REPORT_ERROR2(VLDTR_E_EV_BADSEMANTICS, tkMethod,ulSemantics); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Method must be valid + if(!IsValidToken(tkMethod)) + { + REPORT_ERROR1(VLDTR_E_EV_BADMETHOD, tkMethod); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + // Method's parent must be the same + mdToken tkTypeDef; + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkMethod, &tkTypeDef)); + if(tkTypeDef != tkClass) + { + REPORT_ERROR2(VLDTR_E_EV_ALIENMETHOD, tkMethod,tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } // end loop over methods + // AddOn and RemoveOn are a must + if(!bHasAddOn) + { + REPORT_ERROR0(VLDTR_E_EV_NOADDON); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!bHasRemoveOn) + { + REPORT_ERROR0(VLDTR_E_EV_NOREMOVEON); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + }// end of related method validation block + + hr = hrSave; +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateEvent() + + +//***************************************************************************** +// Validate the given PropertyMap. +//***************************************************************************** +HRESULT RegMeta::ValidatePropertyMap(RID rid) +{ + return S_OK; +} // RegMeta::ValidatePropertyMap(0 + +//***************************************************************************** +// Validate the given PropertyPtr. +//***************************************************************************** +HRESULT RegMeta::ValidatePropertyPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidatePropertyPtr() + +//***************************************************************************** +// Validate the given Property. +//***************************************************************************** +HRESULT RegMeta::ValidateProperty(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkClass = mdTokenNil; // Declaring TypeDef + PropertyRec *pRecord; + HENUMInternal hEnum; + RID tempRid; + + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(pMiniMd->GetPropertyRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + memset(&hEnum, 0, sizeof(HENUMInternal)); + veCtxt.Token = TokenFromRid(rid,mdtProperty); + veCtxt.uOffset = 0; + // The scope must be a valid TypeDef + IfFailGo(pMiniMd->FindParentOfPropertyHelper( veCtxt.Token, &tkClass)); + if ((TypeFromToken(tkClass) != mdtTypeDef) || + !IsValidToken(tkClass) || + IsNilToken(tkClass)) + { + REPORT_ERROR1(VLDTR_E_PR_BADSCOPE, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must have name and signature + { + ULONG cbSig; + PCCOR_SIGNATURE pvSig; + IfFailGo(pMiniMd->getTypeOfProperty(pRecord, &pvSig, &cbSig)); + + LPCUTF8 szName; + IfFailGo(pMiniMd->getNameOfProperty(pRecord, &szName)); + ULONG ulNameLen = (szName != NULL) ? (ULONG)strlen(szName) : 0; + + if (ulNameLen == 0) + { + REPORT_ERROR0(VLDTR_E_PR_NONAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(strcmp(szName, COR_DELETED_NAME_A) == 0) + goto ErrExit; + } + if (cbSig == 0) + { + REPORT_ERROR0(VLDTR_E_PR_NOSIG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must be no duplicates + if ((ulNameLen != 0) && (cbSig != 0)) + { + RID ridPropertyMap; + PropertyMapRec *pPropertyMapRec; + PropertyRec *pRec; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + ULONG cbSig1; + PCCOR_SIGNATURE pvSig1; + + IfFailGo(pMiniMd->FindPropertyMapFor(RidFromToken(tkClass), &ridPropertyMap)); + if (!InvalidRid(ridPropertyMap) ) + { + IfFailGo(pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + + for (i = ridStart; i < ridEnd; i++) + { + if (i == rid) + continue; + IfFailGo(pMiniMd->GetPropertyRecord(i, &pRec)); + IfFailGo(pMiniMd->getTypeOfProperty(pRec, &pvSig1, &cbSig1)); + + if (cbSig != cbSig1) + continue; + if (memcmp(pvSig,pvSig1,cbSig) != 0) + continue; + + LPCSTR szPropertyName; + IfFailGo(pMiniMd->getNameOfProperty(pRec, &szPropertyName)); + if (strcmp(szName, szPropertyName) != 0) + continue; + + REPORT_ERROR1(VLDTR_E_PR_DUP, TokenFromRid(i,mdtProperty)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + // Validate the signature + if ((pvSig != NULL) && (cbSig != 0)) + { + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArgCount; + ULONG i; + ULONG ulNSentinels = 0; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pvSig); + ulCallConv = CorSigUncompressData(pvSig); + if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_PROPERTY )) + { + REPORT_ERROR1(VLDTR_E_PR_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pvSig); + ulArgCount = CorSigUncompressData(pvSig); + + // Validate the arguments. + for (i = 0; i < ulArgCount; i++) + { + hr = ValidateOneArg(veCtxt.Token, pvSig, cbSig, &ulCurByte,&ulNSentinels,(i>0)); + if (hr != S_OK) + { + if (hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i+1); + } + SetVldtrCode(&hr, hrSave); + break; + } + } + }//end if(pvSig && cbSig) + }// end of name/signature block + + // Marked HasDefault <=> has default value + IfFailGo(pMiniMd->FindConstantHelper(veCtxt.Token, &tempRid)); + if (InvalidRid(tempRid) == IsPrHasDefault(pRecord->GetPropFlags())) + { + REPORT_ERROR0(IsPrHasDefault(pRecord->GetPropFlags())? VLDTR_E_PR_MARKEDNODEFLT : VLDTR_E_PR_DEFLTNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate related methods + { + MethodSemanticsRec *pSemantics; + RID ridCur; + ULONG ulSemantics; + mdMethodDef tkMethod; + + IfFailGo( pMiniMd->FindMethodSemanticsHelper(veCtxt.Token, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &ridCur)) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics)); + ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics); + tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef ); + // Semantics must be Setter, Getter or Other + switch (ulSemantics) + { + case msSetter: + case msGetter: + case msOther: + break; + default: + REPORT_ERROR2(VLDTR_E_PR_BADSEMANTICS, tkMethod, ulSemantics); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Method must be valid + if(!IsValidToken(tkMethod)) + { + REPORT_ERROR1(VLDTR_E_PR_BADMETHOD, tkMethod); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + // Method's parent must be the same + mdToken tkTypeDef; + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkMethod, &tkTypeDef)); + if(tkTypeDef != tkClass) + { + REPORT_ERROR2(VLDTR_E_PR_ALIENMETHOD, tkMethod, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } // end loop over methods + }// end of related method validation block + + hr = hrSave; +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateProperty() + +//***************************************************************************** +// Validate the given MethodSemantics. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodSemantics(RID rid) +{ + return S_OK; +} // RegMeta::ValidateMethodSemantics() + +//***************************************************************************** +// Validate the given MethodImpl. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodImpl(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + MethodImplRec* pRecord; + MethodImplRec* pRec; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkClass; // Declaring TypeDef + mdToken tkBody; // Implementing method (MethodDef or MemberRef) + mdToken tkDecl; // Implemented method (MethodDef or MemberRef) + unsigned iCount; + unsigned index; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtMethodImpl); + veCtxt.uOffset = 0; + + PCCOR_SIGNATURE pbBodySig = NULL; + PCCOR_SIGNATURE pbDeclSig = NULL; + + IfFailGo(pMiniMd->GetMethodImplRecord(rid, &pRecord)); + tkClass = pMiniMd->getClassOfMethodImpl(pRecord); + // Class must be valid + if(!IsValidToken(tkClass) || (TypeFromToken(tkClass) != mdtTypeDef)) + { + REPORT_ERROR1(VLDTR_E_MI_BADCLASS, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { // ... and not an Interface + TypeDefRec *pTypeDefRecord; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pTypeDefRecord)); + if(IsTdInterface(pTypeDefRecord->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_CLASSISINTF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Decl must be valid MethodDef or MemberRef + tkDecl = pMiniMd->getMethodDeclarationOfMethodImpl(pRecord); + if(!(IsValidToken(tkDecl) && + ((TypeFromToken(tkDecl) == mdtMethodDef) || (TypeFromToken(tkDecl) == mdtMemberRef)))) + { + REPORT_ERROR1(VLDTR_E_MI_BADDECL, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Body must be valid MethodDef or MemberRef + tkBody = pMiniMd->getMethodBodyOfMethodImpl(pRecord); + if(!(IsValidToken(tkBody) && + ((TypeFromToken(tkBody) == mdtMethodDef) || (TypeFromToken(tkBody) == mdtMemberRef)))) + { + REPORT_ERROR1(VLDTR_E_MI_BADBODY, tkBody); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // No duplicates based on (tkClass,tkDecl) + iCount = pMiniMd->getCountMethodImpls(); + for(index = rid+1; index <= iCount; index++) + { + IfFailGo(pMiniMd->GetMethodImplRecord(index, &pRec)); + if((tkClass == pMiniMd->getClassOfMethodImpl(pRec)) && + (tkDecl == pMiniMd->getMethodDeclarationOfMethodImpl(pRec))) + { + REPORT_ERROR1(VLDTR_E_MI_DUP, index); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + mdToken tkBodyParent; + ULONG cbBodySig; + + if(TypeFromToken(tkBody) == mdtMethodDef) + { + MethodRec *pBodyRec; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkBody), &pBodyRec)); + IfFailGo(pMiniMd->getSignatureOfMethod(pBodyRec, &pbBodySig, &cbBodySig)); + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkBody, &tkBodyParent)); + // Body must not be static + if(IsMdStatic(pBodyRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_BODYSTATIC, tkBody); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(TypeFromToken(tkBody) == mdtMemberRef) + { + MemberRefRec *pBodyRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkBody), &pBodyRec)); + tkBodyParent = pMiniMd->getClassOfMemberRef(pBodyRec); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pBodyRec, &pbBodySig, &cbBodySig)); + } + // Body must belong to the same class + if(tkBodyParent != tkClass) + { + REPORT_ERROR1(VLDTR_E_MI_ALIENBODY, tkBodyParent); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + mdToken tkDeclParent; + ULONG cbDeclSig; + + if(TypeFromToken(tkDecl) == mdtMethodDef) + { + MethodRec *pDeclRec; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkDecl), &pDeclRec)); + IfFailGo(pMiniMd->getSignatureOfMethod(pDeclRec, &pbDeclSig, &cbDeclSig)); + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkDecl, &tkDeclParent)); + // Decl must be virtual + if(!IsMdVirtual(pDeclRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_DECLNOTVIRT, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Decl must not be final + if(IsMdFinal(pDeclRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_DECLFINAL, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Decl must not be private + if(IsMdPrivate(pDeclRec->GetFlags()) && IsMdCheckAccessOnOverride(pDeclRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_DECLPRIV, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(TypeFromToken(tkDecl) == mdtMemberRef) + { + MemberRefRec *pDeclRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkDecl), &pDeclRec)); + tkDeclParent = pMiniMd->getClassOfMemberRef(pDeclRec); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pDeclRec, &pbDeclSig, &cbDeclSig)); + } + + // Compare the signatures as best we can, delegating some comparisons to the loader. + if (*pbBodySig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // decl's callconv must be generic + if (*pbDeclSig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // and the arities must match + ULONG ulBodyArity = CorSigUncompressData(++pbBodySig); + ULONG ulDeclArity = CorSigUncompressData(++pbDeclSig); + if(ulBodyArity != ulDeclArity) + { + REPORT_ERROR3(VLDTR_E_MI_ARITYMISMATCH,tkDecl,ulDeclArity,ulBodyArity); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + REPORT_ERROR1(VLDTR_E_MI_DECLNOTGENERIC,tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // delegate precise signature checking to the loader, + // as this requires signature comparison modulo substitution + } + else if (*pbDeclSig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + REPORT_ERROR1(VLDTR_E_MI_IMPLNOTGENERIC,tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (TypeFromToken(tkDeclParent) == mdtTypeSpec) + { + // do nothing for now... + // delegate precise signature checking to the loader, + // as this requires signature comparison modulo substitution + } + // Signatures must match (except call conv) + else if((cbDeclSig != cbBodySig)||(memcmp(pbDeclSig+1,pbBodySig+1,cbDeclSig-1))) + { + //@GENERICSVER: todo: + /* + //@TODO: Fix to have peverify resolve assemblies + // through the runtime. At that point, use this method instead + // of the current compare + + // @TODO: check for other bad memcmp sig comparisons in peverify + + // Can't use memcmp because there may be two AssemblyRefs + // in this scope, pointing to the same assembly, etc.). + if (!MetaSig::CompareMethodSigs(pbDeclSig, + cbDeclSig, + Module* pModule1, + pbBodySig, + cbDeclSig, + Module* pModule2)) + */ + UnifiedAssemblySigComparer uasc(*this); + MDSigComparer sc(MDSigParser(pbDeclSig, cbDeclSig), + MDSigParser(pbBodySig, cbBodySig), + uasc); + + hr = sc.CompareMethodSignature(); + + if (FAILED(hr)) + { + REPORT_ERROR2(VLDTR_E_MI_SIGMISMATCH,tkDecl,tkBody); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodImpl() + +//***************************************************************************** +// Validate the given ModuleRef. +//***************************************************************************** +HRESULT RegMeta::ValidateModuleRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ModuleRefRec *pRecord; // ModuleRef record. + LPCUTF8 szName; // ModuleRef name. + mdModuleRef tkModuleRef; // Duplicate ModuleRef. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the ModuleRef record. + veCtxt.Token = TokenFromRid(rid, mdtModuleRef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetModuleRefRecord(rid, &pRecord)); + + // C++ emits IJW methods with ImplMaps + // which have resolution=ModuleRef with empty name + IfFailGo(pMiniMd->getNameOfModuleRef(pRecord, &szName)); + if (*szName) + { + // Look for a Duplicate, this function reports only one duplicate. + hr = ImportHelper::FindModuleRef(pMiniMd, szName, &tkModuleRef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MODREF_DUP, tkModuleRef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + else + hrSave = S_FALSE; + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateModuleRef() + +//***************************************************************************** +// Validate the given TypeSpec. +//***************************************************************************** +//@todo GENERICS: reject duplicate specs? +HRESULT RegMeta::ValidateTypeSpec(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + TypeSpecRec *pRecord; // TypeSpec record. + PCCOR_SIGNATURE pbSig; // Signature. + ULONG cbSig; // Size in bytes of the signature. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulNSentinels = 0; // Number of sentinels in the signature + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Extract the record. + veCtxt.Token = TokenFromRid(rid,mdtTypeSpec); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetTypeSpecRecord(rid, &pRecord)); + IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRecord, &pbSig, &cbSig)); + + // Validate the signature is well-formed with respect to the compression + // scheme. If this fails, no further validation needs to be done. + if ( (hr = ValidateSigCompression(veCtxt.Token, pbSig, cbSig)) != S_OK) + goto ErrExit; + + hr = ValidateOneArg(veCtxt.Token, pbSig, cbSig, &ulCurByte,&ulNSentinels,FALSE); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR0(VLDTR_E_TS_EMPTY); + } + SetVldtrCode(&hr, hrSave); + hrSave = hr; + } + if(ulNSentinels != 0) + { + REPORT_ERROR0(VLDTR_E_TS_HASSENTINALS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateTypeSpec() + +//***************************************************************************** +// This function validates the given Field signature. This function works +// with Field signature for both the MemberRef and FieldDef. +//***************************************************************************** +HRESULT RegMeta::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 *pArity) // [Out] Arity of the instantiation +{ + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArity; // Arity of instantiation. + ULONG ulArgCnt; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + _ASSERTE(TypeFromToken(tk) == mdtMethodSpec); + + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_GENERICINST)) + { + REPORT_ERROR1(VLDTR_E_MS_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MS_MISSARITY, ulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulArity = CorSigUncompressData(pbSig); + + if (ulArity == 0) + { + REPORT_ERROR1(VLDTR_E_MS_ARITYZERO, ulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + ulArgCnt = ulArity; + + if(pArity != NULL) + { + *pArity = ulArity; + } + + // Validate and consume the arguments. + while(ulArgCnt--) + { + + PCCOR_SIGNATURE pbTypeArg = pbSig; + ULONG ulTypeArgByte = ulCurByte; + + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, &ulCurByte, NULL, TRUE)); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_MS_MISSARG, ulArity-ulArgCnt); + } + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // reject byref-like args + switch (CorSigUncompressData(pbTypeArg)) + { + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_BYREF: + { + REPORT_ERROR1(VLDTR_E_MS_BYREFINST, ulTypeArgByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + default: + break; + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodSpecSig() + + +//***************************************************************************** +// Validate the given MethodSpec. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodSpec(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + MethodSpecRec *pRecord; // MethodSpec record. + mdToken tkMethod; // Method field (a MethodDefOrRef) + PCCOR_SIGNATURE pInstantiation; // MethodSpec instantiation (a signature) + ULONG cbInstantiation; // Size of instantiation. + ULONG ulInstantiationArity; // Arity of the Instantiation + + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the GenericParamConstraint record. + veCtxt.Token = TokenFromRid(rid, mdtMethodSpec); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetMethodSpecRecord(rid, &pRecord)); + + // 1. The MethodSpec table may contain zero or more rows. + // (Nothing to check.) + + // Implicit (missing from spec): Method is not nil [ERROR] + tkMethod = pMiniMd->getMethodOfMethodSpec(pRecord); + if(IsNilToken(tkMethod)) + { + REPORT_ERROR0(VLDTR_E_MS_METHODNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Implicit in ValidateRecord: Method is a valid mdMethodDefOrRef. + + // 2. One or more rows may refer to the same row in the MethodDef or MethodRef table. + // (There may be more multiple instantions of the same generic method) + // (nothing to check!) + + // 3. "The signature stored at Instantiation shall be a valid instantiation of the signature of the generic method stored at Method. [ERROR] + { + IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiation, &cbInstantiation)); + IfFailGo(ValidateMethodSpecSig(TokenFromRid(rid, mdtMethodSpec), pInstantiation, cbInstantiation,&ulInstantiationArity)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + } + + IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiation, &cbInstantiation)); + // 4. There shall be no duplicate rows based upon Method and Instantiation [ERROR] + { + mdMethodSpec tkDupMethodSpec; + hr = ImportHelper::FindMethodSpecByMethodAndInstantiation(pMiniMd, tkMethod, pInstantiation, cbInstantiation, &tkDupMethodSpec, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MS_DUP, tkDupMethodSpec); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + // check the method is generic and that the arity of the instantiation is correct + { + PCCOR_SIGNATURE pbGenericMethodSig; + ULONG cbGenericMethodSig; + + if(TypeFromToken(tkMethod) == mdtMethodDef) + { + MethodRec *pMethodRec; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkMethod), &pMethodRec)); + IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRec, &pbGenericMethodSig, &cbGenericMethodSig)); + } + else + { + _ASSERTE(TypeFromToken(tkMethod) == mdtMemberRef); + MemberRefRec *pMethodRefRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkMethod), &pMethodRefRec)); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pMethodRefRec, &pbGenericMethodSig, &cbGenericMethodSig)); + } + + if (*pbGenericMethodSig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + ULONG ulGenericArity = CorSigUncompressData(++pbGenericMethodSig); + if(ulGenericArity != ulInstantiationArity) + { + REPORT_ERROR2(VLDTR_E_MS_ARITYMISMATCH,ulGenericArity,ulInstantiationArity); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + REPORT_ERROR1(VLDTR_E_MS_METHODNOTGENERIC, tkMethod); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + hr = hrSave; + +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodSpec() + + +//***************************************************************************** +// Validate the given GenericParamConstraint. +//***************************************************************************** +HRESULT RegMeta::ValidateGenericParamConstraint(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + GenericParamConstraintRec *pRecord; // GenericParamConstraint record. + mdGenericParam tkOwner; // GenericParamConstraint owner field. + mdToken tkConstraint; // GenericParamConstraint constraint field. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the GenericParamConstraint record. + veCtxt.Token = TokenFromRid(rid, mdtGenericParamConstraint); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(rid, &pRecord)); + + // 1. GenericParamConstraint may contain zero or more rows. + // (Nothing to check.) + + // 2. Each row shall have one, and only one, owner row in the GenericParamTable [ERROR] + // (Nothing to check except owner not nil) + tkOwner = pMiniMd->getOwnerOfGenericParamConstraint(pRecord); + if(IsNilToken(tkOwner)) + { + REPORT_ERROR0(VLDTR_E_GPC_OWNERNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // 3. Each row in the GenericParam table shall own a separate row in the GenericParamConstraint table for each constraint that type parameter has [ERROR] + // (Nothing to check) + + // 4.All of the rows in the GenericParamConstraint table that are owned by a given row in the GenericParamTable + // shall form a contiguous range of rows [ERROR] + //@NOTE: this check is (iterated over all rows) is quadratic in the (typically small) number of constraints + { + RID curRid = rid; + GenericParamConstraintRec *pCurRecord; + mdGenericParam tkCurOwner = tkOwner; + // find the first preceding row with a distinct owner + while (curRid > 1 && tkCurOwner == tkOwner) + { + curRid--; + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(curRid, &pCurRecord)); + tkCurOwner = pMiniMd->getOwnerOfGenericParamConstraint(pCurRecord); + }; + // reject this row if there is some row preceding the current row with this owner + while (curRid > 1) + { + curRid--; + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(curRid, &pCurRecord)); + tkCurOwner = pMiniMd->getOwnerOfGenericParamConstraint(pCurRecord); + if (tkCurOwner == tkOwner) + { + REPORT_ERROR1(VLDTR_E_GPC_NONCONTIGUOUS,tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + }; + } + + // 5. "At most one class constraint per GenericParam" --- no longer required. + // 6. "Zero or more interface constraints per GenericParam" --- no longer required. + + tkConstraint = pMiniMd->getConstraintOfGenericParamConstraint(pRecord); + // 7. There shall be no duplicates based upon Owner and Constraint + { + mdGenericParamConstraint tkDupGenericParamConstraint; + hr = ImportHelper::FindGenericParamConstraintByOwnerAndConstraint(pMiniMd, tkOwner, tkConstraint, &tkDupGenericParamConstraint, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_GPC_DUP, tkDupGenericParamConstraint); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + hr = hrSave; + +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateGenericParamConstraint() + +//***************************************************************************** +// Validate the given ImplMap. +//***************************************************************************** +HRESULT RegMeta::ValidateImplMap(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ImplMapRec *pRecord; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + HRESULT hrModuleRef=S_OK; + mdToken tkModuleRef; + mdToken tkMember; + USHORT usFlags; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + for(unsigned jjj=0; jjj<g_nValidated; jjj++) + { + if(g_rValidated[jjj].tok == (rid | 0x51000000)) return g_rValidated[jjj].hr; + } +#endif + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetImplMapRecord(rid, &pRecord)); + if(pRecord == NULL) IfFailGo(E_FAIL); + // ImplMap must have ModuleRef + tkModuleRef = pMiniMd->getImportScopeOfImplMap(pRecord); + if((TypeFromToken(tkModuleRef) != mdtModuleRef) || IsNilToken(tkModuleRef) + || FAILED(hrModuleRef= ValidateModuleRef(RidFromToken(tkModuleRef)))) + { + REPORT_ERROR1(VLDTR_E_IMAP_BADMODREF, tkModuleRef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ImplMap must belong to FieldDef or MethodDef + tkMember = pMiniMd->getMemberForwardedOfImplMap(pRecord); + if((TypeFromToken(tkMember) != mdtFieldDef) && (TypeFromToken(tkMember) != mdtMethodDef)) + { + REPORT_ERROR1(VLDTR_E_IMAP_BADMEMBER, tkMember); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ImplMap must have import name, unless ModuleRef has no name + // (special case for C++ IJW methods) + if(hrModuleRef != S_FALSE) + { + LPCSTR szName; // Import name. + IfFailGo(pMiniMd->getImportNameOfImplMap(pRecord, &szName)); + if((szName==NULL)||(*szName == 0)) + { + REPORT_ERROR0(VLDTR_E_IMAP_BADIMPORTNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // ImplMap must have valid flags: + // one value of pmCharSetMask - always so, no check needed (values: 0,2,4,6, mask=6) + // one value of pmCallConvMask... + // ...and it's not pmCallConvThiscall + usFlags = pRecord->GetMappingFlags() & pmCallConvMask; + if((usFlags < pmCallConvWinapi)||(usFlags > pmCallConvFastcall)) + { + REPORT_ERROR1(VLDTR_E_IMAP_BADCALLCONV, usFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } +ErrExit: + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + g_rValidated[g_nValidated].tok = rid | 0x51000000; + g_rValidated[g_nValidated].hr = hrSave; + g_nValidated++; +#endif + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateImplMap() + +//***************************************************************************** +// Validate the given FieldRVA. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldRVA(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + FieldRVARec *pRecord; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkField; + ULONG ulRVA; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetFieldRVARecord(rid, &pRecord)); + ulRVA = pRecord->GetRVA(); + tkField = pMiniMd->getFieldOfFieldRVA(pRecord); + /* + if(ulRVA == 0) + { + REPORT_ERROR1(VLDTR_E_FRVA_ZERORVA, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + */ + if((0==RidFromToken(tkField))||(TypeFromToken(tkField) != mdtFieldDef)||(!IsValidToken(tkField))) + { + REPORT_ERROR2(VLDTR_E_FRVA_BADFIELD, tkField, ulRVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + { + RID N = pMiniMd->getCountFieldRVAs(); + RID tmp; + FieldRVARec* pRecTmp; + for(tmp = rid+1; tmp <= N; tmp++) + { + IfFailGo(pMiniMd->GetFieldRVARecord(tmp, &pRecTmp)); + if(tkField == pMiniMd->getFieldOfFieldRVA(pRecTmp)) + { + REPORT_ERROR2(VLDTR_E_FRVA_DUPFIELD, tkField, tmp); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFieldRVA() + +//***************************************************************************** +// Validate the given ENCLog. +//***************************************************************************** +HRESULT RegMeta::ValidateENCLog(RID rid) +{ + return S_OK; +} // RegMeta::ValidateENCLog() + +//***************************************************************************** +// Validate the given ENCMap. +//***************************************************************************** +HRESULT RegMeta::ValidateENCMap(RID rid) +{ + return S_OK; +} // RegMeta::ValidateENCMap() + +//***************************************************************************** +// Validate the given Assembly. +//***************************************************************************** +HRESULT RegMeta::ValidateAssembly(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + AssemblyRec *pRecord; // Assembly record. + CorAssemblyFlags dwFlags; // Assembly flags. + LPCSTR szName; // Assembly Name. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL invalidAssemblyFlags; // Whether the CorAssemblyFlags are valid. + BOOL fIsV2Assembly = FALSE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the Assembly record. + veCtxt.Token = TokenFromRid(rid, mdtAssembly); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetAssemblyRecord(rid, &pRecord)); + + // There can only be one Assembly record. + if (rid > 1) + { + REPORT_ERROR0(VLDTR_E_AS_MULTI); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Do checks for name validity.. + IfFailGo(pMiniMd->getNameOfAssembly(pRecord, &szName)); + if (!*szName) + { + // Assembly Name is null. + REPORT_ERROR0(VLDTR_E_AS_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + unsigned L = (unsigned)strlen(szName); + if((*szName==' ')||strchr(szName,':') || strchr(szName,'\\') || strchr(szName, '/') + || strchr(szName, ',') || strchr(szName, '\n') || strchr(szName, '\r') + || ((L > 4)&&((!SString::_stricmp(&szName[L-4],".exe"))||(!SString::_stricmp(&szName[L-4],".dll"))))) + { + //Assembly name has path and/or extension + REPORT_ERROR0(VLDTR_E_AS_BADNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the flags value for the Assembly. + dwFlags = (CorAssemblyFlags) pMiniMd->getFlagsOfAssembly(pRecord); + + // Validate the flags + invalidAssemblyFlags = dwFlags & (~(afPublicKey | afRetargetable | afPA_FullMask | afEnableJITcompileTracking | afDisableJITcompileOptimizer | afContentType_Mask)); + + // Validate we only set a legal processor architecture flags + // The processor architecture flags were introduced in CLR v2.0. + // Note that METAMODEL_MINOR_VER_V2_0 is 0. GCC points out the comparison + // is useless, so that part is commented out. + fIsV2Assembly = (m_pStgdb->m_MiniMd.m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0 + /* && m_pStgdb->m_MiniMd.m_Schema.m_minor >= METAMODEL_MINOR_VER_V2_0*/); + if (fIsV2Assembly) + { + if ((dwFlags & afPA_Mask) > afPA_AMD64 && !IsAfPA_NoPlatform(dwFlags)) + invalidAssemblyFlags = true; + } + else { + if ((dwFlags & afPA_Mask) != 0) + invalidAssemblyFlags = true; + } + + if (!IsAfContentType_Default(dwFlags) && !IsAfContentType_WindowsRuntime(dwFlags)) + { // Unknown ContentType value + invalidAssemblyFlags = true; + } + + if (invalidAssemblyFlags) + { + REPORT_ERROR1(VLDTR_E_AS_BADFLAGS, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate hash algorithm ID + switch(pRecord->GetHashAlgId()) + { + case CALG_MD2: + case CALG_MD4: + case CALG_MD5: + case CALG_SHA: + //case CALG_SHA1: // same as CALG_SHA + case CALG_MAC: + case CALG_SSL3_SHAMD5: + case CALG_HMAC: + case 0: + break; + default: + REPORT_ERROR1(VLDTR_E_AS_HASHALGID, pRecord->GetHashAlgId()); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + // Validate locale + { + LPCSTR szLocale; + IfFailGo(pMiniMd->getLocaleOfAssembly(pRecord, &szLocale)); + if(!_IsValidLocale(szLocale, fIsV2Assembly)) + { + REPORT_ERROR0(VLDTR_E_AS_BADLOCALE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateAssembly() + +//***************************************************************************** +// Validate the given AssemblyProcessor. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyProcessor(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyProcessor() + +//***************************************************************************** +// Validate the given AssemblyOS. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyOS(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyOS() + +//***************************************************************************** +// Validate the given AssemblyRef. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + AssemblyRefRec *pRecord; // Assembly record. + LPCSTR szName; // AssemblyRef Name. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtAssemblyRef); + veCtxt.uOffset = 0; + + // Get the AssemblyRef record. + IfFailGo(pMiniMd->GetAssemblyRefRecord(rid, &pRecord)); + + // Do checks for name and alias validity. + IfFailGo(pMiniMd->getNameOfAssemblyRef(pRecord, &szName)); + if (!*szName) + { + // AssemblyRef Name is null. + REPORT_ERROR0(VLDTR_E_AR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + unsigned L = (unsigned)strlen(szName); + if((*szName==' ')||strchr(szName,':') || strchr(szName,'\\') || strchr(szName, '/') + || strchr(szName, ',') || strchr(szName, '\n') || strchr(szName, '\r') + || ((L > 4)&&((!SString::_stricmp(&szName[L-4],".exe"))||(!SString::_stricmp(&szName[L-4],".dll"))))) + { + //Assembly name has path and/or extension + REPORT_ERROR0(VLDTR_E_AS_BADNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Validate locale + { + LPCSTR szLocale; + IfFailGo(pMiniMd->getLocaleOfAssemblyRef(pRecord, &szLocale)); + BOOL fIsV2Assembly = (m_pStgdb->m_MiniMd.m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0 + /* && m_pStgdb->m_MiniMd.m_Schema.m_minor >= METAMODEL_MINOR_VER_V2_0*/); + if(!_IsValidLocale(szLocale, fIsV2Assembly)) + { + REPORT_ERROR0(VLDTR_E_AS_BADLOCALE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateAssemblyRef() + +//***************************************************************************** +// Validate the given AssemblyRefProcessor. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyRefProcessor(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyRefProcessor() + +//***************************************************************************** +// Validate the given AssemblyRefOS. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyRefOS(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyRefOS() + +//***************************************************************************** +// Validate the given File. +//***************************************************************************** +HRESULT RegMeta::ValidateFile(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + FileRec *pRecord; // File record. + mdFile tkFile; // Duplicate File token. + LPCSTR szName; // File Name. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtFile); + veCtxt.uOffset = 0; + + // Get the File record. + IfFailGo(pMiniMd->GetFileRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfFile(pRecord, &szName)); + if (!*szName) + { + // File Name is null. + REPORT_ERROR0(VLDTR_E_FILE_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_PATH_FNAME) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_PATH_FNAME-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check for duplicates based on Name. + hr = ImportHelper::FindFile(pMiniMd, szName, &tkFile, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_FILE_DUP, tkFile); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + + // File name must not be fully qualified. + if(strchr(szName,':') || strchr(szName,'\\') || strchr(szName,'/')) + { + REPORT_ERROR0(VLDTR_E_FILE_NAMEFULLQLFD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // File name must not be one of system names. + char *sysname[6]={"con","aux","lpt","prn","null","com"}; + char *syssymbol = "0123456789$:"; + for(unsigned i=0; i<6; i++) + { + L = (ULONG)strlen(sysname[i]); + if(!SString::_strnicmp(szName,sysname[i],L)) + { + if((szName[L]==0)|| strchr(syssymbol,szName[L])) + { + REPORT_ERROR0(VLDTR_E_FILE_SYSNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + } + + if (pRecord->GetFlags() & (~0x00000003)) + { + REPORT_ERROR1(VLDTR_E_FILE_BADFLAGS, pRecord->GetFlags()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate hash value + { + const BYTE *pbHashValue = NULL; + ULONG cbHashValue; + IfFailGo(m_pStgdb->m_MiniMd.getHashValueOfFile(pRecord, &pbHashValue, &cbHashValue)); + if ((pbHashValue == NULL) || (cbHashValue == 0)) + { + REPORT_ERROR0(VLDTR_E_FILE_NULLHASH); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Validate that the name is not the same as the file containing + // the manifest. + + // File name must be a valid file name. + + // Each ModuleRef in the assembly must have a corresponding File table entry. + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFile() + +//***************************************************************************** +// Validate the given ExportedType. +//***************************************************************************** +HRESULT RegMeta::ValidateExportedType(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ExportedTypeRec *pRecord; // ExportedType record. + mdExportedType tkExportedType; // Duplicate ExportedType. + mdToken tkImpl; // Implementation token + mdToken tkTypeDef; // TypeDef token + + LPCSTR szName; // ExportedType Name. + LPCSTR szNamespace; // ExportedType Namespace. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtExportedType); + veCtxt.uOffset = 0; + + // Get the ExportedType record. + IfFailGo(pMiniMd->GetExportedTypeRecord(rid, &pRecord)); + + tkImpl = pMiniMd->getImplementationOfExportedType(pRecord); + + tkTypeDef = pRecord->GetTypeDefId(); + if ((TypeFromToken(tkImpl) == mdtFile) && IsNilToken(tkTypeDef)) + { // Report 'No TypeDefId' warning only for types exported from other modules (do not report it for + // type forwarders) + REPORT_ERROR0(VLDTR_E_CT_NOTYPEDEFID); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + + // Do checks for name validity. + IfFailGo(pMiniMd->getTypeNameOfExportedType(pRecord, &szName)); + IfFailGo(pMiniMd->getTypeNamespaceOfExportedType(pRecord, &szNamespace)); + if (!*szName) + { + // ExportedType Name is null. + REPORT_ERROR0(VLDTR_E_CT_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; + ULONG L = (ULONG)(strlen(szName)+strlen(szNamespace)); + if(L >= MAX_CLASSNAME_LENGTH) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check for duplicates based on Name and Enclosing ExportedType. + hr = ImportHelper::FindExportedType(pMiniMd, szNamespace, szName, tkImpl, &tkExportedType, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_CT_DUP, tkExportedType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + // Check for duplicate TypeDef based on Name/NameSpace - only for top-level ExportedTypes. + if(TypeFromToken(tkImpl)==mdtFile) + { + mdToken tkTypeDef2; + hr = ImportHelper::FindTypeDefByName(pMiniMd, szNamespace, szName, mdTypeDefNil, + &tkTypeDef2, 0); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_CT_DUPTDNAME, tkTypeDef2); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + } + // Check if flag value is valid + { + DWORD dwFlags = pRecord->GetFlags(); + DWORD dwInvalidMask, dwExtraBits; + dwInvalidMask = (DWORD)~(tdVisibilityMask | tdLayoutMask | tdClassSemanticsMask | + tdAbstract | tdSealed | tdSpecialName | tdImport | tdSerializable | tdForwarder | + tdStringFormatMask | tdBeforeFieldInit | tdReservedMask); + // check for extra bits + dwExtraBits = dwFlags & dwInvalidMask; + if(!dwExtraBits) + { + // if no extra bits, check layout + dwExtraBits = dwFlags & tdLayoutMask; + if(dwExtraBits != tdLayoutMask) + { + // layout OK, check string format + dwExtraBits = dwFlags & tdStringFormatMask; + if(dwExtraBits != tdStringFormatMask) dwExtraBits = 0; + } + } + if(dwExtraBits) + { + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + if(IsNilToken(tkImpl) + || ((TypeFromToken(tkImpl) != mdtFile)&&(TypeFromToken(tkImpl) != mdtExportedType)&&(TypeFromToken(tkImpl) != mdtAssemblyRef)) + || (!IsValidToken(tkImpl))) + { + REPORT_ERROR1(VLDTR_E_CT_BADIMPL, tkImpl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateExportedType() + +//***************************************************************************** +// Validate the given ManifestResource. +//***************************************************************************** +HRESULT RegMeta::ValidateManifestResource(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ManifestResourceRec *pRecord; // ManifestResource record. + LPCSTR szName; // ManifestResource Name. + DWORD dwFlags; // ManifestResource flags. + mdManifestResource tkmar; // Duplicate ManifestResource. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkImplementation; + BOOL bIsValidImplementation = TRUE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtManifestResource); + veCtxt.uOffset = 0; + + // Get the ManifestResource record. + IfFailGo(pMiniMd->GetManifestResourceRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfManifestResource(pRecord, &szName)); + if (!*szName) + { + // ManifestResource Name is null. + REPORT_ERROR0(VLDTR_E_MAR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + // Check for duplicates based on Name. + hr = ImportHelper::FindManifestResource(pMiniMd, szName, &tkmar, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MAR_DUP, tkmar); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + // Get the flags of the ManifestResource. + dwFlags = pMiniMd->getFlagsOfManifestResource(pRecord); + if(dwFlags &(~0x00000003)) + { + REPORT_ERROR1(VLDTR_E_MAR_BADFLAGS, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Visibility of ManifestResource flags must either be public or private. + if (!IsMrPublic(dwFlags) && !IsMrPrivate(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MAR_NOTPUBPRIV); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Implementation must be Nil or valid AssemblyRef or File + tkImplementation = pMiniMd->getImplementationOfManifestResource(pRecord); + if(!IsNilToken(tkImplementation)) + { + switch(TypeFromToken(tkImplementation)) + { + case mdtAssemblyRef: + bIsValidImplementation = IsValidToken(tkImplementation); + break; + case mdtFile: + if((bIsValidImplementation = IsValidToken(tkImplementation))) + { // if file not PE, offset must be 0 + FileRec *pFR; + IfFailGo(pMiniMd->GetFileRecord(RidFromToken(tkImplementation), &pFR)); + if(IsFfContainsNoMetaData(pFR->GetFlags()) + && pRecord->GetOffset()) + { + REPORT_ERROR1(VLDTR_E_MAR_BADOFFSET, tkImplementation); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + break; + default: + bIsValidImplementation = FALSE; + } + } + if(!bIsValidImplementation) + { + REPORT_ERROR1(VLDTR_E_MAR_BADIMPL, tkImplementation); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate the Offset into the PE file. + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateManifestResource() + +//***************************************************************************** +// Validate the given NestedClass. +//***************************************************************************** +HRESULT RegMeta::ValidateNestedClass(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + NestedClassRec *pRecord; // NestedClass record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save the current state. + VEContext veCtxt; // Context structure. + mdToken tkNested; + mdToken tkEncloser; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = rid; + veCtxt.uOffset = 0; + + // Get the NestedClass record. + IfFailGo(pMiniMd->GetNestedClassRecord(rid, &pRecord)); + tkNested = pMiniMd->getNestedClassOfNestedClass(pRecord); + tkEncloser = pMiniMd->getEnclosingClassOfNestedClass(pRecord); + + // Nested must be valid TypeDef + if((TypeFromToken(tkNested) != mdtTypeDef) || !IsValidToken(tkNested)) + { + REPORT_ERROR1(VLDTR_E_NC_BADNESTED, tkNested); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Encloser must be valid TypeDef + if((TypeFromToken(tkEncloser) != mdtTypeDef) || !IsValidToken(tkEncloser)) + { + REPORT_ERROR1(VLDTR_E_NC_BADENCLOSER, tkEncloser); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check for duplicates + { + RID N = pMiniMd->getCountNestedClasss(); + RID tmp; + NestedClassRec* pRecTmp; + mdToken tkEncloserTmp; + for(tmp = rid+1; tmp <= N; tmp++) + { + IfFailGo(pMiniMd->GetNestedClassRecord(tmp, &pRecTmp)); + if(tkNested == pMiniMd->getNestedClassOfNestedClass(pRecTmp)) + { + if(tkEncloser == (tkEncloserTmp = pMiniMd->getEnclosingClassOfNestedClass(pRecTmp))) + { + REPORT_ERROR1(VLDTR_E_NC_DUP, tmp); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + REPORT_ERROR3(VLDTR_E_NC_DUPENCLOSER, tkNested, tkEncloser, tkEncloserTmp); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateLocalVariable() + +//***************************************************************************** +// Given a Table ID and a Row ID, validate all the columns contain meaningful +// values given the column definitions. Validate that the offsets into the +// different pools are valid, the rids are within range and the coded tokens +// are valid. Every failure here is considered an error. +//***************************************************************************** +HRESULT RegMeta::ValidateRecord(ULONG ixTbl, RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save the current state. + ULONG ulCount; // Count of records in the table. + ULONG ulRawColVal; // Raw value of the column. + void *pRow; // Row with the data. + CMiniTableDef *pTbl; // Table definition. + CMiniColDef *pCol; // Column definition. + const CCodedTokenDef *pCdTkn; // Coded token definition. + ULONG ix; // Index into the array of coded tokens. + + BEGIN_ENTRYPOINT_NOTHROW; + + // Get the table definition. + pTbl = &pMiniMd->m_TableDefs[ixTbl]; + + // Get the row. We may assume that the Row pointer we get back from + // this call is correct since we do the verification on the Record + // pools for each table during the open sequence. The only place + // this is not valid is for Dynamic IL and we don't do this + // verification in that case since we go through IMetaData* APIs + // in that case and it should all be consistent. + IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, rid, &pRow)); + + for (ULONG ixCol = 0; ixCol < pTbl->m_cCols; ixCol++) + { + // Get the column definition. + pCol = &pTbl->m_pColDefs[ixCol]; + + // Get the raw value stored in the column. getIX currently doesn't + // handle byte sized fields, but there are some BYTE fields in the + // MetaData. So using the conditional to access BYTE fields. + if (pCol->m_cbColumn == 1) + ulRawColVal = pMiniMd->getI1(pRow, *pCol); + else + ulRawColVal = pMiniMd->getIX(pRow, *pCol); + + // Do some basic checks on the non-absurdity of the value stored in the + // column. + if (IsRidType(pCol->m_Type)) + { + // Verify that the RID is within range. + _ASSERTE(pCol->m_Type < pMiniMd->GetCountTables()); + ulCount = pMiniMd->GetCountRecs(pCol->m_Type); + // For records storing rids to pointer tables, the stored value may + // be one beyond the last record. + if (IsTblPtr(pCol->m_Type, ixTbl)) + ulCount++; + if (ulRawColVal > ulCount) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_RID_OUTOFRANGE, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if (IsCodedTokenType(pCol->m_Type)) + { + // Verify that the Coded token and rid are valid. + pCdTkn = &g_CodedTokens[pCol->m_Type - iCodedToken]; + ix = ulRawColVal & ~(-1 << CMiniMdRW::m_cb[pCdTkn->m_cTokens]); + if (ix >= pCdTkn->m_cTokens) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_CDTKN_OUTOFRANGE, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + ulCount = pMiniMd->GetCountRecs(TypeFromToken(pCdTkn->m_pTokens[ix]) >> 24); + if ( (ulRawColVal >> CMiniMdRW::m_cb[pCdTkn->m_cTokens]) > ulCount) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_CDRID_OUTOFRANGE, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if (IsHeapType(pCol->m_Type)) + { + // Verify that the offsets for the Heap type fields are valid offsets + // into the heaps. + switch (pCol->m_Type) + { + case iSTRING: + if (!pMiniMd->m_StringHeap.IsValidIndex(ulRawColVal)) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_STRING_INVALID, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + case iGUID: + if (ulRawColVal == 0) + { // GUID value 0 is valid value, though it's invalid GUID heap index + break; + } + if (!pMiniMd->m_GuidHeap.IsValidIndex(ulRawColVal)) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_GUID_INVALID, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + case iBLOB: + if (! pMiniMd->m_BlobHeap.IsValidIndex(ulRawColVal)) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_BLOB_INVALID, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + default: + _ASSERTE(!"Invalid heap type encountered!"); + } + } + else + { + // Not much checking that can be done on the fixed type in a generic sense. + _ASSERTE (IsFixedType(pCol->m_Type)); + } + hr = hrSave; + } +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateRecord() + +//***************************************************************************** +// This function validates that the given Method signature is consistent as per +// the compression scheme. +//***************************************************************************** +HRESULT RegMeta::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. +{ + VEContext veCtxt; // Context record. + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulSize; // Size of uncompressed data at each point. + HRESULT hr = S_OK; // Value returned. + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Check for NULL signature. + if (!cbSig) + { + REPORT_ERROR0(VLDTR_E_SIGNULL); + SetVldtrCode(&hr, VLDTR_S_ERR); + goto ErrExit; + } + + // Walk through the signature. At each point make sure there is enough + // room left in the signature based on the encoding in the current byte. + while (cbSig - ulCurByte) + { + _ASSERTE(ulCurByte <= cbSig); + // Get next chunk of uncompressed data size. + if ((ulSize = CorSigUncompressedDataSize(pbSig)) > (cbSig - ulCurByte)) + { + REPORT_ERROR1(VLDTR_E_SIGNODATA, ulCurByte+1); + SetVldtrCode(&hr, VLDTR_S_ERR); + goto ErrExit; + } + // Go past this chunk. + ulCurByte += ulSize; + CorSigUncompressData(pbSig); + } +ErrExit: + + return hr; +} // RegMeta::ValidateSigCompression() + +//***************************************************************************** +// This function validates one argument given an offset into the signature +// where the argument begins. This function assumes that the signature is well +// formed as far as the compression scheme is concerned. +//***************************************************************************** +//@GENERICS: todo: reject uninstantiated generic types used as types. +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::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 +{ + ULONG ulElementType; // Current element type being processed. + ULONG ulElemSize; // Size of the element type. + mdToken token; // Embedded token. + ULONG ulArgCnt; // Argument count for function pointer. + ULONG ulRank; // Rank of the array. + ULONG ulSizes; // Count of sized dimensions of the array. + ULONG ulLbnds; // Count of lower bounds of the array. + ULONG ulTkSize; // Token size. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them + BOOL bByRefForbidden = FALSE;// ByRef is not allowed for fields + + BEGIN_ENTRYPOINT_NOTHROW; + + switch(TypeFromToken(tk)) + { + case mdtFieldDef: + bByRefForbidden = TRUE; + break; + case mdtName: + tk = TokenFromRid(RidFromToken(tk),mdtFieldDef); + // Field type can be a FNPTR with a sig containing ByRefs. + // So we change the token type not to be mdtFieldDef and thus allow ByRefs, + // but the token needs to be restored to its original type for reporting + break; + } + + _ASSERTE (pulCurByte); + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + while(bRepeat) + { + bRepeat = FALSE; + // Validate that the argument is not missing. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + hr = VLDTR_E_SIG_MISSARG; + goto ErrExit; + } + + // Get the element type. + *pulCurByte += (ulElemSize = CorSigUncompressedDataSize(pbSig)); + ulElementType = CorSigUncompressData(pbSig); + + // Walk past all the modifier types. + while (ulElementType & ELEMENT_TYPE_MODIFIER) + { + _ASSERTE(*pulCurByte <= cbSig); + if(ulElementType == ELEMENT_TYPE_SENTINEL) + { + if(pulNSentinels) *pulNSentinels+=1; + if(TypeFromToken(tk) == mdtMethodDef) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTINMETHODDEF); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if (cbSig == *pulCurByte) + { + REPORT_ERROR0(VLDTR_E_SIG_LASTSENTINEL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + goto ErrExit; + } + } + if (cbSig == *pulCurByte) + { + REPORT_ERROR2(VLDTR_E_SIG_MISSELTYPE, ulElementType, *pulCurByte + 1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + *pulCurByte += (ulElemSize = CorSigUncompressedDataSize(pbSig)); + ulElementType = CorSigUncompressData(pbSig); + } + + switch (ulElementType) + { + case ELEMENT_TYPE_VOID: + if(bNoVoidAllowed) + { + IfBreakGo(m_pVEHandler->VEHandler(VLDTR_E_SIG_BADVOID, veCtxt, 0)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I: + break; + case ELEMENT_TYPE_BYREF: //fallthru + if(bByRefForbidden) + { + IfBreakGo(m_pVEHandler->VEHandler(VLDTR_E_SIG_BYREFINFIELD, veCtxt, 0)); + SetVldtrCode(&hr, hrSave); + } + case ELEMENT_TYPE_PTR: + // Validate the referenced type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,FALSE)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + break; + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SZARRAY: + // Validate the referenced type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,TRUE)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + break; + case ELEMENT_TYPE_VALUETYPE: //fallthru + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_CMOD_OPT: + case ELEMENT_TYPE_CMOD_REQD: + // See if the token is missing. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSTKN, ulElementType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // See if the token is a valid token. + ulTkSize = CorSigUncompressedDataSize(pbSig); + token = CorSigUncompressToken(pbSig); + if (!IsValidToken(token)) + { + REPORT_ERROR2(VLDTR_E_SIG_TKNBAD, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + *pulCurByte += ulTkSize; + break; + } + *pulCurByte += ulTkSize; + if ((ulElementType == ELEMENT_TYPE_CLASS) || (ulElementType == ELEMENT_TYPE_VALUETYPE)) + { + // Check for long-form encoding + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + LPCSTR szName = ""; // token's Name. + LPCSTR szNameSpace = ""; // token's NameSpace. + + + // Check for TypeDef or TypeRef + // To prevent cycles in metadata, token must not be a TypeSpec. + if ((TypeFromToken(token) != mdtTypeRef) && (TypeFromToken(token) != mdtTypeDef)) + { + REPORT_ERROR2(VLDTR_E_SIG_BADTOKTYPE, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (TypeFromToken(token) == mdtTypeRef) + { + TypeRefRec *pTokenRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(token), &pTokenRec)); + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pTokenRec); + if (RidFromToken(tkResScope) && (TypeFromToken(tkResScope) == mdtAssemblyRef)) + { + AssemblyRefRec * pARRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec)); + LPCSTR szAssemblyRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName)); + if((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName))) + { + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTokenRec, &szNameSpace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTokenRec, &szName)); + } + } + } + else if (TypeFromToken(token) == mdtTypeDef) + { + TypeDefRec *pTokenRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(token), &pTokenRec)); + if(g_fValidatingMscorlib) // otherwise don't even bother checking the name + { + IfFailGo(pMiniMd->getNameOfTypeDef(pTokenRec, &szName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTokenRec, &szNameSpace)); + } + // while at it, check if token is indeed a class (valuetype) + BOOL bValueType = FALSE; + if(!IsTdInterface(pTokenRec->GetFlags())) + { + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTokenRec); + if(RidFromToken(tkExtends)) + { + LPCSTR szExtName = ""; // parent's Name. + LPCSTR szExtNameSpace = ""; // parent's NameSpace. + if(TypeFromToken(tkExtends)==mdtTypeRef) + { + TypeRefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pExtRec)); + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pExtRec); + if(RidFromToken(tkResScope) && (TypeFromToken(tkResScope)==mdtAssemblyRef)) + { + AssemblyRefRec *pARRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec)); + LPCSTR szAssemblyRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName)); + if((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName))) + { + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtRec, &szExtNameSpace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pExtRec, &szExtName)); + } + } + } + else if(TypeFromToken(tkExtends)==mdtTypeDef) + { + if(g_fValidatingMscorlib) // otherwise don't even bother checking the name + { + TypeDefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtRec, &szExtNameSpace)); + } + } + if(0 == strcmp(szExtNameSpace,BASE_NAMESPACE)) + { + if(0==strcmp(szExtName,BASE_ENUM_CLASSNAME)) bValueType = TRUE; + else if(0==strcmp(szExtName,BASE_VTYPE_CLASSNAME)) + { + bValueType = (strcmp(szNameSpace,BASE_NAMESPACE) || + strcmp(szName,BASE_ENUM_CLASSNAME)); + } + } + } + } + if(bValueType != (ulElementType == ELEMENT_TYPE_VALUETYPE)) + { + REPORT_ERROR2(VLDTR_E_SIG_TOKTYPEMISMATCH, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + } + if(0 == strcmp(szNameSpace,BASE_NAMESPACE)) + { + for(unsigned jjj = 0; jjj < g_NumSigLongForms; jjj++) + { + if(0 == strcmp(szName,g_SigLongFormName[jjj])) + { + REPORT_ERROR2(VLDTR_E_SIG_LONGFORM, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + } + else // i.e. if(ELEMENT_TYPE_CMOD_OPT || ELEMENT_TYPE_CMOD_REQD) + bRepeat = TRUE; // go on validating, we're not done with this arg + break; + + case ELEMENT_TYPE_FNPTR: + // Validate that calling convention is present. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSFPTR, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume calling convention. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + + // Validate that argument count is present. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSFPTRARGCNT, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume argument count. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCnt = CorSigUncompressData(pbSig); + + // Checking the signature, ByRefs OK + if(bByRefForbidden) + tk = TokenFromRid(RidFromToken(tk),mdtName); + + // Validate and consume return type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,NULL,FALSE)); + if (hr != S_OK) + { + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + + // Validate and consume the arguments. + while(ulArgCnt--) + { + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,NULL,TRUE)); + if (hr != S_OK) + { + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + break; + + case ELEMENT_TYPE_ARRAY: + // Validate and consume the base type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,TRUE)); + + // Validate that the rank is present. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSRANK, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the rank. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulRank = CorSigUncompressData(pbSig); + + // Process the sizes. + if (ulRank) + { + // Validate that the count of sized-dimensions is specified. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSNSIZE, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the count of sized dimensions. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulSizes = CorSigUncompressData(pbSig); + + // Loop over the sizes. + while (ulSizes--) + { + // Validate the current size. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSSIZE, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the current size. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + } + + // Validate that the count of lower bounds is specified. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSNLBND, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the count of lower bound. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulLbnds = CorSigUncompressData(pbSig); + + // Loop over the lower bounds. + while (ulLbnds--) + { + // Validate the current lower bound. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSLBND, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the current size. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + } + } + break; + + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + // Consume index. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + break; + + case ELEMENT_TYPE_GENERICINST: + { + PCCOR_SIGNATURE pbGenericTypeSig = pbSig; + BOOL fCheckArity = FALSE; + ULONG ulGenericArity = 0; + + // Validate and consume the type constructor + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte, NULL, TRUE)); + + // Extract its arity + { + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + switch(CorSigUncompressElementType(pbGenericTypeSig)) + { + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + mdToken tkGenericType = CorSigUncompressToken(pbGenericTypeSig); + if (TypeFromToken(tkGenericType) == mdtTypeDef) + { + HENUMInternal hEnumTyPars; + hr = pMiniMd->FindGenericParamHelper(tkGenericType, &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulGenericArity)); + HENUMInternal::ClearEnum(&hEnumTyPars); + fCheckArity = TRUE; + } + ; + } + // for a mdtTypeRef, don't check anything until load time + break; + } + default: + break; + } + + } + + // Consume argument count. + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARITY, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCnt = CorSigUncompressData(pbSig); + + if (ulArgCnt == 0) + { + REPORT_ERROR1(VLDTR_E_SIG_ARITYZERO,*pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (fCheckArity && ulArgCnt != ulGenericArity) + { + REPORT_ERROR3(VLDTR_E_SIG_ARITYMISMATCH,ulGenericArity,ulArgCnt,*pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate and consume the arguments. + while(ulArgCnt--) + { + PCCOR_SIGNATURE pbTypeArg = pbSig; + ULONG ulTypeArgByte = *pulCurByte; + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte, NULL, TRUE)); + if (hr != S_OK) + { + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + + // reject byref-like args + switch (CorSigUncompressData(pbTypeArg)) + { + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_BYREF: + { + REPORT_ERROR1(VLDTR_E_SIG_BYREFINST, ulTypeArgByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + default: + break; + } + } + + break; + } + + + case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch + if(TypeFromToken(tk) == mdtMethodDef) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTINMETHODDEF); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + default: + REPORT_ERROR2(VLDTR_E_SIG_BADELTYPE, ulElementType, *pulCurByte - ulElemSize); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } // switch (ulElementType) + } // end while(bRepeat) + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateOneArg() +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +// This function validates the given Method signature. This function works +// with Method signature for both the MemberRef and MethodDef. +//***************************************************************************** +HRESULT RegMeta::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. +{ + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArgCount; // Count of arguments. + ULONG ulTyArgCount; // Count of type arguments. + ULONG i; // Looping index. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + ULONG ulNSentinels = 0; + + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(TypeFromToken(tk) == mdtMethodDef || + TypeFromToken(tk) == mdtMemberRef); + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Validate the signature is well-formed with respect to the compression + // scheme. If this fails, no further validation needs to be done. + if ((hr = ValidateSigCompression(tk, pbSig, cbSig)) != S_OK) + goto ErrExit; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + + i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK; + if ((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG) + || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) + { + REPORT_ERROR1(VLDTR_E_MD_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (TypeFromToken(tk) == mdtMethodDef) // MemberRefs have no flags available + { + // If HASTHIS is set on the calling convention, the method should not be static. + if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) && + IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_THISSTATIC, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // If HASTHIS is not set on the calling convention, the method should be static. + if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) && + !IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_NOTTHISNOTSTATIC, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the type argument count. + if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + if (i != IMAGE_CEE_CS_CALLCONV_DEFAULT) + { + REPORT_ERROR1(VLDTR_E_MD_GENERIC_BADCALLCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MD_MISSARITY, ulCurByte+1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulTyArgCount = CorSigUncompressData(pbSig); + + if (ulTyArgCount == 0) + { + REPORT_ERROR1(VLDTR_E_MD_ARITYZERO, ulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // If this is a def, check the arity against the number of generic params + if (TypeFromToken(tk) == mdtMethodDef) + { + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + ULONG ulGenericParamCount; + HENUMInternal hEnumTyPars; + hr = pMiniMd->FindGenericParamHelper(tk, &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulGenericParamCount)); + HENUMInternal::ClearEnum(&hEnumTyPars); + if (ulTyArgCount != ulGenericParamCount) + { + REPORT_ERROR2(VLDTR_E_MD_GPMISMATCH,ulTyArgCount,ulGenericParamCount); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + + // Is there any sig left for arguments? + _ASSERTE(ulCurByte <= cbSig); + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MD_NOARGCNT, ulCurByte+1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCount = CorSigUncompressData(pbSig); + + // Validate the return type and the arguments. +// for (i = 0; i < (ulArgCount + 1); i++) + for(i=1; ulCurByte < cbSig; i++) + { + hr = ValidateOneArg(tk, pbSig, cbSig, &ulCurByte,&ulNSentinels,(i > 1)); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i); + } + SetVldtrCode(&hr, hrSave); + hrSave = hr; + break; + } + } + if((ulNSentinels != 0) && (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_VARARG ))) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTMUSTVARARG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(ulNSentinels > 1) + { + REPORT_ERROR0(VLDTR_E_SIG_MULTSENTINELS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodSig() + +//***************************************************************************** +// This function validates the given Field signature. This function works +// with Field signature for both the MemberRef and FieldDef. +//***************************************************************************** +HRESULT RegMeta::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. +{ + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || + TypeFromToken(tk) == mdtMemberRef); + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_FIELD )) + { + REPORT_ERROR1(VLDTR_E_FD_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate the field. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, &ulCurByte,NULL,TRUE)); + SetVldtrCode(&hrSave, hr); + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFieldSig() + +//***************************************************************************** +// This is a utility function to allocate a one-dimensional zero-based safe +// array of variants. +//***************************************************************************** +static HRESULT _AllocSafeVariantArrayVector( // Return status. + VARIANT *rVar, // [IN] Variant array. + int cElem, // [IN] Size of the array. + SAFEARRAY **ppArray) // [OUT] Double pointer to SAFEARRAY. +{ + HRESULT hr = S_OK; + LONG i; + + _ASSERTE(rVar && cElem && ppArray); + + IfNullGo(*ppArray = SafeArrayCreateVector(VT_VARIANT, 0, cElem)); + for (i = 0; i < cElem; i++) + IfFailGo(SafeArrayPutElement(*ppArray, &i, &rVar[i])); +ErrExit: + return hr; +} // _AllocSafeVariantArrayVector() + +//***************************************************************************** +// Helper function for reporting error with no arguments +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context) +{ + HRESULT hr = S_OK; + + // + // MDValidator does not zero out the Context. Fix it here. This fix relies + // on the fact that MDValidator just uses the token and offset field of the + // context. + // + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, NULL)); +ErrExit: + + return hr; +} // _ValidateErrorHelper() + +//***************************************************************************** +// Helper function for reporting error with 1 argument +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1) +{ + HRESULT hr = S_OK; + SAFEARRAY *psa = 0; // The SAFEARRAY. + VARIANT rVar[1]; // The VARIANT array + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + V_VT(&rVar[0]) = VT_UI4; + V_UI4(&rVar[0]) = ulVal1; + IfFailGo(_AllocSafeVariantArrayVector(rVar, 1, &psa)); + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa)); + +ErrExit: + if (psa) + { + HRESULT hrSave = SafeArrayDestroy(psa); + if (FAILED(hrSave)) + hr = hrSave; + } + return hr; +} // _ValidateErrorHelper() + +//***************************************************************************** +// Helper function for reporting error with 2 arguments +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1, + ULONG ulVal2) +{ + HRESULT hr = S_OK; + SAFEARRAY *psa = 0; // The SAFEARRAY. + VARIANT rVar[2]; // The VARIANT array + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + V_VT(&rVar[0]) = VT_UI4; + V_UI4(&rVar[0]) = ulVal1; + V_VT(&rVar[1]) = VT_UI4; + V_UI4(&rVar[1]) = ulVal2; + + IfFailGo(_AllocSafeVariantArrayVector(rVar, 2, &psa)); + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa)); + +ErrExit: + if (psa) + { + HRESULT hrSave = SafeArrayDestroy(psa); + if (FAILED(hrSave)) + hr = hrSave; + } + return hr; +} // _ValidateErrorHelper() + +//***************************************************************************** +// Helper function for reporting error with 3 arguments +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1, + ULONG ulVal2, + ULONG ulVal3) +{ + HRESULT hr = S_OK; + SAFEARRAY *psa = 0; // The SAFEARRAY. + VARIANT rVar[3]; // The VARIANT array + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + V_VT(&rVar[0]) = VT_UI4; + V_UI4(&rVar[0]) = ulVal1; + V_VT(&rVar[1]) = VT_UI4; + V_UI4(&rVar[1]) = ulVal2; + V_VT(&rVar[2]) = VT_UI4; + V_UI4(&rVar[2]) = ulVal3; + + IfFailGo(_AllocSafeVariantArrayVector(rVar, 3, &psa)); + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa)); + +ErrExit: + if (psa) + { + HRESULT hrSave = SafeArrayDestroy(psa); + if (FAILED(hrSave)) + hr = hrSave; + } + return hr; +} + +//***************************************************************************** +// Helper function to see if there is a duplicate record for ClassLayout. +//***************************************************************************** +static HRESULT _FindClassLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdTypeDef tkParent, // [IN] the parent that ClassLayout is associated with + RID *pclRid, // [OUT] rid for the ClassLayout. + RID rid) // [IN] rid to be ignored. +{ + HRESULT hr; + ULONG cClassLayoutRecs; + ClassLayoutRec *pRecord; + mdTypeDef tkParTmp; + ULONG i; + + _ASSERTE(pMiniMd && pclRid && rid); + _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef && RidFromToken(tkParent)); + + cClassLayoutRecs = pMiniMd->getCountClassLayouts(); + + for (i = 1; i <= cClassLayoutRecs; i++) + { + // Ignore the rid to be ignored! + if (rid == i) + continue; + + IfFailRet(pMiniMd->GetClassLayoutRecord(i, &pRecord)); + tkParTmp = pMiniMd->getParentOfClassLayout(pRecord); + if (tkParTmp == tkParent) + { + *pclRid = i; + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // _FindClassLayout() + +//***************************************************************************** +// Helper function to see if there is a duplicate for FieldLayout. +//***************************************************************************** +static HRESULT _FindFieldLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdFieldDef tkParent, // [IN] the parent that FieldLayout is associated with + RID *pflRid, // [OUT] rid for the FieldLayout record. + RID rid) // [IN] rid to be ignored. +{ + HRESULT hr; + ULONG cFieldLayoutRecs; + FieldLayoutRec *pRecord; + mdFieldDef tkField; + ULONG i; + + _ASSERTE(pMiniMd && pflRid && rid); + _ASSERTE(TypeFromToken(tkParent) == mdtFieldDef && RidFromToken(tkParent)); + + cFieldLayoutRecs = pMiniMd->getCountFieldLayouts(); + + for (i = 1; i <= cFieldLayoutRecs; i++) + { + // Ignore the rid to be ignored! + if (rid == i) + continue; + + IfFailRet(pMiniMd->GetFieldLayoutRecord(i, &pRecord)); + tkField = pMiniMd->getFieldOfFieldLayout(pRecord); + if (tkField == tkParent) + { + *pflRid = i; + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // _FindFieldLayout() + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::CompareMethodSignature() +{ + HRESULT hr = S_OK; + + EX_TRY + { + hr = _CompareMethodSignature(); + } + EX_CATCH + { + hr = E_FAIL; + } + EX_END_CATCH(SwallowAllExceptions) + + return hr; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareMethodSignature() +{ + HRESULT hr; + + // Test equivalency of method signature header + ULONG cArgs; + IfFailRet(_CompareMethodSignatureHeader(cArgs)); + + // Iterate for cArgs + 1 to include the return type + for (ULONG i = 0; i < cArgs + 1; i++) + { + IfFailRet(_CompareExactlyOne()); + } + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareExactlyOne() +{ + HRESULT hr; + + CorElementType typ1, typ2; + IfFailRet(m_sig1.GetElemType(&typ1)); + IfFailRet(m_sig2.GetElemType(&typ2)); + + if (typ1 != typ2) + { + return E_FAIL; + } + + CorElementType typ = typ1; + if (!CorIsPrimitiveType((CorElementType)typ)) + { + switch (typ) + { + default: + { + // _ASSERT(!"Illegal or unimplement type in COM+ sig."); + return META_E_BAD_SIGNATURE; + break; + } + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + { + IfFailRet(_CompareData(NULL)); // Skip variable number + break; + } + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_TYPEDBYREF: + { + break; + } + + case ELEMENT_TYPE_BYREF: // fallthru + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SZARRAY: + { + IfFailRet(_CompareExactlyOne()); // Compare referenced type + break; + } + + case ELEMENT_TYPE_VALUETYPE: // fallthru + case ELEMENT_TYPE_CLASS: + { + mdToken tok1, tok2; + IfFailRet(m_sig1.GetToken(&tok1)); + IfFailRet(m_sig2.GetToken(&tok2)); + IfFailRet(m_comparer.CompareToken(tok1, tok2)); + break; + } + + case ELEMENT_TYPE_FNPTR: + { + IfFailRet(_CompareMethodSignature()); + break; + } + + case ELEMENT_TYPE_ARRAY: + { + IfFailRet(_CompareExactlyOne()); // Compare element type + + ULONG rank; + IfFailRet(_CompareData(&rank)); // Compare & get rank + + if (rank) + { + ULONG nsizes; + IfFailRet(_CompareData(&nsizes)); // Compare & get # of sizes + while (nsizes--) + { + IfFailRet(_CompareData(NULL)); // Compare size + } + + ULONG nlbounds; + IfFailRet(_CompareData(&nlbounds)); // Compare & get # of lower bounds + while (nlbounds--) + { + IfFailRet(_CompareData(NULL)); // Compare lower bounds + } + } + + break; + } + + case ELEMENT_TYPE_SENTINEL: + { + // Should be unreachable since GetElem strips it + break; + } + + case ELEMENT_TYPE_INTERNAL: + { + // Shouldn't ever get this since it is internal to the runtime, + // but just in case we know how to compare and skip these. + PVOID val1 = *((PVOID *)m_sig1.m_ptr); + PVOID val2 = *((PVOID *)m_sig2.m_ptr); + + if (val1 != val2) + { + return E_FAIL; + } + + m_sig1.SkipBytes(sizeof(void*)); + m_sig2.SkipBytes(sizeof(void*)); + break; + } + + case ELEMENT_TYPE_GENERICINST: + { + IfFailRet(_CompareExactlyOne()); // Compare generic type + ULONG argCnt; + IfFailRet(_CompareData(&argCnt)); // Compare & get number of parameters + _ASSERTE(argCnt > 0); + while (argCnt--) + { + IfFailRet(_CompareExactlyOne()); // Compare the parameters + } + break; + } + } + } + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareData( + ULONG *pulData) +{ + ULONG cbCompressedData1, cbCompressedData2; + ULONG ulData1, ulData2; + + cbCompressedData1 = CorSigUncompressData(m_sig1.m_ptr, &ulData1); + cbCompressedData2 = CorSigUncompressData(m_sig2.m_ptr, &ulData2); + + if ((cbCompressedData1 == ((ULONG)(-1))) || + (cbCompressedData2 == ((ULONG)(-1))) || + (cbCompressedData1 != cbCompressedData2) || + (ulData1 != ulData2)) + { + return E_FAIL; + } + + m_sig1.SkipBytes(cbCompressedData1); + m_sig2.SkipBytes(cbCompressedData2); + + // Out data + if (pulData) + *pulData = ulData1; + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareMethodSignatureHeader( + ULONG &cArgs) +{ + HRESULT hr; + + // Get calling convention information, but only use it to get type param information. + ULONG uCallConv1, uCallConv2; + IfFailRet(m_sig1.GetData(&uCallConv1)); + IfFailRet(m_sig2.GetData(&uCallConv2)); + + // Check type parameter information + ULONG uTypeParamCount1 = 0; + ULONG uTypeParamCount2 = 0; + + if (uCallConv1 & IMAGE_CEE_CS_CALLCONV_GENERIC) + IfFailRet(m_sig1.GetData(&uTypeParamCount1)); + + if (uCallConv2 & IMAGE_CEE_CS_CALLCONV_GENERIC) + IfFailRet(m_sig2.GetData(&uTypeParamCount2)); + + if (uTypeParamCount1 != uTypeParamCount2) + { + return E_FAIL; + } + + // Get arg count + ULONG cArgs1, cArgs2; + IfFailRet(m_sig1.GetData(&cArgs1)); + IfFailRet(m_sig2.GetData(&cArgs2)); + + if (cArgs1 != cArgs2) + { + return E_FAIL; + } + + // Out parameter + cArgs = cArgs1; + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** + +#ifdef FEATURE_FUSION +HRESULT +UnifiedAssemblySigComparer::_CreateIAssemblyNameFromAssemblyRef( + mdToken tkAsmRef, + IAssemblyName **ppAsmName) +{ + HRESULT hr; + + void const * pvPublicKey; + ULONG cbPublicKey; + ULONG cchName; + ASSEMBLYMETADATA amd; + void const * pvHashValue; + ULONG cbHashValue; + DWORD dwFlags; + + ZeroMemory(&amd, sizeof(amd)); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef, + NULL, + NULL, + NULL, + 0, + &cchName, + &amd, + NULL, + NULL, + NULL)); + + StackSString ssName; + StackSString ssLocale; + amd.szLocale = ssLocale.OpenUnicodeBuffer(amd.cbLocale); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef, + &pvPublicKey, + &cbPublicKey, + ssName.OpenUnicodeBuffer(cchName), + cchName, + &cchName, + &amd, + &pvHashValue, + &cbHashValue, + &dwFlags)); + + ssName.CloseBuffer(); + ssLocale.CloseBuffer(); + + IAssemblyName *pAsmName = NULL; + + IfFailRet(CreateAssemblyNameObject(&pAsmName, + ssName.GetUnicode(), + CANOF_SET_DEFAULT_VALUES, + NULL)); + + // Set the public key token + IfFailRet(pAsmName->SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, + (LPVOID)pvPublicKey, + cbPublicKey)); + + // Set the culture + if (amd.cbLocale == 0 || amd.szLocale == NULL) + { + IfFailRet(pAsmName->SetProperty(ASM_NAME_CULTURE, + W("Neutral"), + sizeof(W("Neutral")))); + } + else + { + IfFailRet(pAsmName->SetProperty(ASM_NAME_CULTURE, + amd.szLocale, + amd.cbLocale)); + } + + // Set the major version + IfFailRet(pAsmName->SetProperty(ASM_NAME_MAJOR_VERSION, + &amd.usMajorVersion, + sizeof(amd.usMajorVersion))); + + // Set the minor version + IfFailRet(pAsmName->SetProperty(ASM_NAME_MINOR_VERSION, + &amd.usMinorVersion, + sizeof(amd.usMinorVersion))); + + // Set the build number + IfFailRet(pAsmName->SetProperty(ASM_NAME_BUILD_NUMBER, + &amd.usBuildNumber, + sizeof(amd.usBuildNumber))); + + // Set the revision number + IfFailRet(pAsmName->SetProperty(ASM_NAME_REVISION_NUMBER, + &amd.usRevisionNumber, + sizeof(amd.usRevisionNumber))); + + *ppAsmName = pAsmName; + + return S_OK; +} + +//***************************************************************************** +// Define holder to release IAssemblyName on exception. +//***************************************************************************** +void UnifiedAssemblySigComparer_IAssemblyNameRelease(IAssemblyName *value) +{ + if (value != NULL) + { + value->Release(); + } +} + +typedef Holder<IAssemblyName*, + DoNothing<IAssemblyName*>, + &UnifiedAssemblySigComparer_IAssemblyNameRelease, + NULL> UnifiedAssemblySigComparer_IAssemblyNameHolder; + +#endif // FEATURE_FUSION + +#ifndef FEATURE_FUSION +HRESULT UnifiedAssemblySigComparer::_CompareAssemblies(mdToken tkAsmRef1,mdToken tkAsmRef2, BOOL* pfEquivalent) +{ + + HRESULT hr; + void const * pvPublicKey1; + ULONG cbPublicKey1; + ULONG cchName1; + ASSEMBLYMETADATA amd1; + void const * pvHashValue; + ULONG cbHashValue; + DWORD dwFlags1; + + void const * pvPublicKey2; + ULONG cbPublicKey2; + ULONG cchName2; + ASSEMBLYMETADATA amd2; + DWORD dwFlags2; + + + ZeroMemory(&amd1, sizeof(amd1)); + ZeroMemory(&amd2, sizeof(amd2)); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef1, + NULL, + NULL, + NULL, + 0, + &cchName1, + &amd1, + NULL, + NULL, + NULL)); + + StackSString ssName1; + StackSString ssLocale1; + amd1.szLocale = ssLocale1.OpenUnicodeBuffer(amd1.cbLocale); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef1, + &pvPublicKey1, + &cbPublicKey1, + ssName1.OpenUnicodeBuffer(cchName1), + cchName1, + &cchName1, + &amd1, + &pvHashValue, + &cbHashValue, + &dwFlags1)); + + ssName1.CloseBuffer(); + ssLocale1.CloseBuffer(); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef2, + NULL, + NULL, + NULL, + 0, + &cchName2, + &amd2, + NULL, + NULL, + NULL)); + + StackSString ssName2; + StackSString ssLocale2; + amd2.szLocale = ssLocale2.OpenUnicodeBuffer(amd2.cbLocale); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef2, + &pvPublicKey2, + &cbPublicKey2, + ssName2.OpenUnicodeBuffer(cchName2), + cchName2, + &cchName2, + &amd2, + &pvHashValue, + &cbHashValue, + &dwFlags2)); + + ssName2.CloseBuffer(); + ssLocale2.CloseBuffer(); + + StackSString sMscorlib(W("mscorlib")); + + + if(ssName1.CompareCaseInsensitive(sMscorlib)==0 && + ssName2.CompareCaseInsensitive(sMscorlib)==0 ) + { + *pfEquivalent=TRUE; + return S_OK; + } + + *pfEquivalent=FALSE; + + if (ssName1.CompareCaseInsensitive(ssName2)!=0) + return S_OK; + if (ssLocale1.CompareCaseInsensitive(ssLocale2)!=0) + return S_OK; + if(cbPublicKey1!=cbPublicKey2) + return S_OK; + if(memcmp(pvPublicKey1,pvPublicKey2,cbPublicKey1)!=0) + return S_OK; + if(dwFlags1!=dwFlags2) + return S_OK; + if(amd1.usMajorVersion!=amd2.usMajorVersion) + return S_OK; + if(amd1.usMinorVersion!=amd2.usMinorVersion) + return S_OK; + if(amd1.usBuildNumber!=amd2.usBuildNumber) + return S_OK; + if(amd1.usRevisionNumber!=amd2.usRevisionNumber) + return S_OK; + + *pfEquivalent=TRUE; + return S_OK; + +}; +#endif // FEATURE_FUSION + +//***************************************************************************** +//***************************************************************************** +HRESULT +UnifiedAssemblySigComparer::_CreateTypeNameFromTypeRef( + mdToken tkTypeRef, + SString &ssName, + mdToken &tkParent) +{ + HRESULT hr; + + // Get the parent token as well as the name, and return. + ULONG cchTypeRef; + IfFailRet(m_pRegMeta->GetTypeRefProps(tkTypeRef, NULL, NULL, 0, &cchTypeRef)); + IfFailRet(m_pRegMeta->GetTypeRefProps(tkTypeRef, &tkParent, ssName.OpenUnicodeBuffer(cchTypeRef), cchTypeRef, NULL)); + ssName.CloseBuffer(); + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +UnifiedAssemblySigComparer::_CreateFullyQualifiedTypeNameFromTypeRef( + mdToken tkTypeRef, + SString &ssFullName, + mdToken &tkParent) +{ + HRESULT hr; + + StackSString ssBuf; + StackSString ssName; + mdToken tok = tkTypeRef; + BOOL fFirstLoop = TRUE; + + // Loop stops at first non-typeref parent token. + do + { + // Get the name for this token, as well as the parent token value. + IfFailRet(_CreateTypeNameFromTypeRef(tok, ssName, tok)); + + // If this is the first time through the loop, just assign values. + if (fFirstLoop) + { + ssFullName = ssName; + fFirstLoop = FALSE; + } + // If this isn't the first time through, make nested type name + else + { + ns::MakeNestedTypeName(ssBuf, ssName, ssFullName); + ssFullName = ssBuf; + } + } while (TypeFromToken(tok) == mdtTypeRef); + + // Assign non-typeref token parent + tkParent = tok; + + return S_OK; +} + + + +//***************************************************************************** +//***************************************************************************** +HRESULT +UnifiedAssemblySigComparer::CompareToken( + const mdToken &tok1, + const mdToken &tok2) +{ + HRESULT hr; + + // Check binary equality + if (tok1 == tok2) + { + return S_OK; + } + + // Currently only want to do extra checking on TypeRefs + if (TypeFromToken(tok1) != mdtTypeRef || TypeFromToken(tok2) != mdtTypeRef) + { + return E_FAIL; + } + + // Get the fully qualified type names as well as the non-typeref parents. + mdToken tkParent1, tkParent2; + StackSString ssName1, ssName2; + + IfFailRet(_CreateFullyQualifiedTypeNameFromTypeRef(tok1, ssName1, tkParent1)); + IfFailRet(_CreateFullyQualifiedTypeNameFromTypeRef(tok2, ssName2, tkParent2)); + + // Currently only want to do extra checking if the parent tokens are AssemblyRefs + if (TypeFromToken(tkParent1) != mdtAssemblyRef || TypeFromToken(tkParent2) != mdtAssemblyRef) + { + return E_FAIL; + } + + // If the type names are not equal, no need to check the assembly refs for unification since + // we know the types couldn't possibly match. + if (!ssName1.Equals(ssName2)) + { + return E_FAIL; + } + BOOL fEquivalent; + +#ifdef FEATURE_FUSION //move into _CompareAssemblies + IAssemblyName *pAsmName1 = NULL; + IfFailRet(_CreateIAssemblyNameFromAssemblyRef(tkParent1, &pAsmName1)); + UnifiedAssemblySigComparer_IAssemblyNameHolder anh1(pAsmName1); + + IAssemblyName *pAsmName2 = NULL; + IfFailRet(_CreateIAssemblyNameFromAssemblyRef(tkParent2, &pAsmName2)); + UnifiedAssemblySigComparer_IAssemblyNameHolder anh2(pAsmName2); + + DWORD cchDisplayName = 0; + + StackSString ssDisplayName1; + pAsmName1->GetDisplayName(NULL, &cchDisplayName, NULL); + IfFailRet(pAsmName1->GetDisplayName(ssDisplayName1.OpenUnicodeBuffer(cchDisplayName), &cchDisplayName, NULL)); + ssDisplayName1.CloseBuffer(); + + StackSString ssDisplayName2; + pAsmName2->GetDisplayName(NULL, &cchDisplayName, NULL); + IfFailRet(pAsmName2->GetDisplayName(ssDisplayName2.OpenUnicodeBuffer(cchDisplayName), &cchDisplayName, NULL)); + ssDisplayName2.CloseBuffer(); + + AssemblyComparisonResult res; + IfFailRet(CompareAssemblyIdentity(ssDisplayName1.GetUnicode(), + TRUE, + ssDisplayName2.GetUnicode(), + TRUE, + &fEquivalent, + &res)); +#else + // no redirects supported + IfFailRet(_CompareAssemblies(tkParent1,tkParent2,&fEquivalent)); +#endif + + if (!fEquivalent) + { + return E_FAIL; + } + + return S_OK; +} + + +//***************************************************************************** +// Helper function to validate a locale. +//***************************************************************************** +static const char* const g_szValidLocale_V1[] = { +"ar","ar-SA","ar-IQ","ar-EG","ar-LY","ar-DZ","ar-MA","ar-TN","ar-OM","ar-YE","ar-SY","ar-JO","ar-LB","ar-KW","ar-AE","ar-BH","ar-QA", +"bg","bg-BG", +"ca","ca-ES", +"zh-CHS","zh-TW","zh-CN","zh-HK","zh-SG","zh-MO","zh-CHT", +"cs","cs-CZ", +"da","da-DK", +"de","de-DE","de-CH","de-AT","de-LU","de-LI", +"el","el-GR", +"en","en-US","en-GB","en-AU","en-CA","en-NZ","en-IE","en-ZA","en-JM","en-CB","en-BZ","en-TT","en-ZW","en-PH", +"es","es-ES-Ts","es-MX","es-ES","es-GT","es-CR","es-PA","es-DO","es-VE","es-CO","es-PE","es-AR","es-EC","es-CL", +"es-UY","es-PY","es-BO","es-SV","es-HN","es-NI","es-PR", +"fi","fi-FI", +"fr","fr-FR","fr-BE","fr-CA","fr-CH","fr-LU","fr-MC", +"he","he-IL", +"hu","hu-HU", +"is","is-IS", +"it","it-IT","it-CH", +"ja","ja-JP", +"ko","ko-KR", +"nl","nl-NL","nl-BE", +"no", +"nb-NO", +"nn-NO", +"pl","pl-PL", +"pt","pt-BR","pt-PT", +"ro","ro-RO", +"ru","ru-RU", +"hr","hr-HR", +"sk","sk-SK", +"sq","sq-AL", +"sv","sv-SE","sv-FI", +"th","th-TH", +"tr","tr-TR", +"ur","ur-PK", +"id","id-ID", +"uk","uk-UA", +"be","be-BY", +"sl","sl-SI", +"et","et-EE", +"lv","lv-LV", +"lt","lt-LT", +"fa","fa-IR", +"vi","vi-VN", +"hy","hy-AM", +"az", +"eu","eu-ES", +"mk","mk-MK", +"af","af-ZA", +"ka","ka-GE", +"fo","fo-FO", +"hi","hi-IN", +"ms","ms-MY","ms-BN", +"kk","kk-KZ", +"ky","ky-KZ", +"sw","sw-KE", +"uz", +"tt","tt-RU", +"pa","pa-IN", +"gu","gu-IN", +"ta","ta-IN", +"te","te-IN", +"kn","kn-IN", +"mr","mr-IN", +"sa","sa-IN", +"mn","mn-MN", +"gl","gl-ES", +"kok","kok-IN", +"syr","syr-SY", +"div" +}; + +static const char* const g_szValidLocale_V2[] = { + "bn", "bn-IN", + "bs-Latn-BA", "bs-Cyrl-BA", + "hr-BA", + "fil", "fil-PH", + "fy", "fy-NL", + "iu-Latn-CA", + "ga", "ga-IE", + "ky-KG", + "lb", "lb-LU", + "ml", "ml-IN", + "mt", "mt-MT", + "mi", "mi-NZ", + "arn", "arn-CL", + "moh", "moh-CA", + "ne", "ne-NP", + "ps", "ps-AF", + "quz", "quz-BO", "quz-EC", "quz-PE", + "rm", "rm-CH", + "smn", "smn-FI", + "smj" , "smj-SE", "smj-NO", + "se", "se-NO", "se-SE", "se-FI", + "sms", "sms-FI", + "sma", "sma-NO", "sma-SE", + "sr-Latn-BA", "sr-Cyrl-BA", + "nso", "nso-ZA" +}; + +// Pre-vista specific cultures (renamed on Vista) +static const char* const g_szValidLocale_PreVista[] = { + "div-MV", + "sr-SP-Latn", "sr-SP-Cyrl", + "az-AZ-Latn", "az-AZ-Cyrl", + "uz-UZ-Latn", "uz-UZ-Cyrl", +}; + +// Vista only specific cultures (renamed and freshly introduced) +static const char * const g_szValidLocale_Vista[] = { + "dv-MV", + "sr-Latn-CS", "sr-Cyrl-CS", + "az-Latn-AZ", "az-Cyrl-AZ", + "uz-Latn-UZ", "uz-Cyrl-UZ", + "zh-Hant", "zh-Hans", + "gsw", "gsw-FR", + "am", "am-ET", + "as", "as-IN", + "ba", "ba-RU", + "br", "br-FR", + "en-IN", + "kl", "kl-GL", + "iu-Cans-CA", + "km", "km-KH", + "lo", "lo-LA", + "dsb", "dsb-DE", + "mn-Mong-CN", + "oc", "oc-FR", + "or", "or-IN" +}; + +static BOOL FindInArray(LPCUTF8 szLocale, const char * const *cultureArr, const int nCultures) +{ + for (int i = 0; i < nCultures; i++) + { + if(!SString::_stricmp(szLocale, cultureArr[i])) + return TRUE; + } + return FALSE; +} + +#define LENGTH_OF(x) (sizeof(x) / sizeof(x[0])) + +// For Everett assemblies, only the preVista cultures are valid even if running on Vista. +static BOOL _IsValidLocale(LPCUTF8 szLocale, + BOOL fIsV2Assembly) +{ + if (szLocale && *szLocale) + { + // Locales valid for Everett and Whidbey + if (FindInArray(szLocale, g_szValidLocale_V1, LENGTH_OF(g_szValidLocale_V1))) + return TRUE; + + // Locales valid for Whidbey assemblies only + if (fIsV2Assembly && + FindInArray(szLocale, g_szValidLocale_V2, LENGTH_OF(g_szValidLocale_V2))) + return TRUE; + + // Finally search OS specific cultures + if (fIsV2Assembly) + return FindInArray(szLocale, g_szValidLocale_Vista, LENGTH_OF(g_szValidLocale_Vista)); + else + return FindInArray(szLocale, g_szValidLocale_PreVista, LENGTH_OF(g_szValidLocale_PreVista)); + } + + return TRUE; +} + +#endif //FEATURE_METADATA_VALIDATOR diff --git a/src/md/compiler/newmerger.cpp b/src/md/compiler/newmerger.cpp new file mode 100644 index 0000000000..d5199bb570 --- /dev/null +++ b/src/md/compiler/newmerger.cpp @@ -0,0 +1,6303 @@ +// 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. +//***************************************************************************** +// NewMerger.cpp +// + +// +// contains utility code to MD directory +// +// This file provides Compiler Support functionality in metadata. +//***************************************************************************** +#include "stdafx.h" + +#include "newmerger.h" +#include "regmeta.h" + + +#include "importhelper.h" +#include "rwutil.h" +#include "mdlog.h" +#include <posterror.h> +#include <sstring.h> +#include "ndpversion.h" + +#ifdef FEATURE_METADATA_EMIT_ALL + +#define MODULEDEFTOKEN TokenFromRid(1, mdtModule) + +#define COR_MSCORLIB_NAME "mscorlib" +#define COR_MSCORLIB_TYPEREF {0xb7, 0x7a, 0x5c, 0x56,0x19,0x34,0xe0,0x89} + +#define COR_CONSTRUCTOR_METADATA_IDENTIFIER W(".ctor") + +#define COR_COMPILERSERVICE_NAMESPACE "System.Runtime.CompilerServices" +#define COR_EXCEPTIONSERVICE_NAMESPACE "System.Runtime.ExceptionServices" +#define COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE "SuppressMergeCheckAttribute" +#define COR_HANDLE_PROCESS_CORRUPTED_STATE_EXCEPTION_ATTRIBUTE "HandleProcessCorruptedStateExceptionsAttribute" +#define COR_MISCBITS_NAMESPACE "Microsoft.VisualC" +#define COR_MISCBITS_ATTRIBUTE "Microsoft.VisualC.MiscellaneousBitsAttribute" +#define COR_NATIVECPPCLASS_ATTRIBUTE "System.Runtime.CompilerServices.NativeCppClassAttribute" + +// MODULE_CA_LOCATION W("System.Runtime.CompilerServices.AssemblyAttributesGoHere") +#define MODULE_CA_TYPENAME "AssemblyAttributesGoHere" // fake assembly type-ref for hanging Assembly-level CAs off of + +//***************************************************************************** +// BEGIN: Security Critical Attributes and Enumeration +//***************************************************************************** +#define COR_SECURITYCRITICALSCOPE_ENUM_W W("System.Security.SecurityCriticalScope") + +#define COR_SECURITYCRITICAL_ATTRIBUTE_FULL "System.Security.SecurityCriticalAttribute" +#define COR_SECURITYTRANSPARENT_ATTRIBUTE_FULL "System.Security.SecurityTransparentAttribute" +#define COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL "System.Security.SecurityTreatAsSafeAttribute" + +#define COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W W("System.Security.SecurityCriticalAttribute") +#define COR_SECURITYTRANSPARENT_ATTRIBUTE_FULL_W W("System.Security.SecurityTransparentAttribute") +#define COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL_W W("System.Security.SecurityTreatAsSafeAttribute") +#define COR_SECURITYSAFECRITICAL_ATTRIBUTE_FULL_W W("System.Security.SecuritySafeCriticalAttribute") + + // definitions of enumeration for System.Security.SecurityCriticalScope (Explicit or Everything) +#define COR_SECURITYCRITICAL_CTOR_ARGCOUNT_NO_SCOPE 0 +#define COR_SECURITYCRITICAL_CTOR_ARGCOUNT_SCOPE_EVERYTHING 1 +#define COR_SECURITYCRITICAL_CTOR_NO_SCOPE_SIG_MAX_SIZE (3) +#define COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE (5 + sizeof(mdTypeRef) * 1) + +#define COR_SECURITYCRITICAL_ATTRIBUTE_NAMESPACE "System.Security" +#define COR_SECURITYCRITICAL_ATTRIBUTE "SecurityCriticalAttribute" +#define COR_SECURITYTRANSPARENT_ATTRIBUTE_NAMESPACE "System.Security" +#define COR_SECURITYTRANSPARENT_ATTRIBUTE "SecurityTransparentAttribute" +#define COR_SECURITYTREATASSAFE_ATTRIBUTE_NAMESPACE "System.Security" +#define COR_SECURITYTREATASSAFE_ATTRIBUTE "SecurityTreatAsSafeAttribute" +#define COR_SECURITYSAFECRITICAL_ATTRIBUTE "SecuritySafeCriticalAttribute" + + +#define COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EVERYTHING { 0x01, 0x00 ,0x01, 0x00, 0x00, 0x00 ,0x00, 0x00 } +#define COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EXPLICIT {0x01, 0x00, 0x00 ,0x00} +#define COR_SECURITYTREATASSAFE_ATTRIBUTE_VALUE {0x01, 0x00, 0x00 ,0x00} + + + // if true, then registry has been read for enabling or disabling SecurityCritical support +static BOOL g_fRefShouldMergeCriticalChecked = FALSE; + +// by default, security critical attributes will be merged (e.g. unmarked CRT marked Critical/TAS) +// - unless registry config explicitly disables merging +static BOOL g_fRefShouldMergeCritical = TRUE; +//***************************************************************************** +// END: Security Critical Attributes and Enumeration +//***************************************************************************** + +//***************************************************************************** +// Checks to see if the given type is managed or native. We'll key off of the +// Custom Attribute "Microsoft.VisualC.MiscellaneousBitsAttribute". If the third +// byte has the 01000000 bit set then it is an unmanaged type. +// If we can't find the attribute, we will also check for the presence of the +// "System.Runtime.CompilerServices.NativeCppClassAttribute" Custom Attribute +// since the CPP compiler stopped emitting MiscellaneousBitsAttribute in Dev11. +//***************************************************************************** +HRESULT IsManagedType(CMiniMdRW* pMiniMd, + mdTypeDef td, + BOOL *fIsManagedType) +{ + // First look for the custom attribute + HENUMInternal hEnum; + HRESULT hr = S_OK; + + IfFailRet(pMiniMd->CommonEnumCustomAttributeByName(td, COR_MISCBITS_ATTRIBUTE, false, &hEnum)); + + // If there aren't any custom attributes here, then this must be a managed type + if (hEnum.m_ulCount > 0) + { + // Let's loop through these, and see if any of them have that magical bit set. + mdCustomAttribute ca; + CustomAttributeRec *pRec; + ULONG cbData = 0; + + while(HENUMInternal::EnumNext(&hEnum, &ca)) + { + const BYTE* pData = NULL; + + IfFailGo(pMiniMd->GetCustomAttributeRecord(RidFromToken(ca), &pRec)); + IfFailGo(pMiniMd->getValueOfCustomAttribute(pRec, &pData, &cbData)); + + if (pData != NULL && cbData >=3) + { + // See if the magical bit is set to make this an unmanaged type + if ((*(pData+2)&0x40) > 0) + { + // Yes, this is an unmanaged type + HENUMInternal::ClearEnum(&hEnum); + *fIsManagedType = FALSE; + return S_OK; + } + } + } + + } + + HENUMInternal::ClearEnum(&hEnum); + + // If this was emitted by a Dev11+ CPP compiler, we only have NativeCppClassAttribute + // so let's check for that before calling this a managed class. + IfFailRet(pMiniMd->CommonEnumCustomAttributeByName(td, COR_NATIVECPPCLASS_ATTRIBUTE, false, &hEnum)); + if (hEnum.m_ulCount > 0) + { + // Yes, this is an unmanaged type + HENUMInternal::ClearEnum(&hEnum); + *fIsManagedType = FALSE; + return S_OK; + } + + // Nope, this isn't an unmanaged type.... must be managed + HENUMInternal::ClearEnum(&hEnum); + *fIsManagedType = TRUE; + hr = S_OK; +ErrExit: + return hr; +}// IsManagedType + + +//***************************************************************************** +// "Is CustomAttribute from certain namespace and assembly" check helper +// Returns S_OK and fills **ppTypeRefRec. +// Returns error code or S_FALSE otherwise as not found and fills **ppTypeRefRec with NULL. +//***************************************************************************** +HRESULT IsAttributeFromNamespace( + CMiniMdRW *pMiniMd, + mdToken tk, + LPCSTR szNamespace, + LPCSTR szAssembly, + TypeRefRec **ppTypeRefRec) +{ + HRESULT hr = S_OK; + if(TypeFromToken(tk) == mdtMemberRef) + { + MemberRefRec *pMemRefRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tk), &pMemRefRec)); + tk = pMiniMd->getClassOfMemberRef(pMemRefRec); + } + if(TypeFromToken(tk) == mdtTypeRef) + { + TypeRefRec *pTypeRefRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tk), &pTypeRefRec)); + LPCSTR szTypeRefNamespace; + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szTypeRefNamespace)); + if (strcmp(szTypeRefNamespace, szNamespace) == 0) + { + mdToken tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); + if (TypeFromToken(tkResTmp) == mdtAssemblyRef) + { + AssemblyRefRec *pAsmRefRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResTmp), &pAsmRefRec)); + LPCSTR szAssemblyRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pAsmRefRec, &szAssemblyRefName)); + if(SString::_stricmp(szAssemblyRefName, szAssembly) == 0) + { + *ppTypeRefRec = pTypeRefRec; + return S_OK; + } + } + } + } + // Record not found + hr = S_FALSE; +ErrExit: + *ppTypeRefRec = NULL; + return hr; +} + +//***************************************************************************** +// constructor +//***************************************************************************** +NEWMERGER::NEWMERGER() + : m_pRegMetaEmit(0), + m_pImportDataList(NULL), + m_optimizeRefToDef(MDRefToDefDefault), + m_isscsSecurityCritical(ISSCS_Unknown), + m_isscsSecurityCriticalAllScopes(~ISSCS_Unknown) +{ + m_pImportDataTail = &(m_pImportDataList); +#if _DEBUG + m_iImport = 0; +#endif // _DEBUG +} // NEWMERGER::NEWMERGER() + +//***************************************************************************** +// initializer +//***************************************************************************** +HRESULT NEWMERGER::Init(RegMeta *pRegMeta) +{ + HRESULT hr = NOERROR; + MergeTypeData * pMTD; + + m_pRegMetaEmit = pRegMeta; + + // burn an entry so that the RID matches the array index + IfNullGo(pMTD = m_rMTDs.Append()); + + pMTD->m_bSuppressMergeCheck = false; + pMTD->m_cMethods = 0; + pMTD->m_cFields = 0; + pMTD->m_cEvents = 0; + pMTD->m_cProperties = 0; + +ErrExit: + return hr; +} // NEWMERGER::Init + +//***************************************************************************** +// destructor +//***************************************************************************** +NEWMERGER::~NEWMERGER() +{ + if (m_pImportDataList) + { + // delete this list and release all AddRef'ed interfaces! + MergeImportData *pNext; + for (pNext = m_pImportDataList; pNext != NULL; ) + { + pNext = m_pImportDataList->m_pNextImportData; + if (m_pImportDataList->m_pHandler) + m_pImportDataList->m_pHandler->Release(); + if (m_pImportDataList->m_pHostMapToken) + m_pImportDataList->m_pHostMapToken->Release(); + if (m_pImportDataList->m_pMDTokenMap) + delete m_pImportDataList->m_pMDTokenMap; + m_pImportDataList->m_pRegMetaImport->Release(); + delete m_pImportDataList; + m_pImportDataList = pNext; + } + } +} // NEWMERGER::~NEWMERGER + +//***************************************************************************** +CMiniMdRW *NEWMERGER::GetMiniMdEmit() +{ + return &(m_pRegMetaEmit->m_pStgdb->m_MiniMd); +} // CMiniMdRW *NEWMERGER::GetMiniMdEmit() + +//***************************************************************************** +// Adding a new import +//***************************************************************************** +HRESULT NEWMERGER::AddImport( + IMetaDataImport2 *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 error notification. +{ + HRESULT hr = NOERROR; + MergeImportData *pData; + + RegMeta *pRM = static_cast<RegMeta*>(pImport); + + // Add a MergeImportData to track the information for this import scope + pData = new (nothrow) MergeImportData; + IfNullGo( pData ); + pData->m_pRegMetaImport = pRM; + pData->m_pRegMetaImport->AddRef(); + pData->m_pHostMapToken = pHostMapToken; + if (pData->m_pHostMapToken) + pData->m_pHostMapToken->AddRef(); + if (pHandler) + { + pData->m_pHandler = pHandler; + pData->m_pHandler->AddRef(); + } + else + { + pData->m_pHandler = NULL; + } + + pData->m_pMDTokenMap = NULL; + pData->m_pNextImportData = NULL; +#if _DEBUG + pData->m_iImport = ++m_iImport; +#endif // _DEBUG + + pData->m_tkHandleProcessCorruptedStateCtor = mdTokenNil; + // add the newly create node to the tail of the list + *m_pImportDataTail = pData; + m_pImportDataTail = &(pData->m_pNextImportData); + +ErrExit: + + return hr; +} // HRESULT NEWMERGER::AddImport() + +HRESULT NEWMERGER::InitMergeTypeData() +{ + CMiniMdRW *pMiniMdEmit; + ULONG cTypeDefRecs; + ULONG i, j; + bool bSuppressMergeCheck; + + ULONG ridStart, ridEnd; + RID ridMap; + + mdToken tkSuppressMergeCheckCtor = mdTokenNil; + mdToken tkCA; + mdMethodDef mdEmit; + mdFieldDef fdEmit; + mdEvent evEmit; + mdProperty prEmit; + + TypeDefRec *pTypeDefRec; + EventMapRec *pEventMapRec; + PropertyMapRec *pPropertyMapRec; + + MergeTypeData *pMTD; + + HRESULT hr = NOERROR; + + pMiniMdEmit = GetMiniMdEmit(); + + // cache the SuppressMergeCheckAttribute.ctor token + ImportHelper::FindCustomAttributeCtorByName( + pMiniMdEmit, COR_MSCORLIB_NAME, + COR_COMPILERSERVICE_NAMESPACE, COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE, + &tkSuppressMergeCheckCtor); + + cTypeDefRecs = pMiniMdEmit->getCountTypeDefs(); + _ASSERTE(m_rMTDs.Count() > 0); + + for (i = m_rMTDs.Count(); i <= cTypeDefRecs; i++) + { + IfNullGo(pMTD = m_rMTDs.Append()); + + pMTD->m_cMethods = 0; + pMTD->m_cFields = 0; + pMTD->m_cEvents = 0; + pMTD->m_cProperties = 0; + pMTD->m_bSuppressMergeCheck = (tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit, + TokenFromRid(i, mdtTypeDef), tkSuppressMergeCheckCtor, + NULL, 0, &tkCA)); + + IfFailGo(pMiniMdEmit->GetTypeDefRecord(i, &pTypeDefRec)); + + // Count the number methods + ridStart = pMiniMdEmit->getMethodListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMdEmit->getEndMethodListOfTypeDef(i, &ridEnd)); + + for (j = ridStart; j < ridEnd; j++) + { + IfFailGo(pMiniMdEmit->GetMethodRid(j, (ULONG *)&mdEmit)); + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit, + mdEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (!bSuppressMergeCheck) + { + pMTD->m_cMethods++; + } + } + + // Count the number fields + ridStart = pMiniMdEmit->getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMdEmit->getEndFieldListOfTypeDef(i, &ridEnd)); + + for (j = ridStart; j < ridEnd; j++) + { + IfFailGo(pMiniMdEmit->GetFieldRid(j, (ULONG *)&fdEmit)); + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit, + fdEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (!bSuppressMergeCheck) + { + pMTD->m_cFields++; + } + } + + // Count the number of events + IfFailGo(pMiniMdEmit->FindEventMapFor(i, &ridMap)); + if (!InvalidRid(ridMap)) + { + IfFailGo(pMiniMdEmit->GetEventMapRecord(ridMap, &pEventMapRec)); + ridStart = pMiniMdEmit->getEventListOfEventMap(pEventMapRec); + IfFailGo(pMiniMdEmit->getEndEventListOfEventMap(ridMap, &ridEnd)); + + for (j = ridStart; j < ridEnd; j++) + { + IfFailGo(pMiniMdEmit->GetEventRid(j, (ULONG *)&evEmit)); + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit, + evEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (!bSuppressMergeCheck) + { + pMTD->m_cEvents++; + } + } + } + + // Count the number of properties + IfFailGo(pMiniMdEmit->FindPropertyMapFor(i, &ridMap)); + if (!InvalidRid(ridMap)) + { + IfFailGo(pMiniMdEmit->GetPropertyMapRecord(ridMap, &pPropertyMapRec)); + ridStart = pMiniMdEmit->getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(pMiniMdEmit->getEndPropertyListOfPropertyMap(ridMap, &ridEnd)); + + for (j = ridStart; j < ridEnd; j++) + { + IfFailGo(pMiniMdEmit->GetPropertyRid(j, (ULONG *)&prEmit)); + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdEmit, + prEmit, tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (!bSuppressMergeCheck) + { + pMTD->m_cProperties++; + } + } + } + } + +ErrExit: + return hr; +} + +//***************************************************************************** +// Merge now +//***************************************************************************** +HRESULT NEWMERGER::Merge(MergeFlags dwMergeFlags, CorRefToDefCheck optimizeRefToDef) +{ + MergeImportData *pImportData = m_pImportDataList; + MDTOKENMAP **pPrevMap = NULL; + MDTOKENMAP *pMDTokenMap; + HRESULT hr = NOERROR; + MDTOKENMAP *pCurTKMap; + int i; + +#if _DEBUG + { + LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n")); + LOG((LOGMD, "Merge scope list\n")); + i = 0; + for (MergeImportData *pID = m_pImportDataList; pID != NULL; pID = pID->m_pNextImportData) + { + WCHAR szScope[1024], szGuid[40]; + GUID mvid; + ULONG cchScope; + pID->m_pRegMetaImport->GetScopeProps(szScope, 1024, &cchScope, &mvid); + szScope[1023] = 0; + GuidToLPWSTR(mvid, szGuid, 40); + ++i; // Counter is 1-based. + LOG((LOGMD, "%3d: %ls : %ls\n", i, szGuid, szScope)); + } + LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n")); + } +#endif // _DEBUG + + m_dwMergeFlags = dwMergeFlags; + m_optimizeRefToDef = optimizeRefToDef; + + // check to see if we need to do dup check + m_fDupCheck = ((m_dwMergeFlags & NoDupCheck) != NoDupCheck); + + while (pImportData) + { + // Verify that we have a filter for each import scope. + IfNullGo( pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd.GetFilterTable() ); + + // cache the SuppressMergeCheckAttribute.ctor token for each import scope + ImportHelper::FindCustomAttributeCtorByName( + &pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd, COR_MSCORLIB_NAME, + COR_COMPILERSERVICE_NAMESPACE, COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE, + &pImportData->m_tkSuppressMergeCheckCtor); + + // cache the HandleProcessCorruptedStateExceptionsAttribute.ctor token for each import scope + ImportHelper::FindCustomAttributeCtorByName( + &pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd, COR_MSCORLIB_NAME, + COR_EXCEPTIONSERVICE_NAMESPACE, COR_HANDLE_PROCESS_CORRUPTED_STATE_EXCEPTION_ATTRIBUTE, + &pImportData->m_tkHandleProcessCorruptedStateCtor); + + // check for security critical attribute in the assembly (i.e. explicit annotations) + InputScopeSecurityCriticalStatus isscsTemp = CheckInputScopeIsCritical(pImportData, hr); + IfFailGo(hr); + // clear the unset flag bits (e.g. if critical, clear transparent bit) + // whatever bits remain are bits that have been set in all scopes + if (ISSCS_Unknown == (isscsTemp & ISSCS_SECURITYCRITICAL_FLAGS)) + m_isscsSecurityCriticalAllScopes &= ISSCS_SECURITYCRITICAL_LEGACY; + else + m_isscsSecurityCriticalAllScopes &= isscsTemp; + // set the flag bits (essentially, this allows us to see if _any_ scopes requested a bit) + m_isscsSecurityCritical |= isscsTemp; + + // create the tokenmap class to track metadata token remap for each import scope + pMDTokenMap = new (nothrow) MDTOKENMAP; + IfNullGo(pMDTokenMap); + IfFailGo(pMDTokenMap->Init((IMetaDataImport2*)pImportData->m_pRegMetaImport)); + pImportData->m_pMDTokenMap = pMDTokenMap; + pImportData->m_pMDTokenMap->m_pMap = pImportData->m_pHostMapToken; + if (pImportData->m_pHostMapToken) + pImportData->m_pHostMapToken->AddRef(); + pImportData->m_pMDTokenMap->m_pNextMap = NULL; + if (pPrevMap) + *pPrevMap = pImportData->m_pMDTokenMap; + pPrevMap = &(pImportData->m_pMDTokenMap->m_pNextMap); + pImportData = pImportData->m_pNextImportData; + } + + // Populate the m_rMTDs with the type info already defined in the emit scope + IfFailGo( InitMergeTypeData() ); + + // 1. Merge Module + IfFailGo( MergeModule( ) ); + + // 2. Merge TypeDef partially (i.e. only name) + IfFailGo( MergeTypeDefNamesOnly() ); + + // 3. Merge ModuleRef property and do ModuleRef to ModuleDef optimization + IfFailGo( MergeModuleRefs() ); + + // 4. Merge AssemblyRef. + IfFailGo( MergeAssemblyRefs() ); + + // 5. Merge TypeRef with TypeRef to TypeDef optimization + IfFailGo( MergeTypeRefs() ); + + // 6. Merge TypeSpec & MethodSpec + IfFailGo( MergeTypeSpecs() ); + + // 7. Now Merge the remaining of TypeDef records + IfFailGo( CompleteMergeTypeDefs() ); + + // 8. Merge Methods and Fields. Such that Signature translation is respecting the TypeRef to TypeDef optimization. + IfFailGo( MergeTypeDefChildren() ); + + // 9. Merge MemberRef with MemberRef to MethodDef/FieldDef optimization + IfFailGo( MergeMemberRefs( ) ); + + // 10. Merge InterfaceImpl + IfFailGo( MergeInterfaceImpls( ) ); + + // merge all of the remaining in metadata .... + + // 11. constant has dependency on property, field, param + IfFailGo( MergeConstants() ); + + // 12. field marshal has dependency on param and field + IfFailGo( MergeFieldMarshals() ); + + // 13. in ClassLayout, move over the FieldLayout and deal with FieldLayout as well + IfFailGo( MergeClassLayouts() ); + + // 14. FieldLayout has dependency on FieldDef. + IfFailGo( MergeFieldLayouts() ); + + // 15. FieldRVA has dependency on FieldDef. + IfFailGo( MergeFieldRVAs() ); + + // 16. MethodImpl has dependency on MemberRef, MethodDef, TypeRef and TypeDef. + IfFailGo( MergeMethodImpls() ); + + // 17. pinvoke depends on MethodDef and ModuleRef + IfFailGo( MergePinvoke() ); + + IfFailGo( MergeStandAloneSigs() ); + + IfFailGo( MergeMethodSpecs() ); + + IfFailGo( MergeStrings() ); + + if (m_dwMergeFlags & MergeManifest) + { + // keep the manifest!! + IfFailGo( MergeAssembly() ); + IfFailGo( MergeFiles() ); + IfFailGo( MergeExportedTypes() ); + IfFailGo( MergeManifestResources() ); + } + else if (m_dwMergeFlags & ::MergeExportedTypes) + { + IfFailGo( MergeFiles() ); + IfFailGo( MergeExportedTypes() ); + } + + IfFailGo( MergeCustomAttributes() ); + IfFailGo( MergeDeclSecuritys() ); + + + // Please don't add any MergeXxx() below here. CustomAttributess must be + // very late, because custom values are various other types. + + // Fixup list cannot be merged. Linker will need to re-emit them. + + // Now call back to host for the result of token remap + // + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // Send token remap information for each import scope + pCurTKMap = pImportData->m_pMDTokenMap; + TOKENREC *pRec; + if (pImportData->m_pHostMapToken) + { + for (i = 0; i < pCurTKMap->Count(); i++) + { + pRec = pCurTKMap->Get(i); + if (!pRec->IsEmpty()) + pImportData->m_pHostMapToken->Map(pRec->m_tkFrom, pRec->m_tkTo); + } + } + } + + // And last, but not least, let's do Security critical module-level attribute consolidation + // and metadata fixups. + IfFailGo( MergeSecurityCriticalAttributes() ); + + + + + +#if _DEBUG + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // dump the mapping + LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n")); + LOG((LOGMD, "Dumping token remap for one import scope!\n")); + LOG((LOGMD, "This is the %d import scope for merge!\n", pImportData->m_iImport)); + + pCurTKMap = pImportData->m_pMDTokenMap; + TOKENREC *pRec; + for (i = 0; i < pCurTKMap->Count(); i++) + { + pRec = pCurTKMap->Get(i); + if (!pRec->IsEmpty()) + { + LOG((LOGMD, " Token 0x%08x ====>>>> Token 0x%08x\n", pRec->m_tkFrom, pRec->m_tkTo)); + } + } + LOG((LOGMD, "End dumping token remap!\n")); + LOG((LOGMD, "++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n")); + } +#endif // _DEBUG + +ErrExit: + return hr; +} // HRESULT NEWMERGER::Merge() + + +//***************************************************************************** +// Merge ModuleDef +//***************************************************************************** +HRESULT NEWMERGER::MergeModule() +{ + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + HRESULT hr = NOERROR; + TOKENREC *pTokenRec; + + // we don't really merge Module information but we create a one to one mapping for each module token into the TokenMap + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + // set the current MDTokenMap + + pCurTkMap = pImportData->m_pMDTokenMap; + IfFailGo( pCurTkMap->InsertNotFound(MODULEDEFTOKEN, true, MODULEDEFTOKEN, &pTokenRec) ); + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeModule() + + +//***************************************************************************** +// Merge TypeDef but only Names. This is a partial merge to support TypeRef to TypeDef optimization +//***************************************************************************** +HRESULT NEWMERGER::MergeTypeDefNamesOnly() +{ + HRESULT hr = NOERROR; + TypeDefRec *pRecImport = NULL; + TypeDefRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdTypeDef tdEmit; + mdTypeDef tdImport; + bool bDuplicate; + DWORD dwFlags; + DWORD dwExportFlags; + NestedClassRec *pNestedRec; + RID iNestedRec; + mdTypeDef tdNester; + TOKENREC *pTokenRec; + + LPCUTF8 szNameImp; + LPCUTF8 szNamespaceImp; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + mdCustomAttribute tkCA; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + + iCount = pMiniMdImport->getCountTypeDefs(); + + // Merge the typedefs + for (i = 1; i <= iCount; i++) + { + // only merge those TypeDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(TokenFromRid(i, mdtTypeDef)) == false) + continue; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetTypeDefRecord(i, &pRecImport)); + IfFailGo(pMiniMdImport->getNameOfTypeDef(pRecImport, &szNameImp)); + IfFailGo(pMiniMdImport->getNamespaceOfTypeDef(pRecImport, &szNamespaceImp)); + + // If the class is a Nested class, get the parent token. + dwFlags = pMiniMdImport->getFlagsOfTypeDef(pRecImport); + if (IsTdNested(dwFlags)) + { + IfFailGo(pMiniMdImport->FindNestedClassHelper(TokenFromRid(i, mdtTypeDef), &iNestedRec)); + if (InvalidRid(iNestedRec)) + { + _ASSERTE(!"Bad state!"); + IfFailGo(META_E_BADMETADATA); + } + else + { + IfFailGo(pMiniMdImport->GetNestedClassRecord(iNestedRec, &pNestedRec)); + tdNester = pMiniMdImport->getEnclosingClassOfNestedClass(pNestedRec); + _ASSERTE(!IsNilToken(tdNester)); + IfFailGo(pCurTkMap->Remap(tdNester, &tdNester)); + } + } + else + tdNester = mdTokenNil; + + bSuppressMergeCheck = (pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + TokenFromRid(i, mdtTypeDef), pImportData->m_tkSuppressMergeCheckCtor, + NULL, 0, &tkCA)); + + // does this TypeDef already exist in the emit scope? + if ( ImportHelper::FindTypeDefByName( + pMiniMdEmit, + szNamespaceImp, + szNameImp, + tdNester, + &tdEmit) == S_OK ) + { + // Yes, it does + bDuplicate = true; + + // Let's look at their accessiblities. + IfFailGo(pMiniMdEmit->GetTypeDefRecord(RidFromToken(tdEmit), &pRecEmit)); + dwExportFlags = pMiniMdEmit->getFlagsOfTypeDef(pRecEmit); + + // Managed types need to have the same accessiblity + BOOL fManagedType = FALSE; + IfFailGo(IsManagedType(pMiniMdImport, TokenFromRid(i, mdtTypeDef), &fManagedType)); + if (fManagedType) + { + if ((dwFlags&tdVisibilityMask) != (dwExportFlags&tdVisibilityMask)) + { + CheckContinuableErrorEx(META_E_MISMATCHED_VISIBLITY, pImportData, TokenFromRid(i, mdtTypeDef)); + } + + } + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + if (pMTD->m_bSuppressMergeCheck != bSuppressMergeCheck) + { + CheckContinuableErrorEx(META_E_MD_INCONSISTENCY, pImportData, TokenFromRid(i, mdtTypeDef)); + } + } + else + { + // No, it doesn't. Copy it over. + bDuplicate = false; + IfFailGo(pMiniMdEmit->AddTypeDefRecord(&pRecEmit, (RID *)&tdEmit)); + + // make sure the index matches + _ASSERTE(((mdTypeDef)m_rMTDs.Count()) == tdEmit); + + IfNullGo(pMTD = m_rMTDs.Append()); + + pMTD->m_cMethods = 0; + pMTD->m_cFields = 0; + pMTD->m_cEvents = 0; + pMTD->m_cProperties = 0; + pMTD->m_bSuppressMergeCheck = bSuppressMergeCheck; + + tdEmit = TokenFromRid( tdEmit, mdtTypeDef ); + + // Set Full Qualified Name. + IfFailGo( CopyTypeDefPartially( pRecEmit, pMiniMdImport, pRecImport) ); + + // Create a NestedClass record if the class is a Nested class. + if (! IsNilToken(tdNester)) + { + IfFailGo(pMiniMdEmit->AddNestedClassRecord(&pNestedRec, &iNestedRec)); + + // copy over the information + IfFailGo( pMiniMdEmit->PutToken(TBL_NestedClass, NestedClassRec::COL_NestedClass, + pNestedRec, tdEmit)); + + // tdNester has already been remapped above to the Emit scope. + IfFailGo( pMiniMdEmit->PutToken(TBL_NestedClass, NestedClassRec::COL_EnclosingClass, + pNestedRec, tdNester)); + IfFailGo( pMiniMdEmit->AddNestedClassToHash(iNestedRec) ); + + } + } + + // record the token movement + tdImport = TokenFromRid(i, mdtTypeDef); + IfFailGo( pCurTkMap->InsertNotFound(tdImport, bDuplicate, tdEmit, &pTokenRec) ); + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeTypeDefNamesOnly() + + +//***************************************************************************** +// Merge EnclosingType tables +//***************************************************************************** +HRESULT NEWMERGER::CopyTypeDefPartially( + TypeDefRec *pRecEmit, // [IN] the emit record to fill + CMiniMdRW *pMiniMdImport, // [IN] the importing scope + TypeDefRec *pRecImp) // [IN] the record to import + +{ + HRESULT hr; + LPCUTF8 szNameImp; + LPCUTF8 szNamespaceImp; + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + + IfFailGo(pMiniMdImport->getNameOfTypeDef(pRecImp, &szNameImp)); + IfFailGo(pMiniMdImport->getNamespaceOfTypeDef(pRecImp, &szNamespaceImp)); + + IfFailGo( pMiniMdEmit->PutString( TBL_TypeDef, TypeDefRec::COL_Name, pRecEmit, szNameImp) ); + IfFailGo( pMiniMdEmit->PutString( TBL_TypeDef, TypeDefRec::COL_Namespace, pRecEmit, szNamespaceImp) ); + + pRecEmit->SetFlags(pRecImp->GetFlags()); + + // Don't copy over the extends until TypeRef's remap is calculated + +ErrExit: + return hr; + +} // HRESULT NEWMERGER::CopyTypeDefPartially() + + +//***************************************************************************** +// Merge ModuleRef tables including ModuleRef to ModuleDef optimization +//***************************************************************************** +HRESULT NEWMERGER::MergeModuleRefs() +{ + HRESULT hr = NOERROR; + ModuleRefRec *pRecImport = NULL; + ModuleRefRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdModuleRef mrEmit; + bool bDuplicate = false; + TOKENREC *pTokenRec; + LPCUTF8 szNameImp; + bool isModuleDef; + + MergeImportData *pImportData; + MergeImportData *pData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountModuleRefs(); + + // loop through all ModuleRef + for (i = 1; i <= iCount; i++) + { + // only merge those ModuleRefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsModuleRefMarked(TokenFromRid(i, mdtModuleRef)) == false) + continue; + + isModuleDef = false; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetModuleRefRecord(i, &pRecImport)); + IfFailGo(pMiniMdImport->getNameOfModuleRef(pRecImport, &szNameImp)); + + // Only do the ModuleRef to ModuleDef optimization if ModuleRef's name is meaningful! + if ( szNameImp && szNameImp[0] != '\0') + { + + // Check to see if this ModuleRef has become the ModuleDef token + for (pData = m_pImportDataList; pData != NULL; pData = pData->m_pNextImportData) + { + CMiniMdRW *pMiniMd = &(pData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + ModuleRec *pRec; + LPCUTF8 szName; + + IfFailGo(pMiniMd->GetModuleRecord(MODULEDEFTOKEN, &pRec)); + IfFailGo(pMiniMd->getNameOfModule(pRec, &szName)); + if (szName && szName[0] != '\0' && strcmp(szNameImp, szName) == 0) + { + // We found an import Module for merging that has the same name as the ModuleRef + isModuleDef = true; + bDuplicate = true; + mrEmit = MODULEDEFTOKEN; // set the resulting token to ModuleDef Token + break; + } + } + } + + if (isModuleDef == false) + { + // does this ModuleRef already exist in the emit scope? + hr = ImportHelper::FindModuleRef(pMiniMdEmit, + szNameImp, + &mrEmit); + if (hr == S_OK) + { + // Yes, it does + bDuplicate = true; + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + { + // No, it doesn't. Copy it over. + bDuplicate = false; + IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecEmit, (RID*)&mrEmit)); + mrEmit = TokenFromRid(mrEmit, mdtModuleRef); + + // Set ModuleRef Name. + IfFailGo( pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, pRecEmit, szNameImp) ); + } + else + IfFailGo(hr); + } + + // record the token movement + IfFailGo( pCurTkMap->InsertNotFound( + TokenFromRid(i, mdtModuleRef), + bDuplicate, + mrEmit, + &pTokenRec) ); + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeModuleRefs() + + +//***************************************************************************** +// Merge AssemblyRef tables +//***************************************************************************** +HRESULT NEWMERGER::MergeAssemblyRefs() +{ + HRESULT hr = NOERROR; + AssemblyRefRec *pRecImport = NULL; + AssemblyRefRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + mdAssemblyRef arEmit; + bool bDuplicate = false; + LPCUTF8 szTmp; + const void *pbTmp; + ULONG cbTmp; + ULONG iCount; + ULONG i; + ULONG iRecord; + TOKENREC *pTokenRec; + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountAssemblyRefs(); + + // loope through all the AssemblyRefs. + for (i = 1; i <= iCount; i++) + { + // Compare with the emit scope. + IfFailGo(pMiniMdImport->GetAssemblyRefRecord(i, &pRecImport)); + IfFailGo(pMiniMdImport->getPublicKeyOrTokenOfAssemblyRef(pRecImport, (const BYTE **)&pbTmp, &cbTmp)); + hr = CLDB_E_RECORD_NOTFOUND; + if (m_fDupCheck) + { + LPCSTR szAssemblyRefName; + LPCSTR szAssemblyRefLocale; + IfFailGo(pMiniMdImport->getNameOfAssemblyRef(pRecImport, &szAssemblyRefName)); + IfFailGo(pMiniMdImport->getLocaleOfAssemblyRef(pRecImport, &szAssemblyRefLocale)); + hr = ImportHelper::FindAssemblyRef( + pMiniMdEmit, + szAssemblyRefName, + szAssemblyRefLocale, + pbTmp, + cbTmp, + pRecImport->GetMajorVersion(), + pRecImport->GetMinorVersion(), + pRecImport->GetBuildNumber(), + pRecImport->GetRevisionNumber(), + pRecImport->GetFlags(), + &arEmit); + } + if (hr == S_OK) + { + // Yes, it does + bDuplicate = true; + + // <TODO>@FUTURE: more verification?</TODO> + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + { + // No, it doesn't. Copy it over. + bDuplicate = false; + IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecEmit, &iRecord)); + arEmit = TokenFromRid(iRecord, mdtAssemblyRef); + + pRecEmit->Copy(pRecImport); + + IfFailGo(pMiniMdImport->getPublicKeyOrTokenOfAssemblyRef(pRecImport, (const BYTE **)&pbTmp, &cbTmp)); + IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken, + pRecEmit, pbTmp, cbTmp)); + + IfFailGo(pMiniMdImport->getNameOfAssemblyRef(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name, + pRecEmit, szTmp)); + + IfFailGo(pMiniMdImport->getLocaleOfAssemblyRef(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale, + pRecEmit, szTmp)); + + IfFailGo(pMiniMdImport->getHashValueOfAssemblyRef(pRecImport, (const BYTE **)&pbTmp, &cbTmp)); + IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue, + pRecEmit, pbTmp, cbTmp)); + + } + else + IfFailGo(hr); + + // record the token movement. + IfFailGo(pCurTkMap->InsertNotFound( + TokenFromRid(i, mdtAssemblyRef), + bDuplicate, + arEmit, + &pTokenRec)); + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeAssemblyRefs() + + +//***************************************************************************** +// Merge TypeRef tables also performing TypeRef to TypeDef opitimization. ie. +// we will not introduce a TypeRef record if we can optimize it to a TypeDef. +//***************************************************************************** +HRESULT NEWMERGER::MergeTypeRefs() +{ + HRESULT hr = NOERROR; + TypeRefRec *pRecImport = NULL; + TypeRefRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdTypeRef trEmit; + bool bDuplicate = false; + TOKENREC *pTokenRec; + bool isTypeDef; + + mdToken tkResImp; + mdToken tkResEmit; + LPCUTF8 szNameImp; + LPCUTF8 szNamespaceImp; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountTypeRefs(); + + // loop through all TypeRef + for (i = 1; i <= iCount; i++) + { + // only merge those TypeRefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsTypeRefMarked(TokenFromRid(i, mdtTypeRef)) == false) + continue; + + isTypeDef = false; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetTypeRefRecord(i, &pRecImport)); + tkResImp = pMiniMdImport->getResolutionScopeOfTypeRef(pRecImport); + IfFailGo(pMiniMdImport->getNamespaceOfTypeRef(pRecImport, &szNamespaceImp)); + IfFailGo(pMiniMdImport->getNameOfTypeRef(pRecImport, &szNameImp)); + if (!IsNilToken(tkResImp)) + { + IfFailGo(pCurTkMap->Remap(tkResImp, &tkResEmit)); + } + else + { + tkResEmit = tkResImp; + } + + // There are some interesting cases to consider here. + // 1) If the TypeRef's ResolutionScope is a nil token, or is the MODULEDEFTOKEN (current module), + // then the TypeRef refers to a type in the current scope, so we should find a corresponding + // TypeDef in the output scope. If we find the TypeDef, we'll remap this TypeRef token + // to that TypeDef token. + // If we don't find that TypeDef, or if "TypeRef to TypeDef" optimization is turned off, we'll + // create the TypeRef in the output scope. + // 2) If the TypeRef's ResolutionScope has been resolved to a TypeDef, then this TypeRef was part + // of a nested type definition. In that case, we'd better find a corresponding TypeDef + // or we have an error. + if (IsNilToken(tkResEmit) || tkResEmit == MODULEDEFTOKEN || TypeFromToken(tkResEmit) == mdtTypeDef) + { + hr = ImportHelper::FindTypeDefByName( + pMiniMdEmit, + szNamespaceImp, + szNameImp, + (TypeFromToken(tkResEmit) == mdtTypeDef) ? tkResEmit : mdTokenNil, + &trEmit); + if (hr == S_OK) + { + isTypeDef = true; + + // it really does not matter if we set the duplicate to true or false. + bDuplicate = true; + } + } + + // If the ResolutionScope was merged as a TypeDef, and this token wasn't found as TypeDef, send the error. + if (TypeFromToken(tkResEmit) == mdtTypeDef && !isTypeDef) + { + // Send the error notification. Use the "continuable error" callback, but even if linker says it is + // ok, don't continue. + CheckContinuableErrorEx(META_E_TYPEDEF_MISSING, pImportData, TokenFromRid(i, mdtTypeRef)); + IfFailGo(META_E_TYPEDEF_MISSING); + } + + // If this TypeRef cannot be optmized to a TypeDef or the Ref to Def optimization is turned off, do the following. + if (!isTypeDef || !((m_optimizeRefToDef & MDTypeRefToDef) == MDTypeRefToDef)) + { + // does this TypeRef already exist in the emit scope? + if ( m_fDupCheck && ImportHelper::FindTypeRefByName( + pMiniMdEmit, + tkResEmit, + szNamespaceImp, + szNameImp, + &trEmit) == S_OK ) + { + // Yes, it does + bDuplicate = true; + } + else + { + // No, it doesn't. Copy it over. + bDuplicate = false; + IfFailGo(pMiniMdEmit->AddTypeRefRecord(&pRecEmit, (RID*)&trEmit)); + trEmit = TokenFromRid(trEmit, mdtTypeRef); + + // Set ResolutionScope. tkResEmit has already been re-mapped. + IfFailGo(pMiniMdEmit->PutToken(TBL_TypeRef, TypeRefRec::COL_ResolutionScope, + pRecEmit, tkResEmit)); + + // Set Name. + IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Name, + pRecEmit, szNameImp)); + IfFailGo(pMiniMdEmit->AddNamedItemToHash(TBL_TypeRef, trEmit, szNameImp, 0)); + + // Set Namespace. + IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Namespace, + pRecEmit, szNamespaceImp)); + } + } + + // record the token movement + IfFailGo( pCurTkMap->InsertNotFound( + TokenFromRid(i, mdtTypeRef), + bDuplicate, + trEmit, + &pTokenRec) ); + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeTypeRefs() + + +//***************************************************************************** +// copy over the remaining information of partially merged TypeDef records. Right now only +// extends field is delayed to here. The reason that we delay extends field is because we want +// to optimize TypeRef to TypeDef if possible. +//***************************************************************************** +HRESULT NEWMERGER::CompleteMergeTypeDefs() +{ + HRESULT hr = NOERROR; + TypeDefRec *pRecImport = NULL; + TypeDefRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + TOKENREC *pTokenRec; + mdToken tkExtendsImp; + mdToken tkExtendsEmit; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + + iCount = pMiniMdImport->getCountTypeDefs(); + + // Merge the typedefs + for (i = 1; i <= iCount; i++) + { + // only merge those TypeDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(TokenFromRid(i, mdtTypeDef)) == false) + continue; + + if ( !pCurTkMap->Find(TokenFromRid(i, mdtTypeDef), &pTokenRec) ) + { + _ASSERTE( !"bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + + if (pTokenRec->m_isDuplicate == false) + { + // get the extends token from the import + IfFailGo(pMiniMdImport->GetTypeDefRecord(i, &pRecImport)); + tkExtendsImp = pMiniMdImport->getExtendsOfTypeDef(pRecImport); + + // map the extends token to an merged token + IfFailGo( pCurTkMap->Remap(tkExtendsImp, &tkExtendsEmit) ); + + // set the extends to the merged TypeDef records. + IfFailGo(pMiniMdEmit->GetTypeDefRecord(RidFromToken(pTokenRec->m_tkTo), &pRecEmit)); + IfFailGo(pMiniMdEmit->PutToken(TBL_TypeDef, TypeDefRec::COL_Extends, pRecEmit, tkExtendsEmit)); + } + else + { + // <TODO>@FUTURE: we can check to make sure the import extends maps to the one that is set to the emit scope. + // Otherwise, it is a error to report to linker.</TODO> + } + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::CompleteMergeTypeDefs() + + +//***************************************************************************** +// merging TypeSpecs +//***************************************************************************** +HRESULT NEWMERGER::MergeTypeSpecs() +{ + HRESULT hr = NOERROR; + TypeSpecRec *pRecImport = NULL; + TypeSpecRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + TOKENREC *pTokenRec; + mdTypeSpec tsImp; + mdTypeSpec tsEmit; + bool fDuplicate; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + + iCount = pMiniMdImport->getCountTypeSpecs(); + + // loop through all TypeSpec + for (i = 1; i <= iCount; i++) + { + // only merge those TypeSpecs that are marked + if ( pMiniMdImport->GetFilterTable()->IsTypeSpecMarked(TokenFromRid(i, mdtTypeSpec)) == false) + continue; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetTypeSpecRecord(i, &pRecImport)); + IfFailGo(pMiniMdImport->getSignatureOfTypeSpec(pRecImport, &pbSig, &cbSig)); + + // convert tokens contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInFieldSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly information. + pMiniMdImport, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + + hr = CLDB_E_RECORD_NOTFOUND; + if (m_fDupCheck) + hr = ImportHelper::FindTypeSpec( + pMiniMdEmit, + (PCOR_SIGNATURE) qbSig.Ptr(), + cbEmit, + &tsEmit ); + + if ( hr == S_OK ) + { + // find a duplicate + fDuplicate = true; + } + else + { + // copy over + fDuplicate = false; + IfFailGo(pMiniMdEmit->AddTypeSpecRecord(&pRecEmit, (ULONG *)&tsEmit)); + tsEmit = TokenFromRid(tsEmit, mdtTypeSpec); + IfFailGo( pMiniMdEmit->PutBlob( + TBL_TypeSpec, + TypeSpecRec::COL_Signature, + pRecEmit, + (PCOR_SIGNATURE)qbSig.Ptr(), + cbEmit)); + } + tsImp = TokenFromRid(i, mdtTypeSpec); + + // Record the token movement + IfFailGo( pCurTkMap->InsertNotFound(tsImp, fDuplicate, tsEmit, &pTokenRec) ); + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeTypeSpecs() + + +//***************************************************************************** +// merging Children of TypeDefs. This includes field, method, parameter, property, event +//***************************************************************************** +HRESULT NEWMERGER::MergeTypeDefChildren() +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdTypeDef tdEmit; + mdTypeDef tdImport; + TOKENREC *pTokenRec; + +#if _DEBUG + TypeDefRec *pRecImport = NULL; + LPCUTF8 szNameImp; + LPCUTF8 szNamespaceImp; +#endif // _DEBUG + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountTypeDefs(); + + // loop through all TypeDef again to merge/copy Methods, fields, events, and properties + // + for (i = 1; i <= iCount; i++) + { + // only merge those TypeDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(TokenFromRid(i, mdtTypeDef)) == false) + continue; + +#if _DEBUG + IfFailGo(pMiniMdImport->GetTypeDefRecord(i, &pRecImport)); + IfFailGo(pMiniMdImport->getNameOfTypeDef(pRecImport, &szNameImp)); + IfFailGo(pMiniMdImport->getNamespaceOfTypeDef(pRecImport, &szNamespaceImp)); +#endif // _DEBUG + + // check to see if the typedef is duplicate or not + tdImport = TokenFromRid(i, mdtTypeDef); + if ( pCurTkMap->Find( tdImport, &pTokenRec) == false) + { + _ASSERTE( !"bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + tdEmit = pTokenRec->m_tkTo; + if (pTokenRec->m_isDuplicate == false) + { + // now move all of the children records over + IfFailGo( CopyMethods(pImportData, tdImport, tdEmit) ); + IfFailGo( CopyFields(pImportData, tdImport, tdEmit) ); + + IfFailGo( CopyEvents(pImportData, tdImport, tdEmit) ); + + // Property has dependency on events + IfFailGo( CopyProperties(pImportData, tdImport, tdEmit) ); + + // Generic Params. + IfFailGo( CopyGenericParams(pImportData, tdImport, tdEmit) ); + } + else + { + // verify the children records + IfFailGo( VerifyMethods(pImportData, tdImport, tdEmit) ); + IfFailGo( VerifyFields(pImportData, tdImport, tdEmit) ); + IfFailGo( VerifyEvents(pImportData, tdImport, tdEmit) ); + + // property has dependency on events + IfFailGo( VerifyProperties(pImportData, tdImport, tdEmit) ); + + IfFailGo( VerifyGenericParams(pImportData, tdImport, tdEmit) ); + } + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeTypeDefChildren() + + +//******************************************************************************* +// Helper to copy an Method record +//******************************************************************************* +HRESULT NEWMERGER::CopyMethod( + MergeImportData *pImportData, // [IN] import scope + MethodRec *pRecImp, // [IN] the record to import + MethodRec *pRecEmit) // [IN] the emit record to fill +{ + HRESULT hr; + CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + LPCUTF8 szName; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + MDTOKENMAP *pCurTkMap; + + pCurTkMap = pImportData->m_pMDTokenMap; + + // copy over the fix part of the record + pRecEmit->Copy(pRecImp); + + // copy over the name + IfFailGo(pMiniMdImp->getNameOfMethod(pRecImp, &szName)); + IfFailGo(pMiniMdEmit->PutString(TBL_Method, MethodRec::COL_Name, pRecEmit, szName)); + + // copy over the signature + IfFailGo(pMiniMdImp->getSignatureOfMethod(pRecImp, &pbSig, &cbSig)); + + // convert rid contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly scope information. + pMiniMdImp, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + + IfFailGo(pMiniMdEmit->PutBlob(TBL_Method, MethodRec::COL_Signature, pRecEmit, qbSig.Ptr(), cbEmit)); + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyMethod() + + +//******************************************************************************* +// Helper to copy an field record +//******************************************************************************* +HRESULT NEWMERGER::CopyField( + MergeImportData *pImportData, // [IN] import scope + FieldRec *pRecImp, // [IN] the record to import + FieldRec *pRecEmit) // [IN] the emit record to fill +{ + HRESULT hr; + CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + LPCUTF8 szName; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + MDTOKENMAP *pCurTkMap; + + pCurTkMap = pImportData->m_pMDTokenMap; + + // copy over the fix part of the record + pRecEmit->SetFlags(pRecImp->GetFlags()); + + // copy over the name + IfFailGo(pMiniMdImp->getNameOfField(pRecImp, &szName)); + IfFailGo(pMiniMdEmit->PutString(TBL_Field, FieldRec::COL_Name, pRecEmit, szName)); + + // copy over the signature + IfFailGo(pMiniMdImp->getSignatureOfField(pRecImp, &pbSig, &cbSig)); + + // convert rid contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInSig( + NULL, // Emit assembly scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly scope information. + pMiniMdImp, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + + IfFailGo(pMiniMdEmit->PutBlob(TBL_Field, FieldRec::COL_Signature, pRecEmit, qbSig.Ptr(), cbEmit)); + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyField() + +//******************************************************************************* +// Helper to copy an field record +//******************************************************************************* +HRESULT NEWMERGER::CopyParam( + MergeImportData *pImportData, // [IN] import scope + ParamRec *pRecImp, // [IN] the record to import + ParamRec *pRecEmit) // [IN] the emit record to fill +{ + HRESULT hr; + CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + LPCUTF8 szName; + MDTOKENMAP *pCurTkMap; + + pCurTkMap = pImportData->m_pMDTokenMap; + + // copy over the fix part of the record + pRecEmit->Copy(pRecImp); + + // copy over the name + IfFailGo(pMiniMdImp->getNameOfParam(pRecImp, &szName)); + IfFailGo(pMiniMdEmit->PutString(TBL_Param, ParamRec::COL_Name, pRecEmit, szName)); + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyParam() + +//******************************************************************************* +// Helper to copy an Event record +//******************************************************************************* +HRESULT NEWMERGER::CopyEvent( + MergeImportData *pImportData, // [IN] import scope + EventRec *pRecImp, // [IN] the record to import + EventRec *pRecEmit) // [IN] the emit record to fill +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + mdToken tkEventTypeImp; + mdToken tkEventTypeEmit; // could be TypeDef or TypeRef + LPCUTF8 szName; + MDTOKENMAP *pCurTkMap; + + pCurTkMap = pImportData->m_pMDTokenMap; + + pRecEmit->SetEventFlags(pRecImp->GetEventFlags()); + + //move over the event name + IfFailGo(pMiniMdImp->getNameOfEvent(pRecImp, &szName)); + IfFailGo( pMiniMdEmit->PutString(TBL_Event, EventRec::COL_Name, pRecEmit, szName) ); + + // move over the EventType + tkEventTypeImp = pMiniMdImp->getEventTypeOfEvent(pRecImp); + if ( !IsNilToken(tkEventTypeImp) ) + { + IfFailGo( pCurTkMap->Remap(tkEventTypeImp, &tkEventTypeEmit) ); + IfFailGo(pMiniMdEmit->PutToken(TBL_Event, EventRec::COL_EventType, pRecEmit, tkEventTypeEmit)); + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyEvent() + + +//******************************************************************************* +// Helper to copy a property record +//******************************************************************************* +HRESULT NEWMERGER::CopyProperty( + MergeImportData *pImportData, // [IN] import scope + PropertyRec *pRecImp, // [IN] the record to import + PropertyRec *pRecEmit) // [IN] the emit record to fill +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + LPCUTF8 szName; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + MDTOKENMAP *pCurTkMap; + + pCurTkMap = pImportData->m_pMDTokenMap; + + // move over the flag value + pRecEmit->SetPropFlags(pRecImp->GetPropFlags()); + + //move over the property name + IfFailGo(pMiniMdImp->getNameOfProperty(pRecImp, &szName)); + IfFailGo( pMiniMdEmit->PutString(TBL_Property, PropertyRec::COL_Name, pRecEmit, szName) ); + + // move over the type of the property + IfFailGo(pMiniMdImp->getTypeOfProperty(pRecImp, &pbSig, &cbSig)); + + // convert rid contained in signature to new scope + IfFailGo( ImportHelper::MergeUpdateTokenInSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly scope information. + pMiniMdImp, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit) ); // number of bytes write to cbEmit + + IfFailGo(pMiniMdEmit->PutBlob(TBL_Property, PropertyRec::COL_Type, pRecEmit, qbSig.Ptr(), cbEmit)); + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyProperty() + + +//***************************************************************************** +// Copy MethodSemantics for an event or a property +//***************************************************************************** +HRESULT NEWMERGER::CopyMethodSemantics( + MergeImportData *pImportData, + mdToken tkImport, // Event or property in the import scope + mdToken tkEmit) // corresponding event or property in the emitting scope +{ + HRESULT hr = NOERROR; + MethodSemanticsRec *pRecImport = NULL; + MethodSemanticsRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + ULONG i; + ULONG msEmit; // MethodSemantics are just index not tokens + mdToken tkMethodImp; + mdToken tkMethodEmit; + MDTOKENMAP *pCurTkMap; + HENUMInternal hEnum; + + pCurTkMap = pImportData->m_pMDTokenMap; + + // copy over the associates + IfFailGo( pMiniMdImport->FindMethodSemanticsHelper(tkImport, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &i)) + { + IfFailGo(pMiniMdImport->GetMethodSemanticsRecord(i, &pRecImport)); + IfFailGo(pMiniMdEmit->AddMethodSemanticsRecord(&pRecEmit, &msEmit)); + pRecEmit->SetSemantic(pRecImport->GetSemantic()); + + // set the MethodSemantics + tkMethodImp = pMiniMdImport->getMethodOfMethodSemantics(pRecImport); + IfFailGo( pCurTkMap->Remap(tkMethodImp, &tkMethodEmit) ); + IfFailGo( pMiniMdEmit->PutToken(TBL_MethodSemantics, MethodSemanticsRec::COL_Method, pRecEmit, tkMethodEmit)); + + // set the associate + _ASSERTE( pMiniMdImport->getAssociationOfMethodSemantics(pRecImport) == tkImport ); + IfFailGo( pMiniMdEmit->PutToken(TBL_MethodSemantics, MethodSemanticsRec::COL_Association, pRecEmit, tkEmit)); + + // no need to record the movement since it is not a token + IfFailGo( pMiniMdEmit->AddMethodSemanticsToHash(msEmit) ); + } +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + return hr; +} // HRESULT NEWMERGER::CopyMethodSemantics() + + +//***************************************************************************** +// Copy Methods given a TypeDef +//***************************************************************************** +HRESULT NEWMERGER::CopyMethods( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + MethodRec *pRecImport = NULL; + MethodRec *pRecEmit = NULL; + TypeDefRec *pTypeDefRec; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG ridStart, ridEnd; + ULONG i; + mdMethodDef mdEmit; + mdMethodDef mdImp; + TOKENREC *pTokenRec; + MDTOKENMAP *pCurTkMap; + + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + mdCustomAttribute tkCA; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec)); + ridStart = pMiniMdImport->getMethodListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMdImport->getEndMethodListOfTypeDef(RidFromToken(tdImport), &ridEnd)); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + // make sure we didn't count the methods yet + _ASSERTE(pMTD->m_cMethods == 0); + + // loop through all Methods + for (i = ridStart; i < ridEnd; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetMethodRid(i, (ULONG *)&mdImp)); + + // only merge those MethodDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsMethodMarked(TokenFromRid(mdImp, mdtMethodDef)) == false) + continue; + + IfFailGo(pMiniMdImport->GetMethodRecord(mdImp, &pRecImport)); + IfFailGo(pMiniMdEmit->AddMethodRecord(&pRecEmit, (RID *)&mdEmit)); + + // copy the method content over + IfFailGo( CopyMethod(pImportData, pRecImport, pRecEmit) ); + + IfFailGo( pMiniMdEmit->AddMethodToTypeDef(RidFromToken(tdEmit), mdEmit)); + + // record the token movement + mdImp = TokenFromRid(mdImp, mdtMethodDef); + mdEmit = TokenFromRid(mdEmit, mdtMethodDef); + IfFailGo( pMiniMdEmit->AddMemberDefToHash( + mdEmit, + tdEmit) ); + + IfFailGo( pCurTkMap->InsertNotFound(mdImp, false, mdEmit, &pTokenRec) ); + + // copy over the children + IfFailGo( CopyParams(pImportData, mdImp, mdEmit) ); + IfFailGo( CopyGenericParams(pImportData, mdImp, mdEmit) ); + + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + mdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + + if (!bSuppressMergeCheck) { + pMTD->m_cMethods++; + } + } + + // make sure we don't count any methods if merge check is suppressed on the type + _ASSERTE(pMTD->m_cMethods == 0 || !pMTD->m_bSuppressMergeCheck); +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyMethods() + + +//***************************************************************************** +// Copy Fields given a TypeDef +//***************************************************************************** +HRESULT NEWMERGER::CopyFields( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + FieldRec *pRecImport = NULL; + FieldRec *pRecEmit = NULL; + TypeDefRec *pTypeDefRec; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG ridStart, ridEnd; + ULONG i; + mdFieldDef fdEmit; + mdFieldDef fdImp; + bool bDuplicate; + TOKENREC *pTokenRec; + PCCOR_SIGNATURE pvSigBlob; + ULONG cbSigBlob; + MDTOKENMAP *pCurTkMap; + + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + mdCustomAttribute tkCA; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec)); + ridStart = pMiniMdImport->getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMdImport->getEndFieldListOfTypeDef(RidFromToken(tdImport), &ridEnd)); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + // make sure we didn't count the methods yet + _ASSERTE(pMTD->m_cFields == 0); + + // loop through all FieldDef of a TypeDef + for (i = ridStart; i < ridEnd; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetFieldRid(i, (ULONG *)&fdImp)); + + // only merge those FieldDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(TokenFromRid(fdImp, mdtFieldDef)) == false) + continue; + + + IfFailGo(pMiniMdImport->GetFieldRecord(fdImp, &pRecImport)); + bDuplicate = false; + IfFailGo(pMiniMdEmit->AddFieldRecord(&pRecEmit, (RID *)&fdEmit)); + + // copy the field content over + IfFailGo( CopyField(pImportData, pRecImport, pRecEmit) ); + + IfFailGo( pMiniMdEmit->AddFieldToTypeDef(RidFromToken(tdEmit), fdEmit)); + + // record the token movement + fdImp = TokenFromRid(fdImp, mdtFieldDef); + fdEmit = TokenFromRid(fdEmit, mdtFieldDef); + IfFailGo(pMiniMdEmit->getSignatureOfField(pRecEmit, &pvSigBlob, &cbSigBlob)); + IfFailGo( pMiniMdEmit->AddMemberDefToHash( + fdEmit, + tdEmit) ); + + IfFailGo( pCurTkMap->InsertNotFound(fdImp, false, fdEmit, &pTokenRec) ); + + // count the number of fields that didn't suppress merge check + // non-static fields doesn't inherite the suppress merge check attribute from the type + bSuppressMergeCheck = + (IsFdStatic(pRecEmit->GetFlags()) && pMTD->m_bSuppressMergeCheck) || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + fdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (!bSuppressMergeCheck) { + pMTD->m_cFields++; + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyFields() + + +//***************************************************************************** +// Copy Events given a TypeDef +//***************************************************************************** +HRESULT NEWMERGER::CopyEvents( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + RID ridEventMap; + EventMapRec *pEventMapRec; + EventRec *pRecImport; + EventRec *pRecEmit; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + mdEvent evImp; + mdEvent evEmit; + TOKENREC *pTokenRec; + ULONG iEventMap; + EventMapRec *pEventMap; + MDTOKENMAP *pCurTkMap; + + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + mdCustomAttribute tkCA; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + pCurTkMap = pImportData->m_pMDTokenMap; + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + // make sure we didn't count the events yet + _ASSERTE(pMTD->m_cEvents == 0); + + IfFailGo(pMiniMdImport->FindEventMapFor(RidFromToken(tdImport), &ridEventMap)); + if (!InvalidRid(ridEventMap)) + { + IfFailGo(pMiniMdImport->GetEventMapRecord(ridEventMap, &pEventMapRec)); + ridStart = pMiniMdImport->getEventListOfEventMap(pEventMapRec); + IfFailGo(pMiniMdImport->getEndEventListOfEventMap(ridEventMap, &ridEnd)); + + if (ridEnd > ridStart) + { + // If there is any event, create the eventmap record in the emit scope + // Create new record. + IfFailGo(pMiniMdEmit->AddEventMapRecord(&pEventMap, &iEventMap)); + + // Set parent. + IfFailGo(pMiniMdEmit->PutToken(TBL_EventMap, EventMapRec::COL_Parent, pEventMap, tdEmit)); + } + + for (i = ridStart; i < ridEnd; i++) + { + // get the real event rid + IfFailGo(pMiniMdImport->GetEventRid(i, (ULONG *)&evImp)); + + // only merge those Events that are marked + if ( pMiniMdImport->GetFilterTable()->IsEventMarked(TokenFromRid(evImp, mdtEvent)) == false) + continue; + + IfFailGo(pMiniMdImport->GetEventRecord(evImp, &pRecImport)); + IfFailGo(pMiniMdEmit->AddEventRecord(&pRecEmit, (RID *)&evEmit)); + + // copy the event record over + IfFailGo( CopyEvent(pImportData, pRecImport, pRecEmit) ); + + // Add Event to the EventMap. + IfFailGo( pMiniMdEmit->AddEventToEventMap(iEventMap, evEmit) ); + + // record the token movement + evImp = TokenFromRid(evImp, mdtEvent); + evEmit = TokenFromRid(evEmit, mdtEvent); + + IfFailGo( pCurTkMap->InsertNotFound(evImp, false, evEmit, &pTokenRec) ); + + // copy over the method semantics + IfFailGo( CopyMethodSemantics(pImportData, evImp, evEmit) ); + + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + evImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (!bSuppressMergeCheck) { + pMTD->m_cEvents++; + } + } + } + + // make sure we don't count any events if merge check is suppressed on the type + _ASSERTE(pMTD->m_cEvents == 0 || !pMTD->m_bSuppressMergeCheck); +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyEvents() + + +//***************************************************************************** +// Copy Properties given a TypeDef +//***************************************************************************** +HRESULT NEWMERGER::CopyProperties( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + RID ridPropertyMap; + PropertyMapRec *pPropertyMapRec; + PropertyRec *pRecImport; + PropertyRec *pRecEmit; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + mdProperty prImp; + mdProperty prEmit; + TOKENREC *pTokenRec; + ULONG iPropertyMap; + PropertyMapRec *pPropertyMap; + MDTOKENMAP *pCurTkMap; + + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + mdCustomAttribute tkCA; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + pCurTkMap = pImportData->m_pMDTokenMap; + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + // make sure we didn't count the properties yet + _ASSERTE(pMTD->m_cProperties == 0); + + IfFailGo(pMiniMdImport->FindPropertyMapFor(RidFromToken(tdImport), &ridPropertyMap)); + if (!InvalidRid(ridPropertyMap)) + { + IfFailGo(pMiniMdImport->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ridStart = pMiniMdImport->getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(pMiniMdImport->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + + if (ridEnd > ridStart) + { + // If there is any event, create the PropertyMap record in the emit scope + // Create new record. + IfFailGo(pMiniMdEmit->AddPropertyMapRecord(&pPropertyMap, &iPropertyMap)); + + // Set parent. + IfFailGo(pMiniMdEmit->PutToken(TBL_PropertyMap, PropertyMapRec::COL_Parent, pPropertyMap, tdEmit)); + } + + for (i = ridStart; i < ridEnd; i++) + { + // get the property rid + IfFailGo(pMiniMdImport->GetPropertyRid(i, (ULONG *)&prImp)); + + // only merge those Properties that are marked + if ( pMiniMdImport->GetFilterTable()->IsPropertyMarked(TokenFromRid(prImp, mdtProperty)) == false) + continue; + + + IfFailGo(pMiniMdImport->GetPropertyRecord(prImp, &pRecImport)); + IfFailGo(pMiniMdEmit->AddPropertyRecord(&pRecEmit, (RID *)&prEmit)); + + // copy the property record over + IfFailGo( CopyProperty(pImportData, pRecImport, pRecEmit) ); + + // Add Property to the PropertyMap. + IfFailGo( pMiniMdEmit->AddPropertyToPropertyMap(iPropertyMap, prEmit) ); + + // record the token movement + prImp = TokenFromRid(prImp, mdtProperty); + prEmit = TokenFromRid(prEmit, mdtProperty); + + IfFailGo( pCurTkMap->InsertNotFound(prImp, false, prEmit, &pTokenRec) ); + + // copy over the method semantics + IfFailGo( CopyMethodSemantics(pImportData, prImp, prEmit) ); + + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + prImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (!bSuppressMergeCheck) { + pMTD->m_cProperties++; + } + } + } + + // make sure we don't count any properties if merge check is suppressed on the type + _ASSERTE(pMTD->m_cProperties == 0 || !pMTD->m_bSuppressMergeCheck); + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyProperties() + + +//***************************************************************************** +// Copy Parameters given a TypeDef +//***************************************************************************** +HRESULT NEWMERGER::CopyParams( + MergeImportData *pImportData, + mdMethodDef mdImport, + mdMethodDef mdEmit) +{ + HRESULT hr = NOERROR; + ParamRec *pRecImport = NULL; + ParamRec *pRecEmit = NULL; + MethodRec *pMethodRec; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG ridStart, ridEnd; + ULONG i; + mdParamDef pdEmit; + mdParamDef pdImp; + TOKENREC *pTokenRec; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + + IfFailGo(pMiniMdImport->GetMethodRecord(RidFromToken(mdImport), &pMethodRec)); + ridStart = pMiniMdImport->getParamListOfMethod(pMethodRec); + IfFailGo(pMiniMdImport->getEndParamListOfMethod(RidFromToken(mdImport), &ridEnd)); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // loop through all InterfaceImpl + for (i = ridStart; i < ridEnd; i++) + { + // Get the param rid + IfFailGo(pMiniMdImport->GetParamRid(i, (ULONG *)&pdImp)); + + // only merge those Params that are marked + if ( pMiniMdImport->GetFilterTable()->IsParamMarked(TokenFromRid(pdImp, mdtParamDef)) == false) + continue; + + + IfFailGo(pMiniMdImport->GetParamRecord(pdImp, &pRecImport)); + IfFailGo(pMiniMdEmit->AddParamRecord(&pRecEmit, (RID *)&pdEmit)); + + // copy the Parameter record over + IfFailGo( CopyParam(pImportData, pRecImport, pRecEmit) ); + + // warning!! warning!! + // We cannot add paramRec to method list until it is fully set. + // AddParamToMethod will use the ulSequence in the record + IfFailGo( pMiniMdEmit->AddParamToMethod(RidFromToken(mdEmit), pdEmit)); + + // record the token movement + pdImp = TokenFromRid(pdImp, mdtParamDef); + pdEmit = TokenFromRid(pdEmit, mdtParamDef); + + IfFailGo( pCurTkMap->InsertNotFound(pdImp, false, pdEmit, &pTokenRec) ); + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyParams() + + +//***************************************************************************** +// Copy GenericParams given a TypeDef +//***************************************************************************** +HRESULT NEWMERGER::CopyGenericParams( + MergeImportData *pImportData, + mdToken tkImport, + mdToken tkEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + TOKENREC *pTokenRec; + GenericParamRec *pRecImport = NULL; + GenericParamRec *pRecEmit = NULL; + MDTOKENMAP *pCurTkMap; + HENUMInternal hEnum; + mdGenericParam gpImport; + mdGenericParam gpEmit; + LPCSTR szGenericParamName; + + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pMiniMdEmit = GetMiniMdEmit(); + pCurTkMap = pImportData->m_pMDTokenMap; + + IfFailGo( pMiniMdImport->FindGenericParamHelper(tkImport, &hEnum) ); + + while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &gpImport)) + { + // Get the import GenericParam record + _ASSERTE(TypeFromToken(gpImport) == mdtGenericParam); + IfFailGo(pMiniMdImport->GetGenericParamRecord(RidFromToken(gpImport), &pRecImport)); + + // Create new emit record. + IfFailGo(pMiniMdEmit->AddGenericParamRecord(&pRecEmit, (RID *)&gpEmit)); + + // copy the GenericParam content + pRecEmit->SetNumber( pRecImport->GetNumber()); + pRecEmit->SetFlags( pRecImport->GetFlags()); + + IfFailGo( pMiniMdEmit->PutToken(TBL_GenericParam, GenericParamRec::COL_Owner, pRecEmit, tkEmit)); + + IfFailGo(pMiniMdImport->getNameOfGenericParam(pRecImport, &szGenericParamName)); + IfFailGo( pMiniMdEmit->PutString(TBL_GenericParam, GenericParamRec::COL_Name, pRecEmit, szGenericParamName)); + + // record the token movement + gpImport = TokenFromRid(gpImport, mdtGenericParam); + gpEmit = TokenFromRid(gpEmit, mdtGenericParam); + + IfFailGo( pCurTkMap->InsertNotFound(gpImport, false, gpEmit, &pTokenRec) ); + + // copy over any constraints + IfFailGo( CopyGenericParamConstraints(pImportData, gpImport, gpEmit) ); + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyGenericParams() + + +//***************************************************************************** +// Copy GenericParamConstraints given a GenericParam +//***************************************************************************** +HRESULT NEWMERGER::CopyGenericParamConstraints( + MergeImportData *pImportData, + mdGenericParamConstraint tkImport, + mdGenericParamConstraint tkEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + TOKENREC *pTokenRec; + GenericParamConstraintRec *pRecImport = NULL; + GenericParamConstraintRec *pRecEmit = NULL; + MDTOKENMAP *pCurTkMap; + HENUMInternal hEnum; + mdGenericParamConstraint gpImport; + mdGenericParamConstraint gpEmit; + mdToken tkConstraint; + + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pMiniMdEmit = GetMiniMdEmit(); + pCurTkMap = pImportData->m_pMDTokenMap; + + IfFailGo( pMiniMdImport->FindGenericParamConstraintHelper(tkImport, &hEnum) ); + + while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &gpImport)) + { + // Get the import GenericParam record + _ASSERTE(TypeFromToken(gpImport) == mdtGenericParamConstraint); + IfFailGo(pMiniMdImport->GetGenericParamConstraintRecord(RidFromToken(gpImport), &pRecImport)); + + // Translate the constraint before creating new record. + tkConstraint = pMiniMdImport->getConstraintOfGenericParamConstraint(pRecImport); + if (pCurTkMap->Find(tkConstraint, &pTokenRec) == false) + { + // This should never fire unless the TypeDefs/Refs weren't merged + // before this code runs. + _ASSERTE(!"GenericParamConstraint Constraint not found in MERGER::CopyGenericParamConstraints. Bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + tkConstraint = pTokenRec->m_tkTo; + + // Create new emit record. + IfFailGo(pMiniMdEmit->AddGenericParamConstraintRecord(&pRecEmit, (RID *)&gpEmit)); + + // copy the GenericParamConstraint content + IfFailGo( pMiniMdEmit->PutToken(TBL_GenericParamConstraint, GenericParamConstraintRec::COL_Owner, pRecEmit, tkEmit)); + + IfFailGo( pMiniMdEmit->PutToken(TBL_GenericParamConstraint, GenericParamConstraintRec::COL_Constraint, pRecEmit, tkConstraint)); + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyGenericParamConstraints() + + +//***************************************************************************** +// Verify GenericParams given a TypeDef +//***************************************************************************** +HRESULT NEWMERGER::VerifyGenericParams( + MergeImportData *pImportData, + mdToken tkImport, + mdToken tkEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + TOKENREC *pTokenRec; + MDTOKENMAP *pCurTkMap; + HENUMInternal hEnumImport; // Enumerator for import scope. + HENUMInternal hEnumEmit; // Enumerator for emit scope. + ULONG cImport, cEmit; // Count of import & emit records. + ULONG i; // Enumerating records in import scope. + ULONG iEmit; // Tracking records in emit scope. + mdGenericParam gpImport; // Import scope GenericParam token. + mdGenericParam gpEmit; // Emit scope GenericParam token. + GenericParamRec *pRecImport = NULL; + GenericParamRec *pRecEmit = NULL; + LPCSTR szNameImport; // Name of param in import scope. + LPCSTR szNameEmit; // Name of param in emit scope. + + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pMiniMdEmit = GetMiniMdEmit(); + pCurTkMap = pImportData->m_pMDTokenMap; + + // Get enumerators for the input and output scopes. + IfFailGo(pMiniMdImport->FindGenericParamHelper(tkImport, &hEnumImport)); + IfFailGo(pMiniMdEmit->FindGenericParamHelper(tkEmit, &hEnumEmit)); + + // The counts should be the same. + IfFailGo(HENUMInternal::GetCount(&hEnumImport, &cImport)); + IfFailGo(HENUMInternal::GetCount(&hEnumEmit, &cEmit)); + + if (cImport != cEmit) + { + CheckContinuableErrorEx(META_E_GENERICPARAM_INCONSISTENT, pImportData, tkImport); + // If we are here, the linker says this error is OK. + } + + for (i=iEmit=0; i<cImport; ++i) + { + // Get the import GenericParam record + IfFailGo(HENUMInternal::GetElement(&hEnumImport, i, &gpImport)); + _ASSERTE(TypeFromToken(gpImport) == mdtGenericParam); + IfFailGo(pMiniMdImport->GetGenericParamRecord(RidFromToken(gpImport), &pRecImport)); + + // Find the emit record. If the import and emit scopes are ordered the same + // this is easy; otherwise go looking for it. + // Get the "next" emit record. + if (iEmit < cEmit) + { + IfFailGo(HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit)); + _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParam); + IfFailGo(pMiniMdEmit->GetGenericParamRecord(RidFromToken(gpEmit), &pRecEmit)); + } + + // If the import and emit sequence numbers don't match, go looking. + // Also, if we would have walked off end of array, go looking. + if (iEmit >= cEmit || pRecImport->GetNumber() != pRecEmit->GetNumber()) + { + for (iEmit=0; iEmit<cEmit; ++iEmit) + { + IfFailGo( HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit)); + _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParam); + IfFailGo(pMiniMdEmit->GetGenericParamRecord(RidFromToken(gpEmit), &pRecEmit)); + + // The one we want? + if (pRecImport->GetNumber() == pRecEmit->GetNumber()) + break; + } + if (iEmit >= cEmit) + goto Error; // Didn't find it + } + + // Check that these "n'th" GenericParam records match. + + // Flags. + if (pRecImport->GetFlags() != pRecEmit->GetFlags()) + goto Error; + + // Name. + IfFailGo(pMiniMdImport->getNameOfGenericParam(pRecImport, &szNameImport)); + IfFailGo(pMiniMdEmit->getNameOfGenericParam(pRecEmit, &szNameEmit)); + if (strcmp(szNameImport, szNameEmit) != 0) + goto Error; + + // Verify any constraints. + gpImport = TokenFromRid(gpImport, mdtGenericParam); + gpEmit = TokenFromRid(gpEmit, mdtGenericParam); + hr = VerifyGenericParamConstraints(pImportData, gpImport, gpEmit); + + if (SUCCEEDED(hr)) + { + // record the token movement + IfFailGo( pCurTkMap->InsertNotFound(gpImport, true, gpEmit, &pTokenRec) ); + } + else + { +Error: + // inconsistent in GenericParams + hr = S_OK; // discard old error; new error will be returned from CheckContinuableError + CheckContinuableErrorEx(META_E_GENERICPARAM_INCONSISTENT, pImportData, tkImport); + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyGenericParams() + + +//***************************************************************************** +// Verify GenericParamConstraints given a GenericParam +//***************************************************************************** +HRESULT NEWMERGER::VerifyGenericParamConstraints( + MergeImportData *pImportData, // The import scope. + mdGenericParam gpImport, // Import GenericParam. + mdGenericParam gpEmit) // Emit GenericParam. +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + TOKENREC *pTokenRec; + HENUMInternal hEnumImport; // Enumerator for import scope. + HENUMInternal hEnumEmit; // Enumerator for emit scope. + ULONG cImport, cEmit; // Count of import & emit records. + ULONG i; // Enumerating records in import scope. + ULONG iEmit; // Tracking records in emit scope. + GenericParamConstraintRec *pRecImport = NULL; + GenericParamConstraintRec *pRecEmit = NULL; + MDTOKENMAP *pCurTkMap; + mdToken tkConstraintImport = mdTokenNil; + mdToken tkConstraintEmit = mdTokenNil; + + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pMiniMdEmit = GetMiniMdEmit(); + pCurTkMap = pImportData->m_pMDTokenMap; + + // Get enumerators for the input and output scopes. + IfFailGo(pMiniMdImport->FindGenericParamConstraintHelper(gpImport, &hEnumImport)); + IfFailGo(pMiniMdEmit->FindGenericParamConstraintHelper(gpEmit, &hEnumEmit)); + + // The counts should be the same. + IfFailGo(HENUMInternal::GetCount(&hEnumImport, &cImport)); + IfFailGo(HENUMInternal::GetCount(&hEnumEmit, &cEmit)); + + if (cImport != cEmit) + IfFailGo(META_E_GENERICPARAM_INCONSISTENT); // Different numbers of constraints. + + for (i=iEmit=0; i<cImport; ++i) + { + // Get the import GenericParam record + IfFailGo( HENUMInternal::GetElement(&hEnumImport, i, &gpImport)); + _ASSERTE(TypeFromToken(gpImport) == mdtGenericParamConstraint); + IfFailGo(pMiniMdImport->GetGenericParamConstraintRecord(RidFromToken(gpImport), &pRecImport)); + + // Get the constraint. + tkConstraintImport = pMiniMdImport->getConstraintOfGenericParamConstraint(pRecImport); + if (pCurTkMap->Find(tkConstraintImport, &pTokenRec) == false) + { + // This should never fire unless the TypeDefs/Refs weren't merged + // before this code runs. + _ASSERTE(!"GenericParamConstraint Constraint not found in MERGER::VerifyGenericParamConstraints. Bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + tkConstraintImport = pTokenRec->m_tkTo; + + // Find the emit record. If the import and emit scopes are ordered the same + // this is easy; otherwise go looking for it. + // Get the "next" emit record. + if (iEmit < cEmit) + { + IfFailGo( HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit)); + _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParamConstraint); + IfFailGo(pMiniMdEmit->GetGenericParamConstraintRecord(RidFromToken(gpEmit), &pRecEmit)); + tkConstraintEmit = pMiniMdEmit->getConstraintOfGenericParamConstraint(pRecEmit); + } + + // If the import and emit constraints don't match, go looking. + if (iEmit >= cEmit || tkConstraintEmit != tkConstraintImport) + { + for (iEmit=0; iEmit<cEmit; ++iEmit) + { + IfFailGo( HENUMInternal::GetElement(&hEnumEmit, iEmit, &gpEmit)); + _ASSERTE(TypeFromToken(gpEmit) == mdtGenericParamConstraint); + IfFailGo(pMiniMdEmit->GetGenericParamConstraintRecord(RidFromToken(gpEmit), &pRecEmit)); + tkConstraintEmit = pMiniMdEmit->getConstraintOfGenericParamConstraint(pRecEmit); + + // The one we want? + if (tkConstraintEmit == tkConstraintImport) + break; + } + if (iEmit >= cEmit) + { + IfFailGo(META_E_GENERICPARAM_INCONSISTENT); // Didn't find the constraint + } + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyGenericParamConstraints() + + +//***************************************************************************** +// Verify Methods +//***************************************************************************** +HRESULT NEWMERGER::VerifyMethods( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + MethodRec *pRecImp; + MethodRec *pRecEmit; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + + TypeDefRec *pTypeDefRec; + LPCUTF8 szName; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + TOKENREC *pTokenRec; + mdMethodDef mdImp; + mdMethodDef mdEmit; + MDTOKENMAP *pCurTkMap; + + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + ULONG cImport = 0; // count of non-merge check suppressed methods + mdCustomAttribute tkCA; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // Get a count of records in the import scope; prepare to enumerate them. + IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec)); + ridStart = pMiniMdImport->getMethodListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMdImport->getEndMethodListOfTypeDef(RidFromToken(tdImport), &ridEnd)); + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + // loop through all Methods of the TypeDef + for (i = ridStart; i < ridEnd; i++) + { + IfFailGo(pMiniMdImport->GetMethodRid(i, (ULONG *)&mdImp)); + + // only verify those Methods that are marked + if ( pMiniMdImport->GetFilterTable()->IsMethodMarked(TokenFromRid(mdImp, mdtMethodDef)) == false) + continue; + + IfFailGo(pMiniMdImport->GetMethodRecord(mdImp, &pRecImp)); + + if (m_fDupCheck == FALSE && tdImport == pImportData->m_pRegMetaImport->m_tdModule) // TokenFromRid(1, mdtTypeDef)) + { + // No dup check. This is the scenario that we only have one import scope. Just copy over the + // globals. + goto CopyMethodLabel; + } + + IfFailGo(pMiniMdImport->getNameOfMethod(pRecImp, &szName)); + IfFailGo(pMiniMdImport->getSignatureOfMethod(pRecImp, &pbSig, &cbSig)); + + mdImp = TokenFromRid(mdImp, mdtMethodDef); + + if ( IsMdPrivateScope( pRecImp->GetFlags() ) ) + { + // Trigger additive merge + goto CopyMethodLabel; + } + + // convert rid contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly scope information. + pMiniMdImport, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + + hr = ImportHelper::FindMethod( + pMiniMdEmit, + tdEmit, + szName, + (const COR_SIGNATURE *)qbSig.Ptr(), + cbEmit, + &mdEmit); + + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + mdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (bSuppressMergeCheck || (tdImport == pImportData->m_pRegMetaImport->m_tdModule)) + { + // global functions! Make sure that we move over the non-duplicate global function + // declaration + // + if (hr == S_OK) + { + // found the duplicate + IfFailGo( VerifyMethod(pImportData, mdImp, mdEmit) ); + } + else + { +CopyMethodLabel: + // not a duplicate! Copy over the + IfFailGo(pMiniMdEmit->AddMethodRecord(&pRecEmit, (RID *)&mdEmit)); + + // copy the method content over + IfFailGo( CopyMethod(pImportData, pRecImp, pRecEmit) ); + + IfFailGo( pMiniMdEmit->AddMethodToTypeDef(RidFromToken(tdEmit), mdEmit)); + + // record the token movement + mdEmit = TokenFromRid(mdEmit, mdtMethodDef); + IfFailGo( pMiniMdEmit->AddMemberDefToHash( + mdEmit, + tdEmit) ); + + mdImp = TokenFromRid(mdImp, mdtMethodDef); + IfFailGo( pCurTkMap->InsertNotFound(mdImp, false, mdEmit, &pTokenRec) ); + + // copy over the children + IfFailGo( CopyParams(pImportData, mdImp, mdEmit) ); + IfFailGo( CopyGenericParams(pImportData, mdImp, mdEmit) ); + + } + } + else + { + if (hr == S_OK) + { + // Good! We are supposed to find a duplicate + IfFailGo( VerifyMethod(pImportData, mdImp, mdEmit) ); + } + else + { + // Oops! The typedef is duplicated but the method is not!! + hr = S_OK; // discard old error; new error will be returned from CheckContinuableError + CheckContinuableErrorEx(META_E_METHD_NOT_FOUND, pImportData, mdImp); + } + + cImport++; + } + } + + // The counts should be the same, unless this is <module> + if (cImport != pMTD->m_cMethods && tdImport != pImportData->m_pRegMetaImport->m_tdModule) + { + CheckContinuableErrorEx(META_E_METHOD_COUNTS, pImportData, tdImport); + // If we are here, the linker says this error is OK. + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyMethods() + + +//***************************************************************************** +// verify a duplicated method +//***************************************************************************** +HRESULT NEWMERGER::VerifyMethod( + MergeImportData *pImportData, + mdMethodDef mdImp, // [IN] the emit record to fill + mdMethodDef mdEmit) // [IN] the record to import +{ + HRESULT hr; + MethodRec *pRecImp; + MethodRec *pRecEmit; + TOKENREC *pTokenRec; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + IfFailGo( pCurTkMap->InsertNotFound(mdImp, true, mdEmit, &pTokenRec) ); + + IfFailGo(pMiniMdImport->GetMethodRecord(RidFromToken(mdImp), &pRecImp)); + + // We need to make sure that the impl flags are propagated . + // Rules are: if the first method has miForwardRef flag set but the new method does not, + // we want to disable the miForwardRef flag. If the one found in the emit scope does not have + // miForwardRef set and the second one doesn't either, we want to make sure that the rest of + // impl flags are the same. + // + if ( !IsMiForwardRef( pRecImp->GetImplFlags() ) ) + { + IfFailGo(pMiniMdEmit->GetMethodRecord(RidFromToken(mdEmit), &pRecEmit)); + if (!IsMiForwardRef(pRecEmit->GetImplFlags())) + { + // make sure the rest of ImplFlags are the same + if (pRecEmit->GetImplFlags() != pRecImp->GetImplFlags()) + { + // inconsistent in implflags + CheckContinuableErrorEx(META_E_METHDIMPL_INCONSISTENT, pImportData, mdImp); + } + } + else + { + // propagate the importing ImplFlags + pRecEmit->SetImplFlags(pRecImp->GetImplFlags()); + } + } + + // verify the children + IfFailGo( VerifyParams(pImportData, mdImp, mdEmit) ); + IfFailGo( VerifyGenericParams(pImportData, mdImp, mdEmit) ); + +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyMethod() + + +//***************************************************************************** +// Verify Fields +//***************************************************************************** +HRESULT NEWMERGER::VerifyFields( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + FieldRec *pRecImp; + FieldRec *pRecEmit; + mdFieldDef fdImp; + mdFieldDef fdEmit; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + + TypeDefRec *pTypeDefRec; + LPCUTF8 szName; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + TOKENREC *pTokenRec; + MDTOKENMAP *pCurTkMap; + + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + ULONG cImport = 0; // count of non-merge check suppressed fields + mdCustomAttribute tkCA; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // Get a count of records in the import scope; prepare to enumerate them. + IfFailGo(pMiniMdImport->GetTypeDefRecord(RidFromToken(tdImport), &pTypeDefRec)); + ridStart = pMiniMdImport->getFieldListOfTypeDef(pTypeDefRec); + IfFailGo(pMiniMdImport->getEndFieldListOfTypeDef(RidFromToken(tdImport), &ridEnd)); + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + // loop through all fields of the TypeDef + for (i = ridStart; i < ridEnd; i++) + { + IfFailGo(pMiniMdImport->GetFieldRid(i, (ULONG *)&fdImp)); + + // only verify those fields that are marked + if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(TokenFromRid(fdImp, mdtFieldDef)) == false) + continue; + + IfFailGo(pMiniMdImport->GetFieldRecord(fdImp, &pRecImp)); + + if (m_fDupCheck == FALSE && tdImport == pImportData->m_pRegMetaImport->m_tdModule) + { + // No dup check. This is the scenario that we only have one import scope. Just copy over the + // globals. + goto CopyFieldLabel; + } + + IfFailGo(pMiniMdImport->getNameOfField(pRecImp, &szName)); + IfFailGo(pMiniMdImport->getSignatureOfField(pRecImp, &pbSig, &cbSig)); + + if ( IsFdPrivateScope(pRecImp->GetFlags())) + { + // Trigger additive merge + fdImp = TokenFromRid(fdImp, mdtFieldDef); + goto CopyFieldLabel; + } + + // convert rid contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly scope information. + pMiniMdImport, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + + hr = ImportHelper::FindField( + pMiniMdEmit, + tdEmit, + szName, + (const COR_SIGNATURE *)qbSig.Ptr(), + cbEmit, + &fdEmit); + + fdImp = TokenFromRid(fdImp, mdtFieldDef); + + bSuppressMergeCheck = + (IsFdStatic(pRecImp->GetFlags()) && pMTD->m_bSuppressMergeCheck) || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + fdImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (bSuppressMergeCheck || (tdImport == pImportData->m_pRegMetaImport->m_tdModule)) + { + // global data! Make sure that we move over the non-duplicate global function + // declaration + // + if (hr == S_OK) + { + // found the duplicate + IfFailGo( pCurTkMap->InsertNotFound(fdImp, true, fdEmit, &pTokenRec) ); + } + else + { +CopyFieldLabel: + // not a duplicate! Copy over the + IfFailGo(pMiniMdEmit->AddFieldRecord(&pRecEmit, (RID *)&fdEmit)); + + // copy the field record over + IfFailGo( CopyField(pImportData, pRecImp, pRecEmit) ); + + IfFailGo( pMiniMdEmit->AddFieldToTypeDef(RidFromToken(tdEmit), fdEmit)); + + // record the token movement + fdEmit = TokenFromRid(fdEmit, mdtFieldDef); + IfFailGo( pMiniMdEmit->AddMemberDefToHash( + fdEmit, + tdEmit) ); + + fdImp = TokenFromRid(fdImp, mdtFieldDef); + IfFailGo( pCurTkMap->InsertNotFound(fdImp, false, fdEmit, &pTokenRec) ); + } + } + else + { + if (hr == S_OK) + { + // Good! We are supposed to find a duplicate + IfFailGo( pCurTkMap->InsertNotFound(fdImp, true, fdEmit, &pTokenRec) ); + } + else + { + // Oops! The typedef is duplicated but the field is not!! + hr = S_OK; // discard old error; new error will be returned from CheckContinuableError + CheckContinuableErrorEx(META_E_FIELD_NOT_FOUND, pImportData, fdImp); + } + + cImport++; + } + } + + // The counts should be the same, unless this is <module> + if (cImport != pMTD->m_cFields && tdImport != pImportData->m_pRegMetaImport->m_tdModule) + { + CheckContinuableErrorEx(META_E_FIELD_COUNTS, pImportData, tdImport); + // If we are here, the linker says this error is OK. + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyFields() + + +//***************************************************************************** +// Verify Events +//***************************************************************************** +HRESULT NEWMERGER::VerifyEvents( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + RID ridEventMap, ridEventMapEmit; + EventMapRec *pEventMapRec; + EventRec *pRecImport; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + mdEvent evImport; + mdEvent evEmit; + TOKENREC *pTokenRec; + LPCUTF8 szName; + mdToken tkType; + MDTOKENMAP *pCurTkMap; + + EventMapRec *pEventMapEmit; + EventRec *pRecEmit; + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + ULONG cImport = 0; // count of non-merge check suppressed events + mdCustomAttribute tkCA; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + IfFailGo(pMiniMdImport->FindEventMapFor(RidFromToken(tdImport), &ridEventMap)); + if (!InvalidRid(ridEventMap)) + { + // Get a count of records already in emit scope. + IfFailGo(pMiniMdEmit->FindEventMapFor(RidFromToken(tdEmit), &ridEventMapEmit)); + + if (InvalidRid(ridEventMapEmit)) { + // If there is any event, create the eventmap record in the emit scope + // Create new record. + IfFailGo(pMiniMdEmit->AddEventMapRecord(&pEventMapEmit, &ridEventMapEmit)); + + // Set parent. + IfFailGo(pMiniMdEmit->PutToken(TBL_EventMap, EventMapRec::COL_Parent, pEventMapEmit, tdEmit)); + } + + // Get a count of records in the import scope; prepare to enumerate them. + IfFailGo(pMiniMdImport->GetEventMapRecord(ridEventMap, &pEventMapRec)); + ridStart = pMiniMdImport->getEventListOfEventMap(pEventMapRec); + IfFailGo(pMiniMdImport->getEndEventListOfEventMap(ridEventMap, &ridEnd)); + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + for (i = ridStart; i < ridEnd; i++) + { + // get the property rid + IfFailGo(pMiniMdImport->GetEventRid(i, (ULONG *)&evImport)); + + // only verify those Events that are marked + if ( pMiniMdImport->GetFilterTable()->IsEventMarked(TokenFromRid(evImport, mdtEvent)) == false) + continue; + + IfFailGo(pMiniMdImport->GetEventRecord(evImport, &pRecImport)); + IfFailGo(pMiniMdImport->getNameOfEvent(pRecImport, &szName)); + tkType = pMiniMdImport->getEventTypeOfEvent( pRecImport ); + IfFailGo( pCurTkMap->Remap(tkType, &tkType) ); + evImport = TokenFromRid( evImport, mdtEvent); + + hr = ImportHelper::FindEvent( + pMiniMdEmit, + tdEmit, + szName, + &evEmit); + + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + evImport, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (bSuppressMergeCheck) + { + + if (hr == S_OK ) + { + // Good. We found the matching event when we have a duplicate typedef + IfFailGo( pCurTkMap->InsertNotFound(evImport, true, evEmit, &pTokenRec) ); + } + else + { + // not a duplicate! Copy over the + IfFailGo(pMiniMdEmit->AddEventRecord(&pRecEmit, (RID *)&evEmit)); + + // copy the event record over + IfFailGo( CopyEvent(pImportData, pRecImport, pRecEmit) ); + + // Add Event to the EventMap. + IfFailGo( pMiniMdEmit->AddEventToEventMap(ridEventMapEmit, evEmit) ); + + // record the token movement + evEmit = TokenFromRid(evEmit, mdtEvent); + + IfFailGo( pCurTkMap->InsertNotFound(evImport, false, evEmit, &pTokenRec) ); + + // copy over the method semantics + IfFailGo( CopyMethodSemantics(pImportData, evImport, evEmit) ); + } + } + else + { + if (hr == S_OK ) + { + // Good. We found the matching event when we have a duplicate typedef + IfFailGo( pCurTkMap->InsertNotFound(evImport, true, evEmit, &pTokenRec) ); + } + else + { + // Oops! The typedef is duplicated but the event is not!! + hr = S_OK; // discard old error; new error will be returned from CheckContinuableError + CheckContinuableErrorEx(META_E_EVENT_NOT_FOUND, pImportData, evImport); + + } + + cImport++; + } + } + + // The counts should be the same, unless this is <module> + if (cImport != pMTD->m_cEvents && tdImport != pImportData->m_pRegMetaImport->m_tdModule) + { + CheckContinuableErrorEx(META_E_EVENT_COUNTS, pImportData, tdImport); + // If we are here, the linker says this error is OK. + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyEvents() + + +//***************************************************************************** +// Verify Properties +//***************************************************************************** +HRESULT NEWMERGER::VerifyProperties( + MergeImportData *pImportData, + mdTypeDef tdImport, + mdTypeDef tdEmit) +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + RID ridPropertyMap, ridPropertyMapEmit; + PropertyMapRec *pPropertyMapRec; + PropertyRec *pRecImport; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + mdProperty prImp; + mdProperty prEmit; + TOKENREC *pTokenRec; + LPCUTF8 szName; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + MDTOKENMAP *pCurTkMap; + + PropertyMapRec *pPropertyMapEmit; + PropertyRec *pRecEmit; + MergeTypeData *pMTD; + BOOL bSuppressMergeCheck; + ULONG cImport = 0; // count of non-merge check suppressed properties + mdCustomAttribute tkCA; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + IfFailGo(pMiniMdImport->FindPropertyMapFor(RidFromToken(tdImport), &ridPropertyMap)); + if (!InvalidRid(ridPropertyMap)) + { + // Get a count of records already in emit scope. + IfFailGo(pMiniMdEmit->FindPropertyMapFor(RidFromToken(tdEmit), &ridPropertyMapEmit)); + + if (InvalidRid(ridPropertyMapEmit)) + { + // If there is any event, create the PropertyMap record in the emit scope + // Create new record. + IfFailGo(pMiniMdEmit->AddPropertyMapRecord(&pPropertyMapEmit, &ridPropertyMapEmit)); + + // Set parent. + IfFailGo(pMiniMdEmit->PutToken(TBL_PropertyMap, PropertyMapRec::COL_Parent, pPropertyMapEmit, tdEmit)); + } + + // Get a count of records in the import scope; prepare to enumerate them. + IfFailGo(pMiniMdImport->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ridStart = pMiniMdImport->getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(pMiniMdImport->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + + pMTD = m_rMTDs.Get(RidFromToken(tdEmit)); + + for (i = ridStart; i < ridEnd; i++) + { + // get the property rid + IfFailGo(pMiniMdImport->GetPropertyRid(i, (ULONG *)&prImp)); + + // only verify those Properties that are marked + if ( pMiniMdImport->GetFilterTable()->IsPropertyMarked(TokenFromRid(prImp, mdtProperty)) == false) + continue; + + IfFailGo(pMiniMdImport->GetPropertyRecord(prImp, &pRecImport)); + IfFailGo(pMiniMdImport->getNameOfProperty(pRecImport, &szName)); + IfFailGo(pMiniMdImport->getTypeOfProperty(pRecImport, &pbSig, &cbSig)); + prImp = TokenFromRid( prImp, mdtProperty); + + // convert rid contained in signature to new scope + IfFailGo( ImportHelper::MergeUpdateTokenInSig( + NULL, // Emit assembly. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly scope information. + pMiniMdImport, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit) ); // number of bytes write to cbEmit + + hr = ImportHelper::FindProperty( + pMiniMdEmit, + tdEmit, + szName, + (PCCOR_SIGNATURE) qbSig.Ptr(), + cbEmit, + &prEmit); + + bSuppressMergeCheck = pMTD->m_bSuppressMergeCheck || + ((pImportData->m_tkSuppressMergeCheckCtor != mdTokenNil) && + (S_OK == ImportHelper::FindCustomAttributeByToken(pMiniMdImport, + prImp, pImportData->m_tkSuppressMergeCheckCtor, NULL, 0, &tkCA))); + + if (bSuppressMergeCheck) + { + if (hr == S_OK) + { + // Good. We found the matching property when we have a duplicate typedef + IfFailGo( pCurTkMap->InsertNotFound(prImp, true, prEmit, &pTokenRec) ); + } + else + { + IfFailGo(pMiniMdEmit->AddPropertyRecord(&pRecEmit, (RID *)&prEmit)); + + // copy the property record over + IfFailGo( CopyProperty(pImportData, pRecImport, pRecEmit) ); + + // Add Property to the PropertyMap. + IfFailGo( pMiniMdEmit->AddPropertyToPropertyMap(ridPropertyMapEmit, prEmit) ); + + // record the token movement + prEmit = TokenFromRid(prEmit, mdtProperty); + + IfFailGo( pCurTkMap->InsertNotFound(prImp, false, prEmit, &pTokenRec) ); + + // copy over the method semantics + IfFailGo( CopyMethodSemantics(pImportData, prImp, prEmit) ); + } + } + else + { + if (hr == S_OK) + { + // Good. We found the matching property when we have a duplicate typedef + IfFailGo( pCurTkMap->InsertNotFound(prImp, true, prEmit, &pTokenRec) ); + } + else + { + hr = S_OK; // discard old error; new error will be returned from CheckContinuableError + CheckContinuableErrorEx(META_E_PROP_NOT_FOUND, pImportData, prImp); + } + + cImport++; + } + } + + // The counts should be the same, unless this is <module> + if (cImport != pMTD->m_cProperties && tdImport != pImportData->m_pRegMetaImport->m_tdModule) + { + CheckContinuableErrorEx(META_E_PROPERTY_COUNTS, pImportData, tdImport); + // If we are here, the linker says this error is OK. + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyProperties() + + +//***************************************************************************** +// Verify Parameters given a Method +//***************************************************************************** +HRESULT NEWMERGER::VerifyParams( + MergeImportData *pImportData, + mdMethodDef mdImport, + mdMethodDef mdEmit) +{ + HRESULT hr = NOERROR; + ParamRec *pRecImport = NULL; + ParamRec *pRecEmit = NULL; + MethodRec *pMethodRec; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG ridStart, ridEnd; + ULONG ridStartEmit, ridEndEmit; + ULONG cImport, cEmit; + ULONG i, j; + mdParamDef pdEmit = 0; + mdParamDef pdImp; + TOKENREC *pTokenRec; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + pCurTkMap = pImportData->m_pMDTokenMap; + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // Get count of params in import scope; prepare to enumerate. + IfFailGo(pMiniMdImport->GetMethodRecord(RidFromToken(mdImport), &pMethodRec)); + ridStart = pMiniMdImport->getParamListOfMethod(pMethodRec); + IfFailGo(pMiniMdImport->getEndParamListOfMethod(RidFromToken(mdImport), &ridEnd)); + cImport = ridEnd - ridStart; + + // Get count of params in emit scope; prepare to enumerate. + IfFailGo(pMiniMdEmit->GetMethodRecord(RidFromToken(mdEmit), &pMethodRec)); + ridStartEmit = pMiniMdEmit->getParamListOfMethod(pMethodRec); + IfFailGo(pMiniMdEmit->getEndParamListOfMethod(RidFromToken(mdEmit), &ridEndEmit)); + cEmit = ridEndEmit - ridStartEmit; + + // The counts should be the same. + if (cImport != cEmit) + { + // That is, unless this is <module>, so get the method's parent. + mdTypeDef tdImport; + IfFailGo(pMiniMdImport->FindParentOfMethodHelper(mdImport, &tdImport)); + if (tdImport != pImportData->m_pRegMetaImport->m_tdModule) + CheckContinuableErrorEx(META_E_PARAM_COUNTS, pImportData, mdImport); + // If we are here, the linker says this error is OK. + } + + // loop through all Parameters + for (i = ridStart; i < ridEnd; i++) + { + // Get the importing param row + IfFailGo(pMiniMdImport->GetParamRid(i, (ULONG *)&pdImp)); + + // only verify those Params that are marked + if ( pMiniMdImport->GetFilterTable()->IsParamMarked(TokenFromRid(pdImp, mdtParamDef)) == false) + continue; + + + IfFailGo(pMiniMdImport->GetParamRecord(pdImp, &pRecImport)); + pdImp = TokenFromRid(pdImp, mdtParamDef); + + // It turns out when we merge a typelib with itself, the emit and import scope + // has different sequence of parameter + // + // find the corresponding emit param row + for (j = ridStartEmit; j < ridEndEmit; j++) + { + IfFailGo(pMiniMdEmit->GetParamRid(j, (ULONG *)&pdEmit)); + IfFailGo(pMiniMdEmit->GetParamRecord(pdEmit, &pRecEmit)); + if (pRecEmit->GetSequence() == pRecImport->GetSequence()) + break; + } + + if (j == ridEndEmit) + { + // did not find the corresponding parameter in the emiting scope + hr = S_OK; // discard old error; new error will be returned from CheckContinuableError + CheckContinuableErrorEx(META_S_PARAM_MISMATCH, pImportData, pdImp); + } + + else + { + _ASSERTE( pRecEmit->GetSequence() == pRecImport->GetSequence() ); + + pdEmit = TokenFromRid(pdEmit, mdtParamDef); + + // record the token movement +#ifdef WE_DONT_NEED_TO_CHECK_NAMES__THEY_DONT_AFFECT_ANYTHING + LPCUTF8 szNameImp; + LPCUTF8 szNameEmit; + IfFailGo(pMiniMdImport->getNameOfParam(pRecImport, &szNameImp)); + IfFailGo(pMiniMdEmit->getNameOfParam(pRecEmit, &szNameEmit)); + if (szNameImp && szNameEmit && strcmp(szNameImp, szNameEmit) != 0) + { + // parameter name doesn't match + CheckContinuableErrorEx(META_S_PARAM_MISMATCH, pImportData, pdImp); + } +#endif + if (pRecEmit->GetFlags() != pRecImport->GetFlags()) + { + // flags doesn't match + CheckContinuableErrorEx(META_S_PARAM_MISMATCH, pImportData, pdImp); + } + + // record token movement. This is a duplicate. + IfFailGo( pCurTkMap->InsertNotFound(pdImp, true, pdEmit, &pTokenRec) ); + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::VerifyParams() + + +//***************************************************************************** +// merging MemberRef +//***************************************************************************** +HRESULT NEWMERGER::MergeMemberRefs( ) +{ + HRESULT hr = NOERROR; + MemberRefRec *pRecImport = NULL; + MemberRefRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdMemberRef mrEmit; + mdMemberRef mrImp; + bool bDuplicate = false; + TOKENREC *pTokenRec; + mdToken tkParentImp; + mdToken tkParentEmit; + + LPCUTF8 szNameImp; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + + bool isRefOptimizedToDef; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + + iCount = pMiniMdImport->getCountMemberRefs(); + + // loop through all MemberRef + for (i = 1; i <= iCount; i++) + { + + // only merge those MemberRefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsMemberRefMarked(TokenFromRid(i, mdtMemberRef)) == false) + continue; + + isRefOptimizedToDef = false; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetMemberRefRecord(i, &pRecImport)); + IfFailGo(pMiniMdImport->getNameOfMemberRef(pRecImport, &szNameImp)); + IfFailGo(pMiniMdImport->getSignatureOfMemberRef(pRecImport, &pbSig, &cbSig)); + tkParentImp = pMiniMdImport->getClassOfMemberRef(pRecImport); + + IfFailGo( pCurTkMap->Remap(tkParentImp, &tkParentEmit) ); + + // convert rid contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly information. + pMiniMdImport, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + + // We want to know if we can optimize this MemberRef to a FieldDef or MethodDef + if (TypeFromToken(tkParentEmit) == mdtTypeDef && RidFromToken(tkParentEmit) != 0) + { + // The parent of this MemberRef has been successfully optimized to a TypeDef. Then this MemberRef should be + // be able to optimized to a MethodDef or FieldDef unless one of the parent in the inheritance hierachy + // is through TypeRef. Then this MemberRef stay as MemberRef. If This is a VarArg calling convention, then + // we will remap the MemberRef's parent to a MethodDef or stay as TypeRef. + // + mdToken tkParent = tkParentEmit; + mdToken tkMethDefOrFieldDef; + PCCOR_SIGNATURE pbSigTmp = (const COR_SIGNATURE *) qbSig.Ptr(); + + while (TypeFromToken(tkParent) == mdtTypeDef && RidFromToken(tkParent) != 0) + { + TypeDefRec *pRec; + hr = ImportHelper::FindMember(pMiniMdEmit, tkParent, szNameImp, pbSigTmp, cbEmit, &tkMethDefOrFieldDef); + if (hr == S_OK) + { + // We have found a match!! + if (isCallConv(CorSigUncompressCallingConv(pbSigTmp), IMAGE_CEE_CS_CALLCONV_VARARG)) + { + // The found MethodDef token will replace this MemberRef's parent token + _ASSERTE(TypeFromToken(tkMethDefOrFieldDef) == mdtMethodDef); + tkParentEmit = tkMethDefOrFieldDef; + break; + } + else + { + // The found MethodDef/FieldDef token will replace this MemberRef token and we won't introduce a MemberRef + // record. + // + mrEmit = tkMethDefOrFieldDef; + isRefOptimizedToDef = true; + bDuplicate = true; + break; + } + } + + // now walk up to the parent class of tkParent and try to resolve this MemberRef + IfFailGo(pMiniMdEmit->GetTypeDefRecord(RidFromToken(tkParent), &pRec)); + tkParent = pMiniMdEmit->getExtendsOfTypeDef(pRec); + } + + // When we exit the loop, there are several possibilities: + // 1. We found a MethodDef/FieldDef to replace the MemberRef + // 2. We found a MethodDef matches the MemberRef but the MemberRef is VarArg, thus we want to use the MethodDef in the + // parent column but not replacing it. + // 3. We exit because we run out the TypeDef on the parent chain. If it is because we encounter a TypeRef, this TypeRef will + // replace the parent column of the MemberRef. Or we encounter nil token! (This can be unresolved global MemberRef or + // compiler error to put an undefined MemberRef. In this case, we should just use the old tkParentEmit + // on the parent column for the MemberRef. + + if (TypeFromToken(tkParent) == mdtTypeRef && RidFromToken(tkParent) != 0) + { + // we had walked up the parent's chain to resolve it but we have not been successful and got stopped by a TypeRef. + // Then we will use this TypeRef as the parent of the emit MemberRef record + // + tkParentEmit = tkParent; + } + } + else if ((TypeFromToken(tkParentEmit) == mdtMethodDef && + !isCallConv(CorSigUncompressCallingConv(pbSig), IMAGE_CEE_CS_CALLCONV_VARARG)) || + (TypeFromToken(tkParentEmit) == mdtFieldDef)) + { + // If the MemberRef's parent is already a non-vararg MethodDef or FieldDef, we can also + // safely drop the MemberRef + mrEmit = tkParentEmit; + isRefOptimizedToDef = true; + bDuplicate = true; + } + + // If the Ref cannot be optimized to a Def or MemberRef to Def optmization is turned off, do the following. + if (isRefOptimizedToDef == false || !((m_optimizeRefToDef & MDMemberRefToDef) == MDMemberRefToDef)) + { + // does this MemberRef already exist in the emit scope? + if ( m_fDupCheck && ImportHelper::FindMemberRef( + pMiniMdEmit, + tkParentEmit, + szNameImp, + (const COR_SIGNATURE *) qbSig.Ptr(), + cbEmit, + &mrEmit) == S_OK ) + { + // Yes, it does + bDuplicate = true; + } + else + { + // No, it doesn't. Copy it over. + bDuplicate = false; + IfFailGo(pMiniMdEmit->AddMemberRefRecord(&pRecEmit, (RID *)&mrEmit)); + mrEmit = TokenFromRid( mrEmit, mdtMemberRef ); + + // Copy over the MemberRef context + IfFailGo(pMiniMdEmit->PutString(TBL_MemberRef, MemberRefRec::COL_Name, pRecEmit, szNameImp)); + IfFailGo(pMiniMdEmit->PutToken(TBL_MemberRef, MemberRefRec::COL_Class, pRecEmit, tkParentEmit)); + IfFailGo(pMiniMdEmit->PutBlob(TBL_MemberRef, MemberRefRec::COL_Signature, pRecEmit, + qbSig.Ptr(), cbEmit)); + IfFailGo(pMiniMdEmit->AddMemberRefToHash(mrEmit) ); + } + } + // record the token movement + mrImp = TokenFromRid(i, mdtMemberRef); + IfFailGo( pCurTkMap->InsertNotFound(mrImp, bDuplicate, mrEmit, &pTokenRec) ); + } + } + + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeMemberRefs() + + +//***************************************************************************** +// merge interface impl +//***************************************************************************** +HRESULT NEWMERGER::MergeInterfaceImpls( ) +{ + HRESULT hr = NOERROR; + InterfaceImplRec *pRecImport = NULL; + InterfaceImplRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdTypeDef tkParent; + mdInterfaceImpl iiEmit; + bool bDuplicate; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountInterfaceImpls(); + + // loop through all InterfaceImpl + for (i = 1; i <= iCount; i++) + { + // only merge those InterfaceImpls that are marked + if ( pMiniMdImport->GetFilterTable()->IsInterfaceImplMarked(TokenFromRid(i, mdtInterfaceImpl)) == false) + continue; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetInterfaceImplRecord(i, &pRecImport)); + tkParent = pMiniMdImport->getClassOfInterfaceImpl(pRecImport); + + // does this TypeRef already exist in the emit scope? + if ( pCurTkMap->Find(tkParent, &pTokenRec) ) + { + if ( pTokenRec->m_isDuplicate ) + { + // parent in the emit scope + mdToken tkParentEmit; + mdToken tkInterface; + + // remap the typedef token + tkParentEmit = pTokenRec->m_tkTo; + + // remap the implemented interface token + tkInterface = pMiniMdImport->getInterfaceOfInterfaceImpl(pRecImport); + IfFailGo( pCurTkMap->Remap( tkInterface, &tkInterface) ); + + // Set duplicate flag + bDuplicate = true; + + // find the corresponding interfaceimpl in the emit scope + if ( ImportHelper::FindInterfaceImpl(pMiniMdEmit, tkParentEmit, tkInterface, &iiEmit) != S_OK ) + { + // bad state!! We have a duplicate typedef but the interface impl is not the same!! + + // continuable error + CheckContinuableErrorEx( + META_E_INTFCEIMPL_NOT_FOUND, + pImportData, + TokenFromRid(i, mdtInterfaceImpl)); + + iiEmit = mdTokenNil; + } + } + else + { + // No, it doesn't. Copy it over. + bDuplicate = false; + IfFailGo(pMiniMdEmit->AddInterfaceImplRecord(&pRecEmit, (RID *)&iiEmit)); + + // copy the interfaceimp record over + IfFailGo( CopyInterfaceImpl( pRecEmit, pImportData, pRecImport) ); + } + } + else + { + _ASSERTE( !"bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + + // record the token movement + IfFailGo( pCurTkMap->InsertNotFound( + TokenFromRid(i, mdtInterfaceImpl), + bDuplicate, + TokenFromRid( iiEmit, mdtInterfaceImpl ), + &pTokenRec) ); + } + } + + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeInterfaceImpls() + + +//***************************************************************************** +// merge all of the constant for field, property, and parameter +//***************************************************************************** +HRESULT NEWMERGER::MergeConstants() +{ + HRESULT hr = NOERROR; + ConstantRec *pRecImport = NULL; + ConstantRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + ULONG csEmit; // constant value is not a token + mdToken tkParentImp; + TOKENREC *pTokenRec; + void const *pValue; + ULONG cbBlob; +#if _DEBUG + ULONG typeParent; +#endif // _DEBUG + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountConstants(); + + // loop through all Constants + for (i = 1; i <= iCount; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetConstantRecord(i, &pRecImport)); + tkParentImp = pMiniMdImport->getParentOfConstant(pRecImport); + + // only move those constant over if their parents are marked + // If MDTOKENMAP::Find returns false, we don't need to copy the constant value over + if ( pCurTkMap->Find(tkParentImp, &pTokenRec) ) + { + // If the parent is duplicated, no need to move over the constant value + if ( !pTokenRec->m_isDuplicate ) + { + IfFailGo(pMiniMdEmit->AddConstantRecord(&pRecEmit, &csEmit)); + pRecEmit->SetType(pRecImport->GetType()); + + // set the parent + IfFailGo( pMiniMdEmit->PutToken(TBL_Constant, ConstantRec::COL_Parent, pRecEmit, pTokenRec->m_tkTo) ); + + // move over the constant blob value + IfFailGo(pMiniMdImport->getValueOfConstant(pRecImport, (const BYTE **)&pValue, &cbBlob)); + IfFailGo( pMiniMdEmit->PutBlob(TBL_Constant, ConstantRec::COL_Value, pRecEmit, pValue, cbBlob) ); + IfFailGo( pMiniMdEmit->AddConstantToHash(csEmit) ); + } + else + { + // <TODO>@FUTURE: more verification on the duplicate??</TODO> + } + } +#if _DEBUG + // Include this block only under Debug build. The reason is that + // the linker chooses all the errors that we report (such as unmatched MethodDef or FieldDef) + // as a continuable error. It is likely to hit this else while the tkparentImp is marked if there + // is any error reported earlier!! + else + { + typeParent = TypeFromToken(tkParentImp); + if (typeParent == mdtFieldDef) + { + // FieldDef should not be marked. + if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(tkParentImp) == false) + continue; + } + else if (typeParent == mdtParamDef) + { + // ParamDef should not be marked. + if ( pMiniMdImport->GetFilterTable()->IsParamMarked(tkParentImp) == false) + continue; + } + else + { + _ASSERTE(typeParent == mdtProperty); + // Property should not be marked. + if ( pMiniMdImport->GetFilterTable()->IsPropertyMarked(tkParentImp) == false) + continue; + } + + // If we come to here, we have a constant whose parent is marked but we could not + // find it in the map!! Bad state. + + _ASSERTE(!"Ignore this error if you have seen error reported earlier! Otherwise bad token map or bad metadata!"); + } +#endif // _DEBUG + // Note that we don't need to record the token movement since constant is not a valid token kind. + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeConstants() + + +//***************************************************************************** +// Merge field marshal information +//***************************************************************************** +HRESULT NEWMERGER::MergeFieldMarshals() +{ + HRESULT hr = NOERROR; + FieldMarshalRec *pRecImport = NULL; + FieldMarshalRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + ULONG fmEmit; // FieldMarhsal is not a token + mdToken tkParentImp; + TOKENREC *pTokenRec; + void const *pValue; + ULONG cbBlob; +#if _DEBUG + ULONG typeParent; +#endif // _DEBUG + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountFieldMarshals(); + + // loop through all TypeRef + for (i = 1; i <= iCount; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetFieldMarshalRecord(i, &pRecImport)); + tkParentImp = pMiniMdImport->getParentOfFieldMarshal(pRecImport); + + // We want to merge only those field marshals that parents are marked. + // Find will return false if the parent is not marked + // + if ( pCurTkMap->Find(tkParentImp, &pTokenRec) ) + { + // If the parent is duplicated, no need to move over the constant value + if ( !pTokenRec->m_isDuplicate ) + { + IfFailGo(pMiniMdEmit->AddFieldMarshalRecord(&pRecEmit, &fmEmit)); + + // set the parent + IfFailGo( pMiniMdEmit->PutToken( + TBL_FieldMarshal, + FieldMarshalRec::COL_Parent, + pRecEmit, + pTokenRec->m_tkTo) ); + + // move over the constant blob value + IfFailGo(pMiniMdImport->getNativeTypeOfFieldMarshal(pRecImport, (const BYTE **)&pValue, &cbBlob)); + IfFailGo( pMiniMdEmit->PutBlob(TBL_FieldMarshal, FieldMarshalRec::COL_NativeType, pRecEmit, pValue, cbBlob) ); + IfFailGo( pMiniMdEmit->AddFieldMarshalToHash(fmEmit) ); + + } + else + { + // <TODO>@FUTURE: more verification on the duplicate??</TODO> + } + } +#if _DEBUG + else + { + typeParent = TypeFromToken(tkParentImp); + + if (typeParent == mdtFieldDef) + { + // FieldDefs should not be marked + if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(tkParentImp) == false) + continue; + } + else + { + _ASSERTE(typeParent == mdtParamDef); + // ParamDefs should not be marked + if ( pMiniMdImport->GetFilterTable()->IsParamMarked(tkParentImp) == false) + continue; + } + + // If we come to here, that is we have a FieldMarshal whose parent is marked and we don't find it + // in the map!!! + + // either bad lookup map or bad metadata + _ASSERTE(!"Ignore this assert if you have seen error reported earlier. Otherwise, it is bad state!"); + } +#endif // _DEBUG + } + // Note that we don't need to record the token movement since FieldMarshal is not a valid token kind. + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeFieldMarshals() + + +//***************************************************************************** +// Merge class layout information +//***************************************************************************** +HRESULT NEWMERGER::MergeClassLayouts() +{ + HRESULT hr = NOERROR; + ClassLayoutRec *pRecImport = NULL; + ClassLayoutRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + ULONG iRecord; // class layout is not a token + mdToken tkParentImp; + TOKENREC *pTokenRec; + RID ridClassLayout; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountClassLayouts(); + + // loop through all TypeRef + for (i = 1; i <= iCount; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetClassLayoutRecord(i, &pRecImport)); + tkParentImp = pMiniMdImport->getParentOfClassLayout(pRecImport); + + // only merge those TypeDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsTypeDefMarked(tkParentImp) == false) + continue; + + if ( pCurTkMap->Find(tkParentImp, &pTokenRec) ) + { + if ( !pTokenRec->m_isDuplicate ) + { + // If the parent is not duplicated, just copy over the classlayout information + IfFailGo(pMiniMdEmit->AddClassLayoutRecord(&pRecEmit, &iRecord)); + + // copy over the fix part information + pRecEmit->Copy(pRecImport); + IfFailGo( pMiniMdEmit->PutToken(TBL_ClassLayout, ClassLayoutRec::COL_Parent, pRecEmit, pTokenRec->m_tkTo)); + IfFailGo( pMiniMdEmit->AddClassLayoutToHash(iRecord) ); + } + else + { + + IfFailGo(pMiniMdEmit->FindClassLayoutHelper(pTokenRec->m_tkTo, &ridClassLayout)); + + if (InvalidRid(ridClassLayout)) + { + // class is duplicated but not class layout info + CheckContinuableErrorEx(META_E_CLASS_LAYOUT_INCONSISTENT, pImportData, tkParentImp); + } + else + { + IfFailGo(pMiniMdEmit->GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRecEmit)); + if (pMiniMdImport->getPackingSizeOfClassLayout(pRecImport) != pMiniMdEmit->getPackingSizeOfClassLayout(pRecEmit) || + pMiniMdImport->getClassSizeOfClassLayout(pRecImport) != pMiniMdEmit->getClassSizeOfClassLayout(pRecEmit) ) + { + CheckContinuableErrorEx(META_E_CLASS_LAYOUT_INCONSISTENT, pImportData, tkParentImp); + } + } + } + } + else + { + // bad lookup map + _ASSERTE( !"bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + // no need to record the index movement. Classlayout is not a token. + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeClassLayouts() + +//***************************************************************************** +// Merge field layout information +//***************************************************************************** +HRESULT NEWMERGER::MergeFieldLayouts() +{ + HRESULT hr = NOERROR; + FieldLayoutRec *pRecImport = NULL; + FieldLayoutRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + ULONG iRecord; // field layout2 is not a token. + mdToken tkFieldImp; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountFieldLayouts(); + + // loop through all FieldLayout records. + for (i = 1; i <= iCount; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetFieldLayoutRecord(i, &pRecImport)); + tkFieldImp = pMiniMdImport->getFieldOfFieldLayout(pRecImport); + + // only merge those FieldDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(tkFieldImp) == false) + continue; + + if ( pCurTkMap->Find(tkFieldImp, &pTokenRec) ) + { + if ( !pTokenRec->m_isDuplicate ) + { + // If the Field is not duplicated, just copy over the FieldLayout information + IfFailGo(pMiniMdEmit->AddFieldLayoutRecord(&pRecEmit, &iRecord)); + + // copy over the fix part information + pRecEmit->Copy(pRecImport); + IfFailGo( pMiniMdEmit->PutToken(TBL_FieldLayout, FieldLayoutRec::COL_Field, pRecEmit, pTokenRec->m_tkTo)); + IfFailGo( pMiniMdEmit->AddFieldLayoutToHash(iRecord) ); + } + else + { + // <TODO>@FUTURE: more verification??</TODO> + } + } + else + { + // bad lookup map + _ASSERTE( !"bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + // no need to record the index movement. fieldlayout2 is not a token. + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeFieldLayouts() + + +//***************************************************************************** +// Merge field RVAs +//***************************************************************************** +HRESULT NEWMERGER::MergeFieldRVAs() +{ + HRESULT hr = NOERROR; + FieldRVARec *pRecImport = NULL; + FieldRVARec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + ULONG iRecord; // FieldRVA is not a token. + mdToken tkFieldImp; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountFieldRVAs(); + + // loop through all FieldRVA records. + for (i = 1; i <= iCount; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetFieldRVARecord(i, &pRecImport)); + tkFieldImp = pMiniMdImport->getFieldOfFieldRVA(pRecImport); + + // only merge those FieldDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsFieldMarked(TokenFromRid(tkFieldImp, mdtFieldDef)) == false) + continue; + + if ( pCurTkMap->Find(tkFieldImp, &pTokenRec) ) + { + if ( !pTokenRec->m_isDuplicate ) + { + // If the Field is not duplicated, just copy over the FieldRVA information + IfFailGo(pMiniMdEmit->AddFieldRVARecord(&pRecEmit, &iRecord)); + + // copy over the fix part information + pRecEmit->Copy(pRecImport); + IfFailGo( pMiniMdEmit->PutToken(TBL_FieldRVA, FieldRVARec::COL_Field, pRecEmit, pTokenRec->m_tkTo)); + IfFailGo( pMiniMdEmit->AddFieldRVAToHash(iRecord) ); + } + else + { + // <TODO>@FUTURE: more verification??</TODO> + } + } + else + { + // bad lookup map + _ASSERTE( !"bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + // no need to record the index movement. FieldRVA is not a token. + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeFieldRVAs() + + +//***************************************************************************** +// Merge MethodImpl information +//***************************************************************************** +HRESULT NEWMERGER::MergeMethodImpls() +{ + HRESULT hr = NOERROR; + MethodImplRec *pRecImport = NULL; + MethodImplRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + RID iRecord; + mdTypeDef tkClassImp; + mdToken tkBodyImp; + mdToken tkDeclImp; + TOKENREC *pTokenRecClass; + mdToken tkBodyEmit; + mdToken tkDeclEmit; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountMethodImpls(); + + // loop through all the MethodImpls. + for (i = 1; i <= iCount; i++) + { + // only merge those MethodImpls that are marked. + if ( pMiniMdImport->GetFilterTable()->IsMethodImplMarked(i) == false) + continue; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetMethodImplRecord(i, &pRecImport)); + tkClassImp = pMiniMdImport->getClassOfMethodImpl(pRecImport); + tkBodyImp = pMiniMdImport->getMethodBodyOfMethodImpl(pRecImport); + tkDeclImp = pMiniMdImport->getMethodDeclarationOfMethodImpl(pRecImport); + + if ( pCurTkMap->Find(tkClassImp, &pTokenRecClass)) + { + // If the TypeDef is duplicated, no need to move over the MethodImpl record. + if ( !pTokenRecClass->m_isDuplicate ) + { + // Create a new record and set the data. + + // <TODO>@FUTURE: We might want to consider changing the error for the remap into a continuable error. + // Because we probably can continue merging for more data...</TODO> + + IfFailGo( pCurTkMap->Remap(tkBodyImp, &tkBodyEmit) ); + IfFailGo( pCurTkMap->Remap(tkDeclImp, &tkDeclEmit) ); + IfFailGo(pMiniMdEmit->AddMethodImplRecord(&pRecEmit, &iRecord)); + IfFailGo( pMiniMdEmit->PutToken(TBL_MethodImpl, MethodImplRec::COL_Class, pRecEmit, pTokenRecClass->m_tkTo) ); + IfFailGo( pMiniMdEmit->PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodBody, pRecEmit, tkBodyEmit) ); + IfFailGo( pMiniMdEmit->PutToken(TBL_MethodImpl, MethodImplRec::COL_MethodDeclaration, pRecEmit, tkDeclEmit) ); + IfFailGo( pMiniMdEmit->AddMethodImplToHash(iRecord) ); + } + else + { + // <TODO>@FUTURE: more verification on the duplicate??</TODO> + } + // No need to record the token movement, MethodImpl is not a token. + } + else + { + // either bad lookup map or bad metadata + _ASSERTE(!"bad state"); + IfFailGo( META_E_BADMETADATA ); + } + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeMethodImpls() + + +//***************************************************************************** +// Merge PInvoke +//***************************************************************************** +HRESULT NEWMERGER::MergePinvoke() +{ + HRESULT hr = NOERROR; + ImplMapRec *pRecImport = NULL; + ImplMapRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdModuleRef mrImp; + mdModuleRef mrEmit; + mdMethodDef mdImp; + RID mdImplMap; + TOKENREC *pTokenRecMR; + TOKENREC *pTokenRecMD; + + USHORT usMappingFlags; + LPCUTF8 szImportName; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountImplMaps(); + + // loop through all ImplMaps + for (i = 1; i <= iCount; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetImplMapRecord(i, &pRecImport)); + + // Get the MethodDef token in the new space. + mdImp = pMiniMdImport->getMemberForwardedOfImplMap(pRecImport); + + // only merge those MethodDefs that are marked + if ( pMiniMdImport->GetFilterTable()->IsMethodMarked(mdImp) == false) + continue; + + // Get the ModuleRef token in the new space. + mrImp = pMiniMdImport->getImportScopeOfImplMap(pRecImport); + + // map the token to the new scope + if (pCurTkMap->Find(mrImp, &pTokenRecMR) == false) + { + // This should never fire unless the module refs weren't merged + // before this code ran. + _ASSERTE(!"Parent ModuleRef not found in MERGER::MergePinvoke. Bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + + // If the ModuleRef has been remapped to the "module token", we need to undo that + // for the pinvokeimpl. A pinvoke can only have a ModuleRef for the ImportScope. + mrEmit = pTokenRecMR->m_tkTo; + if (mrEmit == MODULEDEFTOKEN) + { // Yes, the ModuleRef has been remapped to the module token. So, + // find the ModuleRef in the output scope; if it is not found, add + // it. + ModuleRefRec *pModRefImport; + LPCUTF8 szNameImp; + IfFailGo(pMiniMdImport->GetModuleRefRecord(RidFromToken(mrImp), &pModRefImport)); + IfFailGo(pMiniMdImport->getNameOfModuleRef(pModRefImport, &szNameImp)); + + // does this ModuleRef already exist in the emit scope? + hr = ImportHelper::FindModuleRef(pMiniMdEmit, + szNameImp, + &mrEmit); + + if (hr == CLDB_E_RECORD_NOTFOUND) + { // No, it doesn't. Copy it over. + ModuleRefRec *pModRefEmit; + IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pModRefEmit, (RID*)&mrEmit)); + mrEmit = TokenFromRid(mrEmit, mdtModuleRef); + + // Set ModuleRef Name. + IfFailGo( pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, pModRefEmit, szNameImp) ); + } + else + IfFailGo(hr); + } + + + if (pCurTkMap->Find(mdImp, &pTokenRecMD) == false) + { + // This should never fire unless the method defs weren't merged + // before this code ran. + _ASSERTE(!"Parent MethodDef not found in MERGER::MergePinvoke. Bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + + + // Get copy of rest of data. + usMappingFlags = pMiniMdImport->getMappingFlagsOfImplMap(pRecImport); + IfFailGo(pMiniMdImport->getImportNameOfImplMap(pRecImport, &szImportName)); + + // If the method associated with PInvokeMap is not duplicated, then don't bother to look up the + // duplicated PInvokeMap information. + if (pTokenRecMD->m_isDuplicate == true) + { + // Does the correct ImplMap entry exist in the emit scope? + IfFailGo(pMiniMdEmit->FindImplMapHelper(pTokenRecMD->m_tkTo, &mdImplMap)); + } + else + { + mdImplMap = mdTokenNil; + } + if (!InvalidRid(mdImplMap)) + { + // Verify that the rest of the data is identical, else it's an error. + IfFailGo(pMiniMdEmit->GetImplMapRecord(mdImplMap, &pRecEmit)); + _ASSERTE(pMiniMdEmit->getMemberForwardedOfImplMap(pRecEmit) == pTokenRecMD->m_tkTo); + LPCSTR szImplMapImportName; + IfFailGo(pMiniMdEmit->getImportNameOfImplMap(pRecEmit, &szImplMapImportName)); + if (pMiniMdEmit->getImportScopeOfImplMap(pRecEmit) != mrEmit || + pMiniMdEmit->getMappingFlagsOfImplMap(pRecEmit) != usMappingFlags || + strcmp(szImplMapImportName, szImportName)) + { + // Mismatched p-invoke entries are found. + _ASSERTE(!"Mismatched P-invoke entries during merge. Bad State!"); + IfFailGo(E_FAIL); + } + } + else + { + IfFailGo(pMiniMdEmit->AddImplMapRecord(&pRecEmit, &mdImplMap)); + + // Copy rest of data. + IfFailGo( pMiniMdEmit->PutToken(TBL_ImplMap, ImplMapRec::COL_MemberForwarded, pRecEmit, pTokenRecMD->m_tkTo) ); + IfFailGo( pMiniMdEmit->PutToken(TBL_ImplMap, ImplMapRec::COL_ImportScope, pRecEmit, mrEmit) ); + IfFailGo( pMiniMdEmit->PutString(TBL_ImplMap, ImplMapRec::COL_ImportName, pRecEmit, szImportName) ); + pRecEmit->SetMappingFlags(usMappingFlags); + IfFailGo( pMiniMdEmit->AddImplMapToHash(mdImplMap) ); + } + } + } + + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergePinvoke() + + +//***************************************************************************** +// Merge StandAloneSigs +//***************************************************************************** +HRESULT NEWMERGER::MergeStandAloneSigs() +{ + HRESULT hr = NOERROR; + StandAloneSigRec *pRecImport = NULL; + StandAloneSigRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + TOKENREC *pTokenRec; + mdSignature saImp; + mdSignature saEmit; + bool fDuplicate; + PCCOR_SIGNATURE pbSig; + ULONG cbSig; + ULONG cbEmit; + CQuickBytes qbSig; + PCOR_SIGNATURE rgSig; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountStandAloneSigs(); + + // loop through all Signatures + for (i = 1; i <= iCount; i++) + { + // only merge those Signatures that are marked + if ( pMiniMdImport->GetFilterTable()->IsSignatureMarked(TokenFromRid(i, mdtSignature)) == false) + continue; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetStandAloneSigRecord(i, &pRecImport)); + IfFailGo(pMiniMdImport->getSignatureOfStandAloneSig(pRecImport, &pbSig, &cbSig)); + + // This is a signature containing the return type after count of args + // convert rid contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Assembly import scope info. + pMiniMdImport, // The scope to merge into the emit scope. + pbSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + rgSig = ( PCOR_SIGNATURE ) qbSig.Ptr(); + + hr = ImportHelper::FindStandAloneSig( + pMiniMdEmit, + rgSig, + cbEmit, + &saEmit ); + if ( hr == S_OK ) + { + // find a duplicate + fDuplicate = true; + } + else + { + // copy over + fDuplicate = false; + IfFailGo(pMiniMdEmit->AddStandAloneSigRecord(&pRecEmit, (ULONG *)&saEmit)); + saEmit = TokenFromRid(saEmit, mdtSignature); + IfFailGo( pMiniMdEmit->PutBlob(TBL_StandAloneSig, StandAloneSigRec::COL_Signature, pRecEmit, rgSig, cbEmit)); + } + saImp = TokenFromRid(i, mdtSignature); + + // Record the token movement + IfFailGo( pCurTkMap->InsertNotFound(saImp, fDuplicate, saEmit, &pTokenRec) ); + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeStandAloneSigs() + +//***************************************************************************** +// Merge MethodSpecs +//***************************************************************************** +HRESULT NEWMERGER::MergeMethodSpecs() +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdToken tk; + ULONG iRecord; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + + // Loop through all MethodSpec + iCount = pMiniMdImport->getCountMethodSpecs(); + for (i=1; i<=iCount; ++i) + { + MethodSpecRec *pRecImport; + MethodSpecRec *pRecEmit; + TOKENREC *pTokenRecMethod; + TOKENREC *pTokenRecMethodNew; + PCCOR_SIGNATURE pvSig; + ULONG cbSig; + CQuickBytes qbSig; + ULONG cbEmit; + + // Only copy marked records. + if (!pMiniMdImport->GetFilterTable()->IsMethodSpecMarked(i)) + continue; + + IfFailGo(pMiniMdImport->GetMethodSpecRecord(i, &pRecImport)); + tk = pMiniMdImport->getMethodOfMethodSpec(pRecImport); + + // Map the token to the new scope. + if (pCurTkMap->Find(tk, &pTokenRecMethod) == false) + { + // This should never fire unless the TypeDefs/Refs weren't merged + // before this code runs. + _ASSERTE(!"MethodSpec method not found in MERGER::MergeGenericsInfo. Bad state!"); + IfFailGo( META_E_BADMETADATA ); + } + // Copy to output scope. + IfFailGo(pMiniMdEmit->AddMethodSpecRecord(&pRecEmit, &iRecord)); + IfFailGo( pMiniMdEmit->PutToken(TBL_MethodSpec, MethodSpecRec::COL_Method, pRecEmit, pTokenRecMethod->m_tkTo)); + + // Copy the signature, translating any embedded tokens. + IfFailGo(pMiniMdImport->getInstantiationOfMethodSpec(pRecImport, &pvSig, &cbSig)); + + // ...convert rid contained in signature to new scope + IfFailGo(ImportHelper::MergeUpdateTokenInSig( + NULL, // Assembly emit scope. + pMiniMdEmit, // The emit scope. + NULL, NULL, 0, // Import assembly scope information. + pMiniMdImport, // The scope to merge into the emit scope. + pvSig, // signature from the imported scope + pCurTkMap, // Internal token mapping structure. + &qbSig, // [OUT] translated signature + 0, // start from first byte of the signature + 0, // don't care how many bytes consumed + &cbEmit)); // number of bytes write to cbEmit + + // ...persist the converted signature + IfFailGo( pMiniMdEmit->PutBlob(TBL_MethodSpec, MethodSpecRec::COL_Instantiation, pRecEmit, qbSig.Ptr(), cbEmit) ); + + IfFailGo( pCurTkMap->InsertNotFound(TokenFromRid(i, mdtMethodSpec), false, + TokenFromRid(iRecord, mdtMethodSpec), &pTokenRecMethodNew) ); + } + } + + ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeMethodSpecs() + +//***************************************************************************** +// Merge DeclSecuritys +//***************************************************************************** +HRESULT NEWMERGER::MergeDeclSecuritys() +{ + HRESULT hr = NOERROR; + DeclSecurityRec *pRecImport = NULL; + DeclSecurityRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdToken tkParentImp; + TOKENREC *pTokenRec; + void const *pValue; + ULONG cbBlob; + mdPermission pmImp; + mdPermission pmEmit; + bool fDuplicate; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountDeclSecuritys(); + + // loop through all DeclSecurity + for (i = 1; i <= iCount; i++) + { + // only merge those DeclSecurities that are marked + if ( pMiniMdImport->GetFilterTable()->IsDeclSecurityMarked(TokenFromRid(i, mdtPermission)) == false) + continue; + + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetDeclSecurityRecord(i, &pRecImport)); + tkParentImp = pMiniMdImport->getParentOfDeclSecurity(pRecImport); + if ( pCurTkMap->Find(tkParentImp, &pTokenRec) ) + { + if ( !pTokenRec->m_isDuplicate ) + { + // If the parent is not duplicated, just copy over the custom value + goto CopyPermission; + } + else + { + // Try to see if the Permission is there in the emit scope or not. + // If not, move it over still + if ( ImportHelper::FindPermission( + pMiniMdEmit, + pTokenRec->m_tkTo, + pRecImport->GetAction(), + &pmEmit) == S_OK ) + { + // found a match + // <TODO>@FUTURE: more verification??</TODO> + fDuplicate = true; + } + else + { + // Parent is duplicated but the Permission is not. Still copy over the + // Permission. +CopyPermission: + fDuplicate = false; + IfFailGo(pMiniMdEmit->AddDeclSecurityRecord(&pRecEmit, (ULONG *)&pmEmit)); + pmEmit = TokenFromRid(pmEmit, mdtPermission); + + pRecEmit->Copy(pRecImport); + + // set the parent + IfFailGo( pMiniMdEmit->PutToken( + TBL_DeclSecurity, + DeclSecurityRec::COL_Parent, + pRecEmit, + pTokenRec->m_tkTo) ); + + // move over the CustomAttribute blob value + IfFailGo(pMiniMdImport->getPermissionSetOfDeclSecurity(pRecImport, (const BYTE **)&pValue, &cbBlob)); + IfFailGo(pMiniMdEmit->PutBlob( + TBL_DeclSecurity, + DeclSecurityRec::COL_PermissionSet, + pRecEmit, + pValue, + cbBlob)); + } + } + pmEmit = TokenFromRid(pmEmit, mdtPermission); + pmImp = TokenFromRid(i, mdtPermission); + + // Record the token movement + IfFailGo( pCurTkMap->InsertNotFound(pmImp, fDuplicate, pmEmit, &pTokenRec) ); + } + else + { + // bad lookup map + _ASSERTE(!"bad state"); + IfFailGo( META_E_BADMETADATA ); + } + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeDeclSecuritys() + + +//***************************************************************************** +// Merge Strings +//***************************************************************************** +HRESULT NEWMERGER::MergeStrings() +{ + HRESULT hr = NOERROR; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + + for (UINT32 nIndex = 0; ;) + { + MetaData::DataBlob userString; + UINT32 nNextIndex; + UINT32 nEmitIndex; + + hr = pMiniMdImport->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; + } + + if (pMiniMdImport->GetFilterTable()->IsUserStringMarked(TokenFromRid(nIndex, mdtString)) == false) + { + // Process next user string in the heap + nIndex = nNextIndex; + continue; + } + + IfFailGo(pMiniMdEmit->PutUserString( + userString, + &nEmitIndex)); + + IfFailGo(pCurTkMap->InsertNotFound( + TokenFromRid(nIndex, mdtString), + false, + TokenFromRid(nEmitIndex, mdtString), + &pTokenRec)); + + // Process next user string in the heap + nIndex = nNextIndex; + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeStrings() + +// Helper method to merge the module-level security critical attributes +// Strips all module-level security critical attribute [that won't be ultimately needed] +// Returns: +// FAILED(hr): Failure occurred retrieving metadata or parsing scopes +// S_OK: Attribute should be merged into final output scope +// S_FALSE: Attribute should be ignored/dropped from output scope +HRESULT NEWMERGER::MergeSecurityCriticalModuleLevelAttributes( + MergeImportData* pImportData, // import scope + mdToken tkParentImp, // parent token with attribute + TOKENREC* pTypeRec, // token record of attribute ctor + mdToken mrSecurityTreatAsSafeAttributeCtor, // 'generic' TAS attribute token + mdToken mrSecurityTransparentAttributeCtor, // 'generic' Transparent attribute token + mdToken mrSecurityCriticalExplicitAttributeCtor, // 'generic' Critical attribute token + mdToken mrSecurityCriticalEverythingAttributeCtor) +{ + HRESULT hr = S_OK; + + // if ANY assembly-level critical attributes were specified, then we'll output + // one assembly-level Critical(Explicit) attribute only + // AND if this scope has tags + if (ISSCS_Unknown != pImportData->m_isscsSecurityCriticalStatus) + { + _ASSERTE(ISSCS_Unknown != m_isscsSecurityCritical); + // drop only assembly-level attributes + TypeRefRec* pTypeRefRec; + // metadata emitter + CMiniMdRW* pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // if compiler is generating a module - then this will be a module token + LPCSTR szTypeRefName; + if (tkParentImp == MODULEDEFTOKEN || + // otherwise, if merging assemblies, we have a fake type ref called MODULE_CA_LOCATION + (TypeFromToken(tkParentImp) == mdtTypeRef && + (IsAttributeFromNamespace(pMiniMdImport, tkParentImp, + COR_COMPILERSERVICE_NAMESPACE, COR_MSCORLIB_NAME, + &pTypeRefRec) == S_OK) && + (pMiniMdImport->getNameOfTypeRef(pTypeRefRec, &szTypeRefName) == S_OK) && + (strcmp(MODULE_CA_TYPENAME, szTypeRefName) == 0))) + { + // drop the TAS attribute (unless all scopes have TAS) + if ( pTypeRec->m_tkTo == mrSecurityTreatAsSafeAttributeCtor ) + { + if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityTreatAsSafe) == + ISSCS_SecurityTreatAsSafe) + { + _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityTreatAsSafe) == + ISSCS_SecurityTreatAsSafe); + return S_OK; + } + return S_FALSE; + } + // drop the Transparent attribute (unless all scopes have Transparent) + else if (pTypeRec->m_tkTo == mrSecurityTransparentAttributeCtor) + { + if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityTransparent) == + ISSCS_SecurityTransparent) + { + _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityTransparent) == + ISSCS_SecurityTransparent); + return S_OK; + } + return S_FALSE; + } + else if (pTypeRec->m_tkTo == mrSecurityCriticalExplicitAttributeCtor) + { + // if NOT Critical Everything, then leave the Critical.Explicit attribute + // the Critical.Explicit attribute will be used as the final global attribute + if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityCriticalEverything) != + ISSCS_SecurityCriticalEverything) + { + _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityCriticalExplicit) == + ISSCS_SecurityCriticalExplicit); + return S_OK; + } + else + { + // drop this attribute + return S_FALSE; + } + } + else if (pTypeRec->m_tkTo == mrSecurityCriticalEverythingAttributeCtor) + { + // OPTIMIZATION: if all attributes are Critical.Everything, + // then leave the global Critical attribute + if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityCriticalEverything) == + ISSCS_SecurityCriticalEverything) + { + _ASSERTE((pImportData->m_isscsSecurityCriticalStatus & ISSCS_SecurityCriticalEverything) == + ISSCS_SecurityCriticalEverything); + return S_OK; + } + else + { + // drop this attribute + return S_FALSE; + } + } + } + } + + return hr; +} // NEWMERGER::MergeSecurityCriticalModuleLevelAttributes + +// HELPER: Retrieve the meta-data info related to SecurityCritical +HRESULT NEWMERGER::RetrieveStandardSecurityCriticalMetaData( + mdAssemblyRef& tkMscorlib, + mdTypeRef& securityEnum, + BYTE*& rgSigBytesSecurityCriticalEverythingCtor, + DWORD& dwSigEverythingSize, + BYTE*& rgSigBytesSecurityCriticalExplicitCtor, + DWORD& dwSigExplicitSize) +{ + HRESULT hr = S_OK; + + CMiniMdRW* emit = GetMiniMdEmit(); + + // get typeref for mscorlib + BYTE pbMscorlibToken[] = COR_MSCORLIB_TYPEREF; + BYTE* pCurr = rgSigBytesSecurityCriticalEverythingCtor; + + IfFailGo(ImportHelper::FindAssemblyRef(emit, + COR_MSCORLIB_NAME, + NULL, + pbMscorlibToken, + sizeof(pbMscorlibToken), + asm_rmj, + asm_rmm, + asm_rup, + asm_rpt, + 0, + &tkMscorlib)); + + IfFailGo(m_pRegMetaEmit->DefineTypeRefByName(tkMscorlib, + COR_SECURITYCRITICALSCOPE_ENUM_W, + &securityEnum)); + + // build the constructor sig that takes SecurityCriticalScope argument + if (rgSigBytesSecurityCriticalEverythingCtor) + { + *pCurr++ = IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS; + *pCurr++ = COR_SECURITYCRITICAL_CTOR_ARGCOUNT_SCOPE_EVERYTHING; // one argument to constructor + *pCurr++ = ELEMENT_TYPE_VOID; + *pCurr++ = ELEMENT_TYPE_VALUETYPE; + pCurr += CorSigCompressToken(securityEnum, pCurr); + dwSigEverythingSize = (DWORD)(pCurr - rgSigBytesSecurityCriticalEverythingCtor); + _ASSERTE(dwSigEverythingSize <= COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE); + } + + // if Explicit ctor is requested + if (rgSigBytesSecurityCriticalExplicitCtor) + { + // build the constructor sig that has NO arguments + pCurr = rgSigBytesSecurityCriticalExplicitCtor; + *pCurr++ = IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS; + *pCurr++ = COR_SECURITYCRITICAL_CTOR_ARGCOUNT_NO_SCOPE; // no arguments to constructor + *pCurr++ = ELEMENT_TYPE_VOID; + dwSigExplicitSize = (DWORD)(pCurr - rgSigBytesSecurityCriticalExplicitCtor); + _ASSERTE(dwSigExplicitSize <= COR_SECURITYCRITICAL_CTOR_NO_SCOPE_SIG_MAX_SIZE); + } + +ErrExit: + return hr; +} // NEWMERGER::RetrieveStandardSecurityCriticalMetaData + +//***************************************************************************** +// Merge CustomAttributes +//***************************************************************************** +HRESULT NEWMERGER::MergeCustomAttributes() +{ + + HRESULT hr = NOERROR; + CustomAttributeRec *pRecImport = NULL; + CustomAttributeRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + ULONG iCount; + ULONG i; + mdToken tkParentImp; // Token of attributed object (parent). + TOKENREC *pTokenRec; // Parent's remap. + mdToken tkType; // Token of attribute's type. + TOKENREC *pTypeRec; // Type's remap. + void const *pValue; // The actual value. + ULONG cbBlob; // Size of the value. + mdToken cvImp; + mdToken cvEmit; + bool fDuplicate; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + TypeRefRec *pTypeRefRec; + ULONG cTypeRefRecs; + mdToken mrSuppressMergeCheckAttributeCtor = mdTokenNil; + mdToken mrSecurityCriticalExplicitAttributeCtor = mdTokenNil; + mdToken mrSecurityCriticalEverythingAttributeCtor = mdTokenNil; + mdToken mrSecurityTransparentAttributeCtor = mdTokenNil; + mdToken mrSecurityTreatAsSafeAttributeCtor = mdTokenNil; + + pMiniMdEmit = GetMiniMdEmit(); + + // Find out the TypeRef referring to our library's System.CompilerServices.SuppressMergeCheckAttribute, + // System.Security.SecurityCriticalAttribute, System.Security.SecurityTransparentAttribute, and + // System.Security.SecurityTreatAsSafeAttibute + cTypeRefRecs = pMiniMdEmit->getCountTypeRefs(); + + { // retrieve global attribute TypeRefs + + mdAssemblyRef tkMscorlib = mdTokenNil; + mdTypeRef securityEnum = mdTokenNil; + + NewArrayHolder<BYTE> rgSigBytesSecurityCriticalEverythingCtor(new (nothrow)BYTE[COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE]); + BYTE* pSigBytesSecurityCriticalEverythingCtor = rgSigBytesSecurityCriticalEverythingCtor.GetValue(); + IfFailGo((pSigBytesSecurityCriticalEverythingCtor == NULL)?E_OUTOFMEMORY:S_OK); + DWORD dwSigEverythingSize = 0; + + NewArrayHolder<BYTE> rgSigBytesSecurityCriticalExplicitCtor(new (nothrow)BYTE[COR_SECURITYCRITICAL_CTOR_NO_SCOPE_SIG_MAX_SIZE]); + BYTE* pSigBytesSecurityCriticalExplicitCtor = rgSigBytesSecurityCriticalExplicitCtor.GetValue(); + IfFailGo((pSigBytesSecurityCriticalExplicitCtor == NULL)?E_OUTOFMEMORY:S_OK); + DWORD dwSigExplicitSize = 0; + + // retrieve security critical metadata info if necessary + if(ISSCS_Unknown != m_isscsSecurityCritical) + { + + hr = RetrieveStandardSecurityCriticalMetaData( + tkMscorlib, + securityEnum, + pSigBytesSecurityCriticalEverythingCtor, + dwSigEverythingSize, + pSigBytesSecurityCriticalExplicitCtor, + dwSigExplicitSize); + + } + + // Search for the TypeRef. + for (i = 1; i <= cTypeRefRecs; i++) + { + mdToken tkTmp = TokenFromRid(i,mdtTypeRef); + + if (IsAttributeFromNamespace(pMiniMdEmit, tkTmp, + COR_COMPILERSERVICE_NAMESPACE, COR_MSCORLIB_NAME, + &pTypeRefRec) == S_OK) + { + LPCSTR szNameOfTypeRef; + IfFailGo(pMiniMdEmit->getNameOfTypeRef(pTypeRefRec, &szNameOfTypeRef)); + if (strcmp(szNameOfTypeRef, COR_SUPPRESS_MERGE_CHECK_ATTRIBUTE) == 0) + { + hr = ImportHelper::FindMemberRef( + pMiniMdEmit, tkTmp, + COR_CTOR_METHOD_NAME, + NULL, 0, + &mrSuppressMergeCheckAttributeCtor); + if (S_OK == hr) continue; + } + } + else + // if we are merging security critical attributes, then look for transparent-related attributes + if ((ISSCS_Unknown != m_isscsSecurityCritical) && + (IsAttributeFromNamespace(pMiniMdEmit, tkTmp, + COR_SECURITYCRITICAL_ATTRIBUTE_NAMESPACE, COR_MSCORLIB_NAME, + &pTypeRefRec) == S_OK)) + { + LPCSTR szNameOfTypeRef; + IfFailGo(pMiniMdEmit->getNameOfTypeRef(pTypeRefRec, &szNameOfTypeRef)); + + // look for the SecurityCritical attribute + if (strcmp(szNameOfTypeRef, COR_SECURITYCRITICAL_ATTRIBUTE) == 0) + { + // since the SecurityCritical attribute can be either + // parameterless constructor or SecurityCriticalScope constructor, we + // look for both + hr = ImportHelper::FindMemberRef( + pMiniMdEmit, tkTmp, + COR_CTOR_METHOD_NAME, + rgSigBytesSecurityCriticalEverythingCtor.GetValue(), dwSigEverythingSize, + &mrSecurityCriticalEverythingAttributeCtor); + if (S_OK == hr) continue; + hr = ImportHelper::FindMemberRef( + pMiniMdEmit, tkTmp, + COR_CTOR_METHOD_NAME, + rgSigBytesSecurityCriticalExplicitCtor.GetValue(), dwSigExplicitSize, + &mrSecurityCriticalExplicitAttributeCtor); + if (S_OK == hr) continue; + } + else + // look for the SecurityTransparent attribute + if (strcmp(szNameOfTypeRef, COR_SECURITYTRANSPARENT_ATTRIBUTE) == 0) + { + hr = ImportHelper::FindMemberRef( + pMiniMdEmit, tkTmp, + COR_CTOR_METHOD_NAME, + NULL, 0, + &mrSecurityTransparentAttributeCtor); + if (S_OK == hr) continue; + } + else + // look for the SecurityTreatAsSafe attribute + if (strcmp(szNameOfTypeRef, COR_SECURITYTREATASSAFE_ATTRIBUTE) == 0) + { + hr = ImportHelper::FindMemberRef( + pMiniMdEmit, tkTmp, + COR_CTOR_METHOD_NAME, + NULL, 0, + &mrSecurityTreatAsSafeAttributeCtor); + if (S_OK == hr) continue; + } + } + hr = S_OK; // ignore failures since the attribute may not be used + } + } + + // Loop over every module scope + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // We know that the filter table is not null here. Tell PREFIX that we know it. + PREFIX_ASSUME( pMiniMdImport->GetFilterTable() != NULL ); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountCustomAttributes(); + + // loop through all CustomAttribute + for (i = 1; i <= iCount; i++) + { + // compare it with the emit scope + IfFailGo(pMiniMdImport->GetCustomAttributeRecord(i, &pRecImport)); + tkParentImp = pMiniMdImport->getParentOfCustomAttribute(pRecImport); + tkType = pMiniMdImport->getTypeOfCustomAttribute(pRecImport); + IfFailGo(pMiniMdImport->getValueOfCustomAttribute(pRecImport, (const BYTE **)&pValue, &cbBlob)); + + // only merge those CustomAttributes that are marked + if ( pMiniMdImport->GetFilterTable()->IsCustomAttributeMarked(TokenFromRid(i, mdtCustomAttribute)) == false) + continue; + + // Check the type of the CustomAttribute. If it is not marked, then we don't need to move over the CustomAttributes. + // This will only occur for compiler defined discardable CAs during linking. + // + if ( pMiniMdImport->GetFilterTable()->IsTokenMarked(tkType) == false) + continue; + + if ( pCurTkMap->Find(tkParentImp, &pTokenRec) ) + { + // If the From token type is different from the To token's type, we have optimized the ref to def. + // In this case, we are dropping the CA associated with the Ref tokens. + // + if (TypeFromToken(tkParentImp) == TypeFromToken(pTokenRec->m_tkTo)) + { + + // If tkParentImp is a MemberRef and it is also mapped to a MemberRef in the merged scope with a MethodDef + // parent, then it is a MemberRef optimized to a MethodDef. We are keeping the MemberRef because it is a + // vararg call. So we can drop CAs on this MemberRef. + if (TypeFromToken(tkParentImp) == mdtMemberRef) + { + MemberRefRec *pTempRec; + IfFailGo(pMiniMdEmit->GetMemberRefRecord(RidFromToken(pTokenRec->m_tkTo), &pTempRec)); + if (TypeFromToken(pMiniMdEmit->getClassOfMemberRef(pTempRec)) == mdtMethodDef) + continue; + } + + + if (! pCurTkMap->Find(tkType, &pTypeRec) ) + { + _ASSERTE(!"CustomAttribute Type not found in output scope"); + IfFailGo(META_E_BADMETADATA); + } + + // Determine if we need to copy or ignore security-critical-related attributes + hr = MergeSecurityCriticalModuleLevelAttributes( + pImportData, tkParentImp, pTypeRec, + mrSecurityTreatAsSafeAttributeCtor, mrSecurityTransparentAttributeCtor, + mrSecurityCriticalExplicitAttributeCtor, + mrSecurityCriticalEverythingAttributeCtor); + IfFailGo(hr); + // S_FALSE means skip attribute + if (hr == S_FALSE) continue; + // S_OK means consider copying attribute + + // if it's the SuppressMergeCheckAttribute, don't copy it + if ( pTypeRec->m_tkTo == mrSuppressMergeCheckAttributeCtor ) + { + continue; + } + + if ( pTokenRec->m_isDuplicate) + { + // Try to see if the custom value is there in the emit scope or not. + // If not, move it over still + hr = ImportHelper::FindCustomAttributeByToken( + pMiniMdEmit, + pTokenRec->m_tkTo, + pTypeRec->m_tkTo, + pValue, + cbBlob, + &cvEmit); + + if ( hr == S_OK ) + { + // found a match + // <TODO>@FUTURE: more verification??</TODO> + fDuplicate = true; + } + else + { + TypeRefRec *pAttributeTypeRefRec; + // We need to allow additive merge on TypeRef for CustomAttributes because compiler + // could build module but not assembly. They are hanging of Assembly level CAs on a bogus + // TypeRef. + // Also allow additive merge for CAs from CompilerServices and Microsoft.VisualC + if (tkParentImp == MODULEDEFTOKEN + || TypeFromToken(tkParentImp) == mdtTypeRef + || (IsAttributeFromNamespace(pMiniMdImport, tkType, + COR_COMPILERSERVICE_NAMESPACE, COR_MSCORLIB_NAME, + &pAttributeTypeRefRec) == S_OK) + || (IsAttributeFromNamespace(pMiniMdImport, tkType, + COR_MISCBITS_NAMESPACE, COR_MISCBITS_NAMESPACE, + &pAttributeTypeRefRec) == S_OK)) + { + // clear the error + hr = NOERROR; + + // custom value of module token! Copy over the custom value + goto CopyCustomAttribute; + } + + // another case to support additive merge if the CA on MehtodDef is + // HandleProcessCorruptedStateExceptionsAttribute + if ( TypeFromToken(tkParentImp) == mdtMethodDef && tkType == pImportData->m_tkHandleProcessCorruptedStateCtor) + { + // clear the error + hr = NOERROR; + + // custom value of module token! Copy over the custom value + goto CopyCustomAttribute; + } + CheckContinuableErrorEx(META_E_MD_INCONSISTENCY, pImportData, TokenFromRid(i, mdtCustomAttribute)); + } + } + else + { +CopyCustomAttribute: + if ((m_dwMergeFlags & DropMemberRefCAs) && TypeFromToken(pTokenRec->m_tkTo) == mdtMemberRef) + { + // CustomAttributes associated with MemberRef. If the parent of MemberRef is a MethodDef or FieldDef, drop + // the custom attribute. + MemberRefRec *pMemberRefRec; + IfFailGo(pMiniMdEmit->GetMemberRefRecord(RidFromToken(pTokenRec->m_tkTo), &pMemberRefRec)); + mdToken mrParent = pMiniMdEmit->getClassOfMemberRef(pMemberRefRec); + if (TypeFromToken(mrParent) == mdtMethodDef || TypeFromToken(mrParent) == mdtFieldDef) + { + // Don't bother to copy over + continue; + } + } + + // Parent is duplicated but the custom value is not. Still copy over the + // custom value. + fDuplicate = false; + IfFailGo(pMiniMdEmit->AddCustomAttributeRecord(&pRecEmit, (ULONG *)&cvEmit)); + cvEmit = TokenFromRid(cvEmit, mdtCustomAttribute); + + // set the parent + IfFailGo( pMiniMdEmit->PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Parent, pRecEmit, pTokenRec->m_tkTo) ); + // set the type + IfFailGo( pMiniMdEmit->PutToken(TBL_CustomAttribute, CustomAttributeRec::COL_Type, pRecEmit, pTypeRec->m_tkTo)); + + // move over the CustomAttribute blob value + IfFailGo(pMiniMdImport->getValueOfCustomAttribute(pRecImport, (const BYTE **)&pValue, &cbBlob)); + + IfFailGo( pMiniMdEmit->PutBlob(TBL_CustomAttribute, CustomAttributeRec::COL_Value, pRecEmit, pValue, cbBlob)); + IfFailGo( pMiniMdEmit->AddCustomAttributesToHash(cvEmit) ); + } + cvEmit = TokenFromRid(cvEmit, mdtCustomAttribute); + cvImp = TokenFromRid(i, mdtCustomAttribute); + + // Record the token movement + IfFailGo( pCurTkMap->InsertNotFound(cvImp, pTokenRec->m_isDuplicate, cvEmit, &pTokenRec) ); + } + } + else + { + + // either bad lookup map or bad metadata + _ASSERTE(!"Bad state"); + IfFailGo( META_E_BADMETADATA ); + } + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeCustomAttributes() + +//******************************************************************************* +// Helper to check if input scope has assembly-level security transparent awareness (either SecurityTransparent or SecurityCritical) +// SIDE EFFECT: pImportData->m_isscsSecurityCriticalStatus will be explicitly set [same value as return value] +// SecurityCritical.Explicit attribute injection occurs for all scopes that have tags (e.g. NOT ISSCS_Unknown) +// If the tagged scopes are all SecurityCritical.Everything, then the final attribute should be SecurityCritical.Everything +// anyway. Otherwise, at least one SecurityCritical.Explicit tag will be used/injected +//******************************************************************************* +InputScopeSecurityCriticalStatus NEWMERGER::CheckInputScopeIsCritical(MergeImportData* pImportData, HRESULT& hr) +{ + hr = S_OK; + + // the attribute should be in a known state no matter how we return from this function + // default to no attribute explicitly specified + pImportData->m_isscsSecurityCriticalStatus = ISSCS_Unknown; + + mdTypeRef fakeModuleTypeRef = mdTokenNil; + mdAssemblyRef tkMscorlib = mdTokenNil; + + // TODO: Should we remove the ability to disable merging critical attributes? + if (g_fRefShouldMergeCriticalChecked == FALSE) + { + // shouldn't require thread safety lock + g_fRefShouldMergeCritical = (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_MergeCriticalAttributes) != 0); + g_fRefShouldMergeCriticalChecked = TRUE; + } + + // return no merge needed, if the merge critical attribute setting is not enabled. + if (!g_fRefShouldMergeCritical) return ISSCS_Unknown; + + // get typeref for mscorlib + BYTE pbMscorlibToken[] = COR_MSCORLIB_TYPEREF; + + CMiniMdRW* pImportedMiniMd = &pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd; + + if (S_OK != ImportHelper::FindAssemblyRef(pImportedMiniMd, + COR_MSCORLIB_NAME, + NULL, + pbMscorlibToken, + sizeof(pbMscorlibToken), + asm_rmj, + asm_rmm, + asm_rup, + asm_rpt, + 0, + &tkMscorlib)) + { + // there isn't an mscorlib ref here... we can't have the security critical attribute + return ISSCS_Unknown; + } + + if (S_OK != ImportHelper::FindTypeRefByName(pImportedMiniMd, + tkMscorlib, + COR_COMPILERSERVICE_NAMESPACE, + MODULE_CA_TYPENAME, + &fakeModuleTypeRef)) + { + // for now let use the fake module ref as the assembly def + fakeModuleTypeRef = 0x000001; + } + + // Check the input scope for TreatAsSafe + if (S_OK == ImportHelper::GetCustomAttributeByName(pImportedMiniMd, + fakeModuleTypeRef, // This is the assembly def token + COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL, + NULL, + NULL)) + { + pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityTreatAsSafe; + } + + // Check the input scope for security transparency awareness + // For example, the assembly is marked SecurityTransparent, SecurityCritical(Explicit), or SecurityCritical(Everything) + + + const void *pbData = NULL; // [OUT] Put pointer to data here. + ULONG cbData = 0; // number of bytes in pbData + + // Check if the SecurityTransparent attribute is present + if (S_OK == ImportHelper::GetCustomAttributeByName(pImportedMiniMd, + fakeModuleTypeRef, // This is the assembly def token + COR_SECURITYTRANSPARENT_ATTRIBUTE_FULL, + NULL, + NULL)) + { + pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityTransparent; + } + else + // Check if the SecurityCritical attribute is present + if (S_OK == ImportHelper::GetCustomAttributeByName(pImportedMiniMd, + fakeModuleTypeRef, // This is the assembly def token + COR_SECURITYCRITICAL_ATTRIBUTE_FULL, + &pbData, + &cbData)) + { + // find out if critical everything or explicit + + // default to critical + pImportData->m_isscsSecurityCriticalStatus = ISSCS_SecurityCritical; + + BYTE rgSecurityCriticalEverythingCtorValue[] = COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EVERYTHING; + // if value is non-0 (i.e. 1), then mark as SecurityCritical everything, otherwise, explicit + if (NULL != pbData && cbData == 8 && + memcmp(rgSecurityCriticalEverythingCtorValue, pbData, cbData) == 0) + { + pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityCriticalEverything; + } + else + { + pImportData->m_isscsSecurityCriticalStatus |= ISSCS_SecurityCriticalExplicit; + } + } + + return pImportData->m_isscsSecurityCriticalStatus; +} // HRESULT NEWMERGER::CheckInputScopeIsCritical() + +//******************************************************************************* +// Helper to merge security critical annotations across assemblies and types +//******************************************************************************* +HRESULT NEWMERGER::MergeSecurityCriticalAttributes() +{ + // if no assembly-level critical attributes were specified, then none are needed, + // and no need to do special attribute merging + if (ISSCS_Unknown == m_isscsSecurityCritical) + { + return S_OK; + } + // or if the global-scope already has TAS/Critical.Everything, then ignore individual type fixes + else if ((ISSCS_SECURITYCRITICAL_LEGACY & m_isscsSecurityCriticalAllScopes) == ISSCS_SECURITYCRITICAL_LEGACY) + { + return S_OK; + } + + HRESULT hr = S_OK; + + CMiniMdRW* emit = GetMiniMdEmit(); + // The attribute we want to decorate all of the types with has not been defined. + mdMemberRef tkSecurityCriticalEverythingAttribute = mdTokenNil; + mdMemberRef tkSecurityTreatAsSafeAttribute = mdTokenNil; + mdMemberRef tkSecuritySafeCriticalAttribute = mdTokenNil; + + mdAssemblyRef tkMscorlib = mdTokenNil; + mdTypeRef fakeModuleTypeRef = mdTokenNil; + mdTypeRef securityEnum = mdTokenNil; + + DWORD dwSigSize; + BYTE* rgSigBytesSecurityCriticalExplicitCtor = 0; + DWORD dwSigSize_TEMP; + + BYTE rgSigBytesTreatAsSafeCtor[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0x00, ELEMENT_TYPE_VOID}; + + BYTE rgSecurityCriticalEverythingCtorValue[] = COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EVERYTHING; + + + mdTypeRef tkSecurityCriticalEverythingAttributeType = mdTokenNil; + mdTypeRef tkSecurityTreatAsSafeAttributeType = mdTokenNil; + + NewArrayHolder<BYTE> rgSigBytesSecurityCriticalEverythingCtor(new (nothrow)BYTE[COR_SECURITYCRITICAL_CTOR_SCOPE_SIG_MAX_SIZE]); + BYTE* pSigBytesSecurityCriticalEverythingCtor = rgSigBytesSecurityCriticalEverythingCtor.GetValue(); + IfFailGo((pSigBytesSecurityCriticalEverythingCtor == NULL)?E_OUTOFMEMORY:S_OK); + + IfFailGo(RetrieveStandardSecurityCriticalMetaData( + tkMscorlib, + securityEnum, + pSigBytesSecurityCriticalEverythingCtor, + dwSigSize, + rgSigBytesSecurityCriticalExplicitCtor, + dwSigSize_TEMP)); + + if (S_OK != ImportHelper::FindTypeRefByName(emit, + tkMscorlib, + COR_COMPILERSERVICE_NAMESPACE, + MODULE_CA_TYPENAME, + &fakeModuleTypeRef)) + { + // for now let use the fake module ref as the assembly def + fakeModuleTypeRef = 0x000001; + } + + IfFailGo(m_pRegMetaEmit->DefineTypeRefByName( + tkMscorlib, COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W, &tkSecurityCriticalEverythingAttributeType)); + + IfFailGo(m_pRegMetaEmit->DefineMemberRef(tkSecurityCriticalEverythingAttributeType, + COR_CONSTRUCTOR_METADATA_IDENTIFIER, + rgSigBytesSecurityCriticalEverythingCtor.GetValue(), + dwSigSize, + &tkSecurityCriticalEverythingAttribute)); + + IfFailGo(m_pRegMetaEmit->DefineTypeRefByName( + tkMscorlib, COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL_W, + &tkSecurityTreatAsSafeAttributeType)); + + IfFailGo(m_pRegMetaEmit->DefineMemberRef(tkSecurityTreatAsSafeAttributeType, + COR_CONSTRUCTOR_METADATA_IDENTIFIER, + rgSigBytesTreatAsSafeCtor, + sizeof(rgSigBytesTreatAsSafeCtor), + &tkSecurityTreatAsSafeAttribute)); + + + // place this block in a new scope so that we can safely goto past it + { + mdTypeRef tkSecuritySafeCriticalAttributeType = mdTokenNil; + if (FAILED (hr = m_pRegMetaEmit->DefineTypeRefByName(tkMscorlib, COR_SECURITYSAFECRITICAL_ATTRIBUTE_FULL_W, + &tkSecuritySafeCriticalAttributeType))) + { + _ASSERTE(!"Couldn't Emit a Typeref for SafeCritical attribute"); + return hr; + } + + BYTE rgSigBytesSafeCriticalCtor[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0x00, ELEMENT_TYPE_VOID}; + if (FAILED(hr = m_pRegMetaEmit->DefineMemberRef(tkSecuritySafeCriticalAttributeType, + W(".ctor"), + rgSigBytesSafeCriticalCtor, + sizeof(rgSigBytesSafeCriticalCtor), + &tkSecuritySafeCriticalAttribute))) + { + _ASSERTE(!"Couldn't Emit a MemberRef for SafeCritical attribute .ctor"); + return hr; + } + } + + + for (MergeImportData* pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // if the import is marked TAS, then we need to explicitly mark each type as TAS + // if the import is marked Crit/Everything, then we need to explicitly mark each type as Crit + // if the import is not marked at all, then we need to explicitly mark each type TAS/Crit + + // if the import is marked ONLY Crit/Explicit or ONLY Transparent then we can ignore it + if (ISSCS_SecurityTransparent == pImportData->m_isscsSecurityCriticalStatus || + ISSCS_SecurityCriticalExplicit == pImportData->m_isscsSecurityCriticalStatus) continue; + + // Run through the scopes that need to have their types decorated with this attribute + MDTOKENMAP*pCurTKMap = pImportData->m_pMDTokenMap; + BYTE rgTreatAsSafeCtorValue[] = COR_SECURITYTREATASSAFE_ATTRIBUTE_VALUE; + + BOOL fMarkEachTokenAsCritical = FALSE; + // if the import is unmarked or marked Crit/Everything, + // then we need to explicitly mark each token as Crit + // Unless the the global scope already has SecurityCritical.Everything + if (((ISSCS_SecurityCriticalEverything & m_isscsSecurityCriticalAllScopes) != ISSCS_SecurityCriticalEverything) && + (((ISSCS_SecurityCriticalEverything & pImportData->m_isscsSecurityCriticalStatus) == ISSCS_SecurityCriticalEverything ) || + + // OR this scope is NOT transparent or critical-explicit + (ISSCS_SecurityTransparent & pImportData->m_isscsSecurityCriticalStatus) == 0 || + (ISSCS_SecurityCritical & pImportData->m_isscsSecurityCriticalStatus) == 0 || + + // OR this scope is UNKNOWN + (ISSCS_Unknown == (ISSCS_SECURITYCRITICAL_FLAGS & pImportData->m_isscsSecurityCriticalStatus)))) + { + fMarkEachTokenAsCritical = TRUE; + } + + BOOL fMarkEachTokenAsSafe = FALSE; + // if the import is unmarked or marked TAS, + // then we need to explicitly mark each token as TAS + // Unless the the global scope already has SecurityTreatAsSafe + if (((ISSCS_SecurityTreatAsSafe & m_isscsSecurityCriticalAllScopes) != ISSCS_SecurityTreatAsSafe) && + ((ISSCS_SecurityTreatAsSafe & pImportData->m_isscsSecurityCriticalStatus) || + ISSCS_Unknown == (pImportData->m_isscsSecurityCriticalStatus & ISSCS_SECURITYCRITICAL_FLAGS))) + { + fMarkEachTokenAsSafe = TRUE; + } + + BYTE rgSafeCriticalCtorValue[] = {0x01, 0x00, 0x00 ,0x00}; + + for (int i = 0; i < pCurTKMap->Count(); i++) + { + TOKENREC* pRec = pCurTKMap->Get(i); + BOOL fInjectSecurityAttributes = FALSE; + + // skip empty records + if (pRec->IsEmpty()) continue; + + // If this scope contained a typeref that was resolved to a typedef, let's not mark it. We'll let the owner + // of the actual typedef decide if that type should be marked. + if ((TypeFromToken(pRec->m_tkFrom) == mdtTypeRef) && (TypeFromToken(pRec->m_tkTo) == mdtTypeDef)) + continue; + + // Same for method refs/method defs + if ((TypeFromToken(pRec->m_tkFrom) == mdtMemberRef) && (TypeFromToken(pRec->m_tkTo) == mdtMethodDef)) + continue; + + // check for typedefs, but don't put this on the global typedef + if ((TypeFromToken(pRec->m_tkTo) == mdtTypeDef) && (pRec->m_tkTo != TokenFromRid(1, mdtTypeDef))) + { + // by default we will inject + fInjectSecurityAttributes = TRUE; + // except for Enums + DWORD dwClassAttrs = 0; + mdTypeRef crExtends = mdTokenNil; + + if (FAILED(hr = m_pRegMetaEmit->GetTypeDefProps(pRec->m_tkTo, NULL, NULL, 0 , &dwClassAttrs, &crExtends))) + { + // TODO: should we fail ?? + } + + // check for Enum types + if (!IsNilToken(crExtends) && (TypeFromToken(crExtends)==mdtTypeRef)) + { + // get the namespace and the name for this token + CMiniMdRW *pMiniMd = GetMiniMdEmit(); + TypeRefRec *pTypeRefRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(crExtends), &pTypeRefRec)); + LPCSTR szNamespace; + LPCSTR szName; + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespace));; + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szName)); + // check for System.Enum + BOOL bIsEnum = (!strcmp(szNamespace,"System"))&&(!strcmp(szName,"Enum")); + if (bIsEnum) + { + fInjectSecurityAttributes = FALSE; + } + } + } + else // check for global method defs + if (TypeFromToken(pRec->m_tkTo) == mdtMethodDef) + { + int isGlobal = 0; + if (!FAILED(m_pRegMetaEmit->IsGlobal(pRec->m_tkTo, &isGlobal))) + { + // check for global methods + if (isGlobal != 0) + { + fInjectSecurityAttributes = TRUE; + } + } + } + + if (fInjectSecurityAttributes) + { + // check to see if the token already has a custom attribute + const void *pbData = NULL; // [OUT] Put pointer to data here. + ULONG cbData = 0; + + if (fMarkEachTokenAsCritical) + { + // Check if the Type already has SecurityCritical + BOOL fInjectSecurityCriticalEverything = TRUE; + if (S_OK == m_pRegMetaEmit->GetCustomAttributeByName(pRec->m_tkTo, COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W, &pbData, &cbData)) + { + // if value is non-0 (i.e. 1), then it is SecurityCritical.Everything - so do not inject another + fInjectSecurityCriticalEverything = !(NULL != pbData && cbData == 8 && + memcmp(rgSecurityCriticalEverythingCtorValue, pbData, cbData) == 0); + } + + // either inject or overwrite SecurityCritical.Everything + if (fInjectSecurityCriticalEverything) + { + IfFailGo(m_pRegMetaEmit->DefineCustomAttribute( + pRec->m_tkTo, tkSecurityCriticalEverythingAttribute, + rgSecurityCriticalEverythingCtorValue, // Use this if you need specific custom attribute data (presence of the attribute isn't enough) + sizeof(rgSecurityCriticalEverythingCtorValue), // Length of your custom attribute data + NULL)); + } + } + + // If the Type does NOT already have TAS then add it + if (fMarkEachTokenAsSafe && + S_OK != m_pRegMetaEmit->GetCustomAttributeByName(pRec->m_tkTo, COR_SECURITYTREATASSAFE_ATTRIBUTE_FULL_W, &pbData, &cbData)) + { + IfFailGo(m_pRegMetaEmit->DefineCustomAttribute( + pRec->m_tkTo, tkSecurityTreatAsSafeAttribute, + rgTreatAsSafeCtorValue, // Use this if you need specific custom attribute data (presence of the attribute isn't enough) + sizeof(rgTreatAsSafeCtorValue), // Length of your custom attribute data + NULL)); + } + + hr = m_pRegMetaEmit->DefineCustomAttribute(pRec->m_tkTo, tkSecuritySafeCriticalAttribute, + rgSafeCriticalCtorValue, // Use this if you need specific custom attribute data (presence of the attribute isn't enough) + sizeof(rgSafeCriticalCtorValue), // Length of your custom attribute data + NULL); + + } + } + } + + // If the global scope is not Transparent, we should emit SecurityCritical.Explicit || Everything + if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityTransparent) != ISSCS_SecurityTransparent && + (m_isscsSecurityCritical & ISSCS_SecurityCriticalEverything) != ISSCS_SecurityCriticalExplicit) + { + BOOL fEmitSecurityEverything = FALSE; + // in the case of Unmarked and TAS/Unmarked, we need to emit the SecurityCritical.Everything attribute + // if it hasn't already been emitted + if ((m_isscsSecurityCriticalAllScopes & ISSCS_SecurityCriticalEverything) == + ISSCS_SecurityCriticalEverything) + { + fEmitSecurityEverything = TRUE; + } + // otherwise, emit the SecurityCritical.Explicit attribute + + BOOL fSecurityCriticalExists = FALSE; + // check to see if the assembly already has the appropriate SecurityCritical attribute + // [from one of the input scopes] + const void *pbData = NULL; + ULONG cbData = 0; + if (S_OK == ImportHelper::GetCustomAttributeByName(emit, + fakeModuleTypeRef, // This is the assembly def token + COR_SECURITYCRITICAL_ATTRIBUTE_FULL, + &pbData, + &cbData)) + { + // find out if critical everything or explicit + // default to critical + // if value is non-0 (i.e. 1), then mark as SecurityCritical everything, otherwise, explicit + if (NULL != pbData && cbData == 8 && + memcmp(rgSecurityCriticalEverythingCtorValue, pbData, cbData) == 0) + { + if (!fEmitSecurityEverything) + { + _ASSERTE(!"Unexpected SecurityCritical.Everything attribute detected"); + IfFailGo(META_E_BADMETADATA); + } + } + else + { + if (fEmitSecurityEverything) + { + _ASSERTE(!"Unexpected SecurityCritical.Explicit attribute detected"); + IfFailGo(META_E_BADMETADATA); + } + } + fSecurityCriticalExists = TRUE; + } + + if (!fSecurityCriticalExists) + { + // retrieve the type and CustomAttribute + mdCustomAttribute tkSecurityCriticalAttributeExplicit; + + mdTypeRef tkSecurityCriticalExplicitAttributeType = mdTokenNil; + + IfFailGo(m_pRegMetaEmit->DefineTypeRefByName( + tkMscorlib, COR_SECURITYCRITICAL_ATTRIBUTE_FULL_W, + &tkSecurityCriticalExplicitAttributeType)); + + BYTE rgSigBytesSecurityCriticalExplicitCtorLocal[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0x00, ELEMENT_TYPE_VOID}; + BYTE rgSecurityCriticalExplicitCtorValue[] = COR_SECURITYCRITICAL_ATTRIBUTE_VALUE_EXPLICIT; + + IfFailGo(m_pRegMetaEmit->DefineMemberRef(tkSecurityCriticalExplicitAttributeType, + COR_CONSTRUCTOR_METADATA_IDENTIFIER, + rgSigBytesSecurityCriticalExplicitCtorLocal, + sizeof(rgSigBytesSecurityCriticalExplicitCtorLocal), + &tkSecurityCriticalAttributeExplicit)); + + IfFailGo(m_pRegMetaEmit->DefineCustomAttribute( + fakeModuleTypeRef, + fEmitSecurityEverything?tkSecurityCriticalEverythingAttribute:tkSecurityCriticalAttributeExplicit, + fEmitSecurityEverything?rgSecurityCriticalEverythingCtorValue:rgSecurityCriticalExplicitCtorValue, + fEmitSecurityEverything?sizeof(rgSecurityCriticalEverythingCtorValue):sizeof(rgSecurityCriticalExplicitCtorValue), + NULL)); + + } + } + +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeSecurityCriticalAttributes() + +//******************************************************************************* +// Helper to copy an InterfaceImpl record +//******************************************************************************* +HRESULT NEWMERGER::CopyInterfaceImpl( + InterfaceImplRec *pRecEmit, // [IN] the emit record to fill + MergeImportData *pImportData, // [IN] the importing context + InterfaceImplRec *pRecImp) // [IN] the record to import +{ + HRESULT hr; + mdToken tkParent; + mdToken tkInterface; + CMiniMdRW *pMiniMdEmit = GetMiniMdEmit(); + CMiniMdRW *pMiniMdImp; + MDTOKENMAP *pCurTkMap; + + pMiniMdImp = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + + tkParent = pMiniMdImp->getClassOfInterfaceImpl(pRecImp); + tkInterface = pMiniMdImp->getInterfaceOfInterfaceImpl(pRecImp); + + IfFailGo( pCurTkMap->Remap(tkParent, &tkParent) ); + IfFailGo( pCurTkMap->Remap(tkInterface, &tkInterface) ); + + IfFailGo( pMiniMdEmit->PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Class, pRecEmit, tkParent) ); + IfFailGo( pMiniMdEmit->PutToken( TBL_InterfaceImpl, InterfaceImplRec::COL_Interface, pRecEmit, tkInterface) ); + +ErrExit: + return hr; +} // HRESULT NEWMERGER::CopyInterfaceImpl() + + +//***************************************************************************** +// Merge Assembly table +//***************************************************************************** +HRESULT NEWMERGER::MergeAssembly() +{ + HRESULT hr = NOERROR; + AssemblyRec *pRecImport = NULL; + AssemblyRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + LPCUTF8 szTmp; + const BYTE *pbTmp; + ULONG cbTmp; + ULONG iRecord; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + if (!pMiniMdImport->getCountAssemblys()) + goto ErrExit; // There is no Assembly in the import scope to merge. + + // Copy the Assembly map record to the Emit scope and send a token remap notifcation + // to the client. No duplicate checking needed since the Assembly can be present in + // only one scope and there can be atmost one entry. + IfFailGo(pMiniMdImport->GetAssemblyRecord(1, &pRecImport)); + IfFailGo(pMiniMdEmit->AddAssemblyRecord(&pRecEmit, &iRecord)); + + pRecEmit->Copy(pRecImport); + + IfFailGo(pMiniMdImport->getPublicKeyOfAssembly(pRecImport, &pbTmp, &cbTmp)); + IfFailGo(pMiniMdEmit->PutBlob(TBL_Assembly, AssemblyRec::COL_PublicKey, pRecEmit, + pbTmp, cbTmp)); + + IfFailGo(pMiniMdImport->getNameOfAssembly(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_Assembly, AssemblyRec::COL_Name, pRecEmit, szTmp)); + + IfFailGo(pMiniMdImport->getLocaleOfAssembly(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_Assembly, AssemblyRec::COL_Locale, pRecEmit, szTmp)); + + // record the token movement. + IfFailGo(pCurTkMap->InsertNotFound( + TokenFromRid(1, mdtAssembly), + false, + TokenFromRid(iRecord, mdtAssembly), + &pTokenRec)); + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeAssembly() + + + + +//***************************************************************************** +// Merge File table +//***************************************************************************** +HRESULT NEWMERGER::MergeFiles() +{ + HRESULT hr = NOERROR; + FileRec *pRecImport = NULL; + FileRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + LPCUTF8 szTmp; + const void *pbTmp; + ULONG cbTmp; + ULONG iCount; + ULONG i; + ULONG iRecord; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountFiles(); + + // Loop through all File records and copy them to the Emit scope. + // Since there can only be one File table in all the scopes combined, + // there isn't any duplicate checking that needs to be done. + for (i = 1; i <= iCount; i++) + { + IfFailGo(pMiniMdImport->GetFileRecord(i, &pRecImport)); + IfFailGo(pMiniMdEmit->AddFileRecord(&pRecEmit, &iRecord)); + + pRecEmit->Copy(pRecImport); + + IfFailGo(pMiniMdImport->getNameOfFile(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_File, FileRec::COL_Name, pRecEmit, szTmp)); + + IfFailGo(pMiniMdImport->getHashValueOfFile(pRecImport, (const BYTE **)&pbTmp, &cbTmp)); + IfFailGo(pMiniMdEmit->PutBlob(TBL_File, FileRec::COL_HashValue, pRecEmit, pbTmp, cbTmp)); + + // record the token movement. + IfFailGo(pCurTkMap->InsertNotFound( + TokenFromRid(i, mdtFile), + false, + TokenFromRid(iRecord, mdtFile), + &pTokenRec)); + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeFiles() + + +//***************************************************************************** +// Merge ExportedType table +//***************************************************************************** +HRESULT NEWMERGER::MergeExportedTypes() +{ + HRESULT hr = NOERROR; + ExportedTypeRec *pRecImport = NULL; + ExportedTypeRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + LPCUTF8 szTmp; + mdToken tkTmp; + ULONG iCount; + ULONG i; + ULONG iRecord; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountExportedTypes(); + + // Loop through all ExportedType records and copy them to the Emit scope. + // Since there can only be one ExportedType table in all the scopes combined, + // there isn't any duplicate checking that needs to be done. + for (i = 1; i <= iCount; i++) + { + IfFailGo(pMiniMdImport->GetExportedTypeRecord(i, &pRecImport)); + IfFailGo(pMiniMdEmit->AddExportedTypeRecord(&pRecEmit, &iRecord)); + + pRecEmit->Copy(pRecImport); + + IfFailGo(pMiniMdImport->getTypeNameOfExportedType(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_ExportedType, ExportedTypeRec::COL_TypeName, pRecEmit, szTmp)); + + IfFailGo(pMiniMdImport->getTypeNamespaceOfExportedType(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_ExportedType, ExportedTypeRec::COL_TypeNamespace, pRecEmit, szTmp)); + + tkTmp = pMiniMdImport->getImplementationOfExportedType(pRecImport); + IfFailGo(pCurTkMap->Remap(tkTmp, &tkTmp)); + IfFailGo(pMiniMdEmit->PutToken(TBL_ExportedType, ExportedTypeRec::COL_Implementation, + pRecEmit, tkTmp)); + + + // record the token movement. + IfFailGo(pCurTkMap->InsertNotFound( + TokenFromRid(i, mdtExportedType), + false, + TokenFromRid(iRecord, mdtExportedType), + &pTokenRec)); + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeExportedTypes() + + +//***************************************************************************** +// Merge ManifestResource table +//***************************************************************************** +HRESULT NEWMERGER::MergeManifestResources() +{ + HRESULT hr = NOERROR; + ManifestResourceRec *pRecImport = NULL; + ManifestResourceRec *pRecEmit = NULL; + CMiniMdRW *pMiniMdImport; + CMiniMdRW *pMiniMdEmit; + LPCUTF8 szTmp; + mdToken tkTmp; + ULONG iCount; + ULONG i; + ULONG iRecord; + TOKENREC *pTokenRec; + + MergeImportData *pImportData; + MDTOKENMAP *pCurTkMap; + + pMiniMdEmit = GetMiniMdEmit(); + + for (pImportData = m_pImportDataList; pImportData != NULL; pImportData = pImportData->m_pNextImportData) + { + // for each import scope + pMiniMdImport = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + + // set the current MDTokenMap + pCurTkMap = pImportData->m_pMDTokenMap; + iCount = pMiniMdImport->getCountManifestResources(); + + // Loop through all ManifestResource records and copy them to the Emit scope. + // Since there can only be one ManifestResource table in all the scopes combined, + // there isn't any duplicate checking that needs to be done. + for (i = 1; i <= iCount; i++) + { + IfFailGo(pMiniMdImport->GetManifestResourceRecord(i, &pRecImport)); + IfFailGo(pMiniMdEmit->AddManifestResourceRecord(&pRecEmit, &iRecord)); + + pRecEmit->Copy(pRecImport); + + IfFailGo(pMiniMdImport->getNameOfManifestResource(pRecImport, &szTmp)); + IfFailGo(pMiniMdEmit->PutString(TBL_ManifestResource, ManifestResourceRec::COL_Name, + pRecEmit, szTmp)); + + tkTmp = pMiniMdImport->getImplementationOfManifestResource(pRecImport); + IfFailGo(pCurTkMap->Remap(tkTmp, &tkTmp)); + IfFailGo(pMiniMdEmit->PutToken(TBL_ManifestResource, ManifestResourceRec::COL_Implementation, + pRecEmit, tkTmp)); + + // record the token movement. + IfFailGo(pCurTkMap->InsertNotFound( + TokenFromRid(i, mdtManifestResource), + false, + TokenFromRid(iRecord, mdtManifestResource), + &pTokenRec)); + } + } +ErrExit: + return hr; +} // HRESULT NEWMERGER::MergeManifestResources() + + + + + +//***************************************************************************** +// Error handling. Call back to host to see what they want to do. +//***************************************************************************** +HRESULT NEWMERGER::OnError( + HRESULT hrIn, // The error HR we're reporting. + MergeImportData *pImportData, // The input scope with the error. + mdToken token) // The token with the error. +{ + // This function does a QI and a Release on every call. However, it should be + // called very infrequently, and lets the scope just keep a generic handler. + IMetaDataError *pIErr = NULL; + IUnknown *pHandler = pImportData->m_pHandler; + CMiniMdRW *pMiniMd = &(pImportData->m_pRegMetaImport->m_pStgdb->m_MiniMd); + CQuickArray<WCHAR> rName; // Name of the TypeDef in unicode. + LPCUTF8 szTypeName; + LPCUTF8 szNSName; + TypeDefRec *pTypeRec; + int iLen; // Length of a name. + mdToken tkParent; + HRESULT hr = NOERROR; + + if (pHandler && pHandler->QueryInterface(IID_IMetaDataError, (void**)&pIErr)==S_OK) + { + switch (hrIn) + { + + case META_E_PARAM_COUNTS: + case META_E_METHD_NOT_FOUND: + case META_E_METHDIMPL_INCONSISTENT: + { + LPCUTF8 szMethodName; + MethodRec *pMethodRec; + + // Method name. + _ASSERTE(TypeFromToken(token) == mdtMethodDef); + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(token), &pMethodRec)); + IfFailGo(pMiniMd->getNameOfMethod(pMethodRec, &szMethodName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMethodName, szMethodName); + IfNullGo(wzMethodName); + + // Type and its name. + IfFailGo( pMiniMd->FindParentOfMethodHelper(token, &tkParent) ); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + PostError(hrIn, (LPWSTR) rName.Ptr(), wzMethodName, token); + break; + } + case META_E_FIELD_NOT_FOUND: + { + LPCUTF8 szFieldName; + FieldRec *pFieldRec; + + // Field name. + _ASSERTE(TypeFromToken(token) == mdtFieldDef); + IfFailGo(pMiniMd->GetFieldRecord(RidFromToken(token), &pFieldRec)); + IfFailGo(pMiniMd->getNameOfField(pFieldRec, &szFieldName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzFieldName, szFieldName); + IfNullGo(wzFieldName); + + // Type and its name. + IfFailGo( pMiniMd->FindParentOfFieldHelper(token, &tkParent) ); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + PostError(hrIn, (LPWSTR) rName.Ptr(), wzFieldName, token); + break; + } + case META_E_EVENT_NOT_FOUND: + { + LPCUTF8 szEventName; + EventRec *pEventRec; + + // Event name. + _ASSERTE(TypeFromToken(token) == mdtEvent); + IfFailGo(pMiniMd->GetEventRecord(RidFromToken(token), &pEventRec)); + IfFailGo(pMiniMd->getNameOfEvent(pEventRec, &szEventName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzEventName, szEventName); + IfNullGo(wzEventName); + + // Type and its name. + IfFailGo( pMiniMd->FindParentOfEventHelper(token, &tkParent) ); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + PostError(hrIn, (LPWSTR) rName.Ptr(), wzEventName, token); + break; + } + case META_E_PROP_NOT_FOUND: + { + LPCUTF8 szPropertyName; + PropertyRec *pPropertyRec; + + // Property name. + _ASSERTE(TypeFromToken(token) == mdtProperty); + IfFailGo(pMiniMd->GetPropertyRecord(RidFromToken(token), &pPropertyRec)); + IfFailGo(pMiniMd->getNameOfProperty(pPropertyRec, &szPropertyName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzPropertyName, szPropertyName); + IfNullGo(wzPropertyName); + + // Type and its name. + IfFailGo( pMiniMd->FindParentOfPropertyHelper(token, &tkParent) ); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + PostError(hrIn, (LPWSTR) rName.Ptr(), wzPropertyName, token); + break; + } + case META_S_PARAM_MISMATCH: + { + LPCUTF8 szMethodName; + MethodRec *pMethodRec; + mdToken tkMethod; + + // Method name. + _ASSERTE(TypeFromToken(token) == mdtParamDef); + IfFailGo( pMiniMd->FindParentOfParamHelper(token, &tkMethod) ); + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkMethod), &pMethodRec)); + IfFailGo(pMiniMd->getNameOfMethod(pMethodRec, &szMethodName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMethodName, szMethodName); + IfNullGo(wzMethodName); + + // Type and its name. + IfFailGo( pMiniMd->FindParentOfMethodHelper(token, &tkParent) ); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + // use the error hresult so that we can post the correct error. + PostError(META_E_PARAM_MISMATCH, wzMethodName, (LPWSTR) rName.Ptr(), token); + break; + } + case META_E_INTFCEIMPL_NOT_FOUND: + { + InterfaceImplRec *pRec; // The InterfaceImpl + mdToken tkIface; // Token of the implemented interface. + CQuickArray<WCHAR> rIface; // Name of the Implemented Interface in unicode. + TypeRefRec *pRef; // TypeRef record when II is a typeref. + InterfaceImplRec *pInterfaceImplRec; + + // Get the record. + _ASSERTE(TypeFromToken(token) == mdtInterfaceImpl); + IfFailGo(pMiniMd->GetInterfaceImplRecord(RidFromToken(token), &pRec)); + // Get the name of the class. + tkParent = pMiniMd->getClassOfInterfaceImpl(pRec); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + // Get the name of the implemented interface. + IfFailGo(pMiniMd->GetInterfaceImplRecord(RidFromToken(token), &pInterfaceImplRec)); + tkIface = pMiniMd->getInterfaceOfInterfaceImpl(pInterfaceImplRec); + if (TypeFromToken(tkIface) == mdtTypeDef) + { // If it is a typedef... + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkIface), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + } + else + { // If it is a typeref... + _ASSERTE(TypeFromToken(tkIface) == mdtTypeRef); + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkIface), &pRef)); + IfFailGo(pMiniMd->getNameOfTypeRef(pRef, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pRef, &szNSName)); + } + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rIface.ReSizeNoThrow(iLen+1)); + ns::MakePath(rIface.Ptr(), iLen+1, szNSName, szTypeName); + + + PostError(hrIn, (LPWSTR) rName.Ptr(), (LPWSTR)rIface.Ptr(), token); + break; + } + case META_E_CLASS_LAYOUT_INCONSISTENT: + case META_E_METHOD_COUNTS: + case META_E_FIELD_COUNTS: + case META_E_EVENT_COUNTS: + case META_E_PROPERTY_COUNTS: + { + // get the type name. + _ASSERTE(TypeFromToken(token) == mdtTypeDef); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(token), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + PostError(hrIn, (LPWSTR) rName.Ptr(), token); + break; + } + case META_E_GENERICPARAM_INCONSISTENT: + { + // If token is type, get type name; if method, get method name. + LPWSTR wzName; + LPCUTF8 szMethodName; + MethodRec *pMethodRec; + + if ((TypeFromToken(token) == mdtMethodDef)) + { + // Get the method name. + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(token), &pMethodRec)); + IfFailGo(pMiniMd->getNameOfMethod(pMethodRec, &szMethodName)); + MAKE_WIDEPTR_FROMUTF8_NOTHROW(wzMethodName, szMethodName); + IfNullGo(wzMethodName); + wzName = wzMethodName; + } + else + { + // Get the type name. + _ASSERTE(TypeFromToken(token) == mdtTypeDef); + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(token), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTypeRec, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTypeRec, &szNSName)); + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + wzName = (LPWSTR)rName.Ptr(); + } + + PostError(hrIn, wzName, token); + break; + } + case META_E_TYPEDEF_MISSING: + { + TypeRefRec *pRef; // TypeRef record when II is a typeref. + + // Get the record. + _ASSERTE(TypeFromToken(token) == mdtTypeRef); + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(token), &pRef)); + IfFailGo(pMiniMd->getNameOfTypeRef(pRef, &szTypeName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pRef, &szNSName)); + + // Put namespace + name together. + iLen = ns::GetFullLength(szNSName, szTypeName); + IfFailGo(rName.ReSizeNoThrow(iLen+1)); + ns::MakePath(rName.Ptr(), iLen+1, szNSName, szTypeName); + + + PostError(hrIn, (LPWSTR) rName.Ptr(), token); + break; + } + default: + { + PostError(hrIn, token); + break; + } + } + hr = pIErr->OnError(hrIn, token); + } + else + hr = S_FALSE; +ErrExit: + if (pIErr) + pIErr->Release(); + return (hr); +} // NEWMERGER::OnError + +#endif //FEATURE_METADATA_EMIT_ALL diff --git a/src/md/compiler/newmerger.h b/src/md/compiler/newmerger.h new file mode 100644 index 0000000000..fc89ab7f61 --- /dev/null +++ b/src/md/compiler/newmerger.h @@ -0,0 +1,256 @@ +// 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. +//***************************************************************************** +// NewMerger.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __NEWMERGER__h__ +#define __NEWMERGER__h__ + +class RegMeta; + +class MDTOKENMAP; + +// module-level awareness of Security critical annotions +typedef BYTE InputScopeSecurityCriticalStatus; +#define ISSCS_Unknown 0x0 +#define ISSCS_SecurityCritical 0x1 +#define ISSCS_SecurityCriticalEverything (ISSCS_SecurityCritical | 0x2) +#define ISSCS_SecurityCriticalExplicit (ISSCS_SecurityCritical) +#define ISSCS_SecurityTransparent 0x4 +#define ISSCS_SecurityTreatAsSafe 0x8 +#define ISSCS_SECURITYCRITICAL_LEGACY (ISSCS_SecurityCriticalEverything | ISSCS_SecurityTreatAsSafe) +#define ISSCS_SECURITYCRITICAL_FLAGS (ISSCS_SecurityCriticalEverything | ISSCS_SecurityTransparent) + +//********************************************************************* +// MergeImportData +//********************************************************************* +class MergeImportData +{ +public: + RegMeta *m_pRegMetaImport; + IUnknown *m_pHandler; + IMapToken *m_pHostMapToken; + MDTOKENMAP *m_pMDTokenMap; + MergeImportData *m_pNextImportData; + + mdMemberRef m_tkSuppressMergeCheckCtor; // caches the SuppressMergeCheckAttribute's .ctor token + mdMemberRef m_tkHandleProcessCorruptedStateCtor; // caches the memberRef token to HandleProcessCorruptedStateExceptionsAttribute's .ctor token + + // import contains assembly-level SecurityTransparent or SecurityCritical + InputScopeSecurityCriticalStatus m_isscsSecurityCriticalStatus; +#if _DEBUG + int m_iImport; // debug only. This is the ith import for merge. +#endif // _DEBUG +}; + +//********************************************************************* +// MergeTypeData +//********************************************************************* +struct MergeTypeData +{ + ULONG m_cMethods; + ULONG m_cFields; + ULONG m_cEvents; + ULONG m_cProperties; + BOOL m_bSuppressMergeCheck; +}; + + +//********************************************************************* +// Class to handle merge +//********************************************************************* +class NEWMERGER +{ + friend class RegMeta; +public: + NEWMERGER(); + ~NEWMERGER(); + + HRESULT Init(RegMeta *pRegMetaDest); + + HRESULT AddImport( + IMetaDataImport2 *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. + + HRESULT Merge(MergeFlags flags, CorRefToDefCheck optimizeRefToDef); + +protected: + + CMiniMdRW *GetMiniMdEmit(); + + HRESULT InitMergeTypeData(); + + HRESULT MergeTypeDefNamesOnly(); + HRESULT MergeModuleRefs(); + HRESULT MergeAssemblyRefs(); + HRESULT MergeTypeRefs(); + HRESULT CompleteMergeTypeDefs(); + + HRESULT CopyTypeDefPartially( + TypeDefRec *pRecEmit, // [IN] the emit record to fill + CMiniMdRW *pMiniMdImport, // [IN] the importing scope + TypeDefRec *pRecImp); // [IN] the record to import + + // helpers for merging tables + HRESULT MergeModule( ); + HRESULT MergeTypeDefChildren(); + HRESULT MergeInterfaceImpls( ); + HRESULT MergeMemberRefs( ); + HRESULT MergePinvoke(); + + HRESULT MergeConstants( ); + HRESULT MergeCustomAttributes( ); + HRESULT MergeFieldMarshals( ); + HRESULT MergeDeclSecuritys( ); + HRESULT MergeClassLayouts( ); + HRESULT MergeFieldLayouts( ); + HRESULT MergeFieldRVAs(); + HRESULT MergeMethodImpls( ); + HRESULT MergeStandAloneSigs(); + HRESULT MergeMethodSpecs(); + HRESULT MergeTypeSpecs(); + HRESULT MergeSourceFiles( ); + HRESULT MergeBlocks( ); + HRESULT MergeScopes( ); + HRESULT MergeLocalVariables( ); + HRESULT MergeStrings( ); + + HRESULT MergeAssembly(); + HRESULT MergeFiles(); + HRESULT MergeExportedTypes(); + HRESULT MergeManifestResources(); + + // helpers for SecurityCritical-related merging + InputScopeSecurityCriticalStatus CheckInputScopeIsCritical(MergeImportData* pImportData, HRESULT& hr); + HRESULT RetrieveStandardSecurityCriticalMetaData( + mdAssemblyRef& tkMscorlib, + mdTypeRef& securityEnum, + BYTE*& rgSigBytesSecurityCriticalEverythingCtor, + DWORD& dwSigEverythingSize, + BYTE*& rgSigBytesSecurityCriticalExplicitCtor, + DWORD& dwSigExplicitSize); + + HRESULT MergeSecurityCriticalModuleLevelAttributes( + MergeImportData* pImportData, + mdToken tkParentImp, TOKENREC* pTypeRec, + mdToken mrSecurityTreatAsSafeAttributeCtor, + mdToken mrSecurityTransparentAttributeCtor, + mdToken mrSecurityCriticalExplicitAttributeCtor, + mdToken mrSecurityCriticalEverythingAttributeCtor); + HRESULT MergeSecurityCriticalAttributes(); + + // copy over a interfaceimpl record + HRESULT CopyInterfaceImpl( + InterfaceImplRec *pRecEmit, // [IN] the emit record to fill + MergeImportData *pImportData, // [IN] the importing context + InterfaceImplRec *pRecImp); // [IN] the record to import + + // verification helpers + HRESULT VerifyMethods(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT VerifyFields(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT VerifyEvents(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT VerifyProperties(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT VerifyParams(MergeImportData *pImportData, mdMethodDef mdImp, mdMethodDef mdEmit); + HRESULT VerifyGenericParams(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT VerifyGenericParamConstraints(MergeImportData *pImportData, mdGenericParam gpImp, mdGenericParam gpEmit); + + // Copy helpers + HRESULT CopyMethods(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT CopyFields(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT CopyEvents(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT CopyProperties(MergeImportData *pImportData, mdTypeDef tdImp, mdTypeDef tdEmit); + HRESULT CopyParams(MergeImportData *pImportData, mdMethodDef mdImp, mdMethodDef mdEmit); + HRESULT CopyGenericParams(MergeImportData *pImportData, mdToken tkImp, mdToken tkEmit); + HRESULT CopyGenericParamConstraints(MergeImportData *pImportData, mdGenericParam gpImp, mdGenericParam gpEmit); + + HRESULT CopyMethod( + MergeImportData *pImportData, // [IN] import scope + MethodRec *pRecImp, // [IN] the record to import + MethodRec *pRecEmit); // [IN] the emit record to fill + + HRESULT CopyField( + MergeImportData *pImportData, // [IN] import scope + FieldRec *pRecImp, // [IN] the record to import + FieldRec *pRecEmit); // [IN] the emit record to fill + + HRESULT CopyEvent( + MergeImportData *pImportData, // [IN] import scope + EventRec *pRecImp, // [IN] the record to import + EventRec *pRecEmit); // [IN] the emit record to fill + + HRESULT CopyProperty( + MergeImportData *pImportData, // [IN] import scope + PropertyRec *pRecImp, // [IN] the record to import + PropertyRec *pRecEmit); // [IN] the emit record to fill + + HRESULT CopyParam( + MergeImportData *pImportData, // [IN] import scope + ParamRec *pRecImp, // [IN] the record to import + ParamRec *pRecEmit); // [IN] the emit record to fill + + HRESULT CopyMethodSemantics( + MergeImportData *pImportData, + mdToken tkImport, // Event or property in the import scope + mdToken tkEmit); // corresponding event or property in the emitting scope + + HRESULT VerifyMethod( + MergeImportData *pImportData, + mdMethodDef mdImp, // [IN] the emit record to fill + mdMethodDef mdEmit); // [IN] the record to import + + HRESULT OnError(HRESULT hr, MergeImportData *pImportData, mdToken token); + +private: + RegMeta *m_pRegMetaEmit; + MergeImportData *m_pImportDataList; + MergeImportData **m_pImportDataTail; + MergeFlags m_dwMergeFlags; + BOOL m_fDupCheck; + CorRefToDefCheck m_optimizeRefToDef; + // the combined value of the Security Critical input scopes (e.g. UNION of each scope's attributes) + // if ANY of the scopes have a bit set, then we must do some merging + InputScopeSecurityCriticalStatus m_isscsSecurityCritical; + // the common values of the Security Critical input scopes (e.g. INTERSECTION of each scope's attributes) + // if all scopes have the same bit set, then we can emit one bit at the final output scope + InputScopeSecurityCriticalStatus m_isscsSecurityCriticalAllScopes; + + CDynArray<MergeTypeData> m_rMTDs; +#if _DEBUG + int m_iImport; // debug only. To count how many import scopes to be merged. +#endif // _DEBUG +}; + + +#define CheckContinuableErrorEx(EXPR, HANDLER, TOKEN) \ +{ \ + HRESULT hrOnErr, hrExpr; \ + hrExpr = EXPR; \ + \ + hrOnErr = OnError(hrExpr, HANDLER, TOKEN); \ + if (hrOnErr != S_OK) \ + { \ + if (hrOnErr == S_FALSE) \ + { \ + hr = hrExpr; \ + } \ + else if (SUCCEEDED(hrOnErr)) \ + { \ + hr = E_UNEXPECTED; \ + } \ + else if (FAILED(hrOnErr)) \ + { \ + hr = hrOnErr; \ + } \ + IfFailGo(hr); \ + } \ +} + + +#endif // __NEWMERGER__h__ 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 + diff --git a/src/md/compiler/regmeta.h b/src/md/compiler/regmeta.h new file mode 100644 index 0000000000..cb7bae17b5 --- /dev/null +++ b/src/md/compiler/regmeta.h @@ -0,0 +1,2123 @@ +// 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.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 <corperm.h> +#include "../inc/mdlog.h" +#include "utsem.h" + +#include "newmerger.h" + +#include "rwutil.h" +#include "mdperf.h" +#include <ivehandler.h> + +#include "sigparser.h" +#ifdef FEATURE_FUSION +#include "fusion.h" +#endif + +#include "winmdinterfaces.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 + +#ifndef FEATURE_METADATA_STANDALONE_WINRT + , public IMetaDataInfo +#endif + +#ifdef FEATURE_METADATA_EMIT + , public IMetaDataEmit2 + , public IMetaDataAssemblyEmit +#endif + +#ifdef FEATURE_METADATA_VALIDATOR + , public IMetaDataValidate +#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 NEWMERGER; + 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) + +//***************************************************************************** +// 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_VALIDATOR +//***************************************************************************** +// IMetaDataValidator +//***************************************************************************** + + STDMETHODIMP ValidatorInit( + DWORD dwModuleType, // [IN] Specifies whether the module is a PE file or an obj. + IUnknown * pUnk); // [IN] Validation error handler. + + STDMETHODIMP ValidateMetaData(); +#endif //FEATURE_METADATA_VALIDATOR + +#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); + +//***************************************************************************** +// 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. + +#ifndef FEATURE_METADATA_STANDALONE_WINRT + +//***************************************************************************** +// 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). + +#endif //!FEATURE_METADATA_STANDALONE_WINRT + +#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(); + + 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(); + HRESULT ProcessFilter(); + HRESULT ProcessFilterWorker(); + + // 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() + + // 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; + bool m_fOwnSem; + + 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. +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + ICorRuntimeHost *m_pCorHost; // Hosting environment for EE runtime. +#endif // FEATURE_INCLUDE_ALL_INTERFACES + IUnknown *m_pAppDomain; // AppDomain in which managed security code will be run. + +private: + ULONG m_OpenFlags; // Open time flags. + + LONG m_cRef; // Ref count. +#ifdef FEATURE_METADATA_EMIT_ALL + NEWMERGER m_newMerger; // class for handling merge +#endif //FEATURE_METADATA_EMIT_ALL + 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; + IVEHandler *m_pVEHandler; +#ifndef FEATURE_CORECLR + ValidateRecordFunction m_ValidateRecordFunctionTable[TBL_COUNT]; +#endif + 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 + + HRESULT _ValidateErrorHelper( + HRESULT VECode, + VEContext Context); + + HRESULT _ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1); + + HRESULT _ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1, + ULONG ulVal2); + + HRESULT _ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1, + ULONG ulVal2, + ULONG ulVal3); + +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/md/compiler/regmeta_compilersupport.cpp b/src/md/compiler/regmeta_compilersupport.cpp new file mode 100644 index 0000000000..0bea06699b --- /dev/null +++ b/src/md/compiler/regmeta_compilersupport.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. +// 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 <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. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + IMetaDataImport2 *pI2=NULL; + + LOG((LOGMD, "RegMeta::Merge(0x%08x, 0x%08x)\n", pImport, pHandler)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(VerifyNotWinMD(pImport, "IMetaDataEmit::Merge(): merging with a .winmd file not supported.")); + + IfFailGo(pImport->QueryInterface(IID_IMetaDataImport2, (void**)&pI2)); + m_hasOptimizedRefToDef = false; + + // track this import + IfFailGo( m_newMerger.AddImport(pI2, pHostMapToken, pHandler) ); + +ErrExit: + if (pI2) + pI2->Release(); + STOP_MD_PERF(Merge); + END_ENTRYPOINT_NOTHROW; + + return (hr); +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // RegMeta::Merge + + +//***************************************************************************** +// real merge takes place here +//***************************************************************************** +STDMETHODIMP RegMeta::MergeEnd() // S_OK or error. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = NOERROR; + + BEGIN_ENTRYPOINT_NOTHROW; + + LOG((LOGMD, "RegMeta::MergeEnd()\n")); + START_MD_PERF(); + LOCKWRITE(); + // Merge happens here!! + + // <REVISIT_TODO>bug 16719. Merge itself is doing a lots of small changes in literally + // dozens of places. It would be to hard to maintain and would cause code + // bloat to auto-grow the tables. So instead, we've opted to just expand + // the world right away and avoid the trouble.</REVISIT_TODO> + IfFailGo(m_pStgdb->m_MiniMd.ExpandTables()); + + IfFailGo(m_newMerger.Merge(m_OptionValue.m_MergeOptions, m_OptionValue.m_RefToDefCheck) ); + +ErrExit: + STOP_MD_PERF(MergeEnd); + END_ENTRYPOINT_NOTHROW; + + return (hr); +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // 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 + +#ifdef FEATURE_METADATA_EMIT_ALL + +// Helper for code:RegMeta::ProcessFilter +HRESULT RegMeta::ProcessFilterWorker() +{ + HRESULT hr = S_OK; + + CMiniMdRW *pMiniMd; // The MiniMd with the data. + RegMeta *pMetaNew = NULL; + CMapToken *pMergeMap = NULL; + IMapToken *pMapNew = NULL; + MergeTokenManager *pCompositHandler = NULL; + IMapToken *pHostMapToken = NULL; + + // For convenience. + pMiniMd = &(m_pStgdb->m_MiniMd); + IfNullGo( pMiniMd->GetFilterTable() ); + _ASSERTE(pMiniMd->GetFilterTable()->Count() != 0); // caller verified this + + // Yes, client has used filter to specify what are the metadata needed. + // We will create another instance of RegMeta and make this module an imported module + // to be merged into the new RegMeta. We will provide the handler to track all of the token + // movements. We will replace the merged light weight stgdb to this RegMeta.. + // Then we will need to fix up the MergeTokenManager with this new movement. + // The reason that we decide to choose this approach is because it will be more complicated + // and very likely less efficient to fix up the signature blob pool and then compact all of the pools! + // + + // Create a new RegMeta. + pMetaNew = new (nothrow) RegMeta(); + IfNullGo( pMetaNew ); + pMetaNew->AddRef(); + IfFailGo(pMetaNew->SetOption(&m_OptionValue)); + + + // Remember the open type. + IfFailGo(pMetaNew->CreateNewMD()); + IfFailGo(pMetaNew->AddToCache()); + + // Ignore the error return by setting handler + hr = pMetaNew->SetHandler(m_pHandler); + + // create the IMapToken to receive token remap information from merge + pMergeMap = new (nothrow) CMapToken; + IfNullGo( pMergeMap ); + + // use merge to filter out the unneeded data. But we need to keep COMType and also need to drop off the + // CustomAttributes that associated with MemberRef with parent MethodDef + // + pMetaNew->m_hasOptimizedRefToDef = false; + IfFailGo( pMetaNew->m_newMerger.AddImport(this, pMergeMap, NULL) ); + IfFailGo( pMetaNew->m_pStgdb->m_MiniMd.ExpandTables()); + IfFailGo( pMetaNew->m_newMerger.Merge((MergeFlags)(MergeManifest | DropMemberRefCAs | NoDupCheck), MDRefToDefDefault) ); + + // Now we need to recalculate the token movement + // + if (m_newMerger.m_pImportDataList) + { + + // This is the case the filter is applied to merged emit scope. We need calculate how this implicit merge + // affects the original merge remap. Basically we need to walk all the m_pTkMapList in the merger and replace + // the to token to the most recent to token. + // + MDTOKENMAP *pMDTokenMapList; + + pMDTokenMapList = m_newMerger.m_pImportDataList->m_pMDTokenMap; + + MDTOKENMAP *pMap; + TOKENREC *pTKRec; + ULONG i; + mdToken tkFinalTo; + ModuleRec *pMod; + ModuleRec *pModNew; + LPCUTF8 szName; + + // update each import map from merge to have the m_tkTo points to the final mapped to token + for (pMap = pMDTokenMapList; pMap; pMap = pMap->m_pNextMap) + { + // update each record + for (i = 0; i < (ULONG) (pMap->Count()); i++) + { + TOKENREC *pRecTo; + pTKRec = pMap->Get(i); + if ( pMergeMap->Find( pTKRec->m_tkTo, &pRecTo ) ) + { + // This record is kept by the filter and the tkTo is changed + pRecTo->m_isFoundInImport = true; + tkFinalTo = pRecTo->m_tkTo; + pTKRec->m_tkTo = tkFinalTo; + pTKRec->m_isDeleted = false; + + // send the notification now. Because after merge, we may have everything in order and + // won't send another set of notification. + // + LOG((LOGMD, "TokenRemap in RegMeta::ProcessFilter (IMapToken 0x%08x): from 0x%08x to 0x%08x\n", pMap->m_pMap, pTKRec->m_tkFrom, pTKRec->m_tkTo)); + + pMap->m_pMap->Map(pTKRec->m_tkFrom, pTKRec->m_tkTo); + } + else + { + // This record is pruned by the filter upon save + pTKRec->m_isDeleted = true; + } + } + } + + // now walk the pMergeMap and check to see if there is any entry that is not set to true for m_isFoundInImport. + // These are the records that from calling DefineXXX methods directly on the Emitting scope! + if (m_pHandler) + m_pHandler->QueryInterface(IID_IMapToken, (void **)&pHostMapToken); + if (pHostMapToken) + { + for (i = 0; i < (ULONG) (pMergeMap->m_pTKMap->Count()); i++) + { + pTKRec = pMergeMap->m_pTKMap->Get(i); + if (pTKRec->m_isFoundInImport == false) + { + LOG((LOGMD, "TokenRemap in RegMeta::ProcessFilter (default IMapToken 0x%08x): from 0x%08x to 0x%08x\n", pHostMapToken, pTKRec->m_tkFrom, pTKRec->m_tkTo)); + + // send the notification on the IMapToken from SetHandler of this RegMeta + pHostMapToken->Map(pTKRec->m_tkFrom, pTKRec->m_tkTo); + } + } + } + + // Preserve module name across merge. + IfFailGo(m_pStgdb->m_MiniMd.GetModuleRecord(1, &pMod)); + IfFailGo(pMetaNew->m_pStgdb->m_MiniMd.GetModuleRecord(1, &pModNew)); + IfFailGo(m_pStgdb->m_MiniMd.getNameOfModule(pMod, &szName)); + IfFailGo(pMetaNew->m_pStgdb->m_MiniMd.PutString(TBL_Module, ModuleRec::COL_Name, pModNew, szName)); + + // now swap the stgdb but keep the merger... + _ASSERTE( !IsOfExternalStgDB(m_OpenFlags) ); + + CLiteWeightStgdbRW * pStgdbTmp = m_pStgdb; + m_pStgdb = pMetaNew->m_pStgdb; + pMetaNew->m_pStgdb = pStgdbTmp; + // Update RuntimeVersion string pointers to point to the owning RegMeta string (the strings are 2 copies of the same string content) + m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = m_OptionValue.m_RuntimeVersion; + pMetaNew->m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = pMetaNew->m_OptionValue.m_RuntimeVersion; + } + else + { + // swap the Stgdb + CLiteWeightStgdbRW * pStgdbTmp = m_pStgdb; + m_pStgdb = pMetaNew->m_pStgdb; + pMetaNew->m_pStgdb = pStgdbTmp; + // Update RuntimeVersion string pointers to point to the owning RegMeta string (the strings are 2 copies of the same string content) + m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = m_OptionValue.m_RuntimeVersion; + pMetaNew->m_pStgdb->m_MiniMd.m_OptionValue.m_RuntimeVersion = pMetaNew->m_OptionValue.m_RuntimeVersion; + + // Client either open an existing scope and apply the filter mechanism, or client define the scope and then + // apply the filter mechanism. + + // In this case, host better has supplied the handler!! + _ASSERTE( m_bRemap && m_pHandler); + IfFailGo( m_pHandler->QueryInterface(IID_IMapToken, (void **) &pMapNew) ); + + + { + // Send the notification of token movement now because after merge we may not move tokens again + // and thus no token notification will be send. + MDTOKENMAP *pMap = pMergeMap->m_pTKMap; + TOKENREC *pTKRec; + ULONG i; + + for (i=0; i < (ULONG) (pMap->Count()); i++) + { + pTKRec = pMap->Get(i); + pMap->m_pMap->Map(pTKRec->m_tkFrom, pTKRec->m_tkTo); + } + + } + + + // What we need to do here is create a IMapToken that will replace the original handler. This new IMapToken + // upon called will first map the from token to the most original from token. + // + pCompositHandler = new (nothrow) MergeTokenManager(pMergeMap->m_pTKMap, NULL); + IfNullGo( pCompositHandler ); + + // now update the following field to hold on to the real IMapToken supplied by our client by SetHandler + if (pMergeMap->m_pTKMap->m_pMap) + pMergeMap->m_pTKMap->m_pMap->Release(); + _ASSERTE(pMapNew); + pMergeMap->m_pTKMap->m_pMap = pMapNew; + + // ownership transferred + pMergeMap = NULL; + pMapNew = NULL; + + // now you want to replace all of the IMapToken set by calling SetHandler to this new MergeTokenManager + IfFailGo( m_pStgdb->m_MiniMd.SetHandler(pCompositHandler) ); + + m_pHandler = pCompositHandler; + + // ownership transferred + pCompositHandler = NULL; + } + + // Force a ref to def optimization because the remap information was stored in the thrown away CMiniMdRW + m_hasOptimizedRefToDef = false; + IfFailGo( RefToDefOptimization() ); + +ErrExit: + if (pHostMapToken) + pHostMapToken->Release(); + if (pMetaNew) + pMetaNew->Release(); + if (pMergeMap) + pMergeMap->Release(); + if (pCompositHandler) + pCompositHandler->Release(); + if (pMapNew) + pMapNew->Release(); + + return hr; +} // RegMeta::ProcessFilter + +#endif //FEATURE_METADATA_EMIT_ALL + +#endif //FEATURE_METADATA_EMIT diff --git a/src/md/compiler/regmeta_emit.cpp b/src/md/compiler/regmeta_emit.cpp new file mode 100644 index 0000000000..22d2979343 --- /dev/null +++ b/src/md/compiler/regmeta_emit.cpp @@ -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. +// See the LICENSE file in the project root for more information. +// +// 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))); + } + } +#ifdef FEATURE_METADATA_EMIT_ALL + else if (m_newMerger.m_pImportDataList) + { + // always pipe through another pass of merge to drop unnecessary ref for linker. + MarkAll(); + } +#endif //FEATURE_METADATA_EMIT_ALL + + 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; + MergeTokenManager *ptkMgr = NULL; + + // 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; + +#ifdef FEATURE_METADATA_EMIT_ALL + if (m_newMerger.m_pImportDataList != NULL) + { + // This is the linker scenario. We we have IMap for each scope. We will create an instance of our own mapper + // who knows how to send notification back to host! + + // cache the host provided handler to the end our MergeTokenManager + + ptkMgr = new (nothrow) MergeTokenManager(m_newMerger.m_pImportDataList->m_pMDTokenMap, m_pHandler); + IfNullGo(ptkMgr); + hr = m_pStgdb->m_MiniMd.SetHandler(ptkMgr); + _ASSERTE(SUCCEEDED(hr)); + } +#endif //FEATURE_METADATA_EMIT_ALL + + 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)); + } + } + } + } + +#ifdef FEATURE_METADATA_EMIT_ALL + IfFailGo(ProcessFilter()); + + if (m_newMerger.m_pImportDataList != NULL) + { + // Allocate a token mapper object that will be used for phase 1 if there is not Handler but + // linker has provided the IMapToken + // + m_bRemap = true; + } +#endif //FEATURE_METADATA_EMIT_ALL + + // 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: + if (ptkMgr != NULL) + { + // recovery the initial state + hr = m_pStgdb->m_MiniMd.SetHandler(NULL); + ptkMgr->Release(); + } + + 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 + +#ifdef FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// Process filter +//***************************************************************************** +HRESULT RegMeta::ProcessFilter() +{ + HRESULT hr = S_OK; + + CMiniMdRW *pMiniMd; // The MiniMd with the data. + + START_MD_PERF(); + + // For convenience. + pMiniMd = &(m_pStgdb->m_MiniMd); + IfNullGo( pMiniMd->GetFilterTable() ); + if ( pMiniMd->GetFilterTable()->Count() == 0 ) + { + // there is no filter + goto ErrExit; + } + hr = ProcessFilterWorker(); + +ErrExit: + STOP_MD_PERF(ProcessFilter); + + return hr; +} // RegMeta::ProcessFilter + +#endif //FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// 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)); + memset(&hEnum, 0, sizeof(HENUMInternal)); + + // 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 == ULONG_MAX); + + // Get the record. + IfFailGo(m_pStgdb->m_MiniMd.GetTypeDefRecord(RidFromToken(td), &pRecord)); + + if (dwTypeDefFlags != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_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 != ULONG_MAX) + pRecord->SetRVA(ulCodeRVA); + if (dwImplFlags != ULONG_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 != ULONG_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (dwFieldFlags == ULONG_MAX) + dwFieldFlags = pRecord->GetFlags(); + dwFieldFlags |= fdHasDefault; + + bHasDefault = true; + } + + // Set the flags. + if (dwFieldFlags != ULONG_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 != ULONG_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 != ULONG_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (dwPropFlags == ULONG_MAX) + dwPropFlags = pRecord->GetPropFlags(); + dwPropFlags |= prHasDefault; + + bHasDefault = true; + } + if (dwPropFlags != ULONG_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 != ULONG_MAX) + { + _ASSERTE(TypeFromToken(mdGetter) == mdtMethodDef || IsNilToken(mdGetter)); + IfFailGo(_DefineMethodSemantics(msGetter, mdGetter, pr, bClear)); + } + + // Store the setter (or clear out old one). + if (mdSetter != ULONG_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 != ULONG_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 != ULONG_MAX) && + (pValue || (pValue == 0 && (dwCPlusTypeFlag == ELEMENT_TYPE_STRING || + dwCPlusTypeFlag == ELEMENT_TYPE_CLASS)))) + { + if (dwParamFlags == ULONG_MAX) + dwParamFlags = pRecord->GetFlags(); + dwParamFlags |= pdHasDefault; + + bHasDefault = true; + } + // Set the flags. + if (dwParamFlags != ULONG_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/md/compiler/regmeta_imetadatatables.cpp b/src/md/compiler/regmeta_imetadatatables.cpp new file mode 100644 index 0000000000..a36ccfc38b --- /dev/null +++ b/src/md/compiler/regmeta_imetadatatables.cpp @@ -0,0 +1,719 @@ +// 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. +// +// 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/md/compiler/regmeta_import.cpp b/src/md/compiler/regmeta_import.cpp new file mode 100644 index 0000000000..dd260b42a8 --- /dev/null +++ b/src/md/compiler/regmeta_import.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. +// See the LICENSE file in the project root for more information. +// +// 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 __stdcall 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; + + // This function may be called through RCW. When hosted, we have probed before this call, so the + // following contract violation is OK. + CONTRACT_VIOLATION(SOToleranceViolation); + 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/md/compiler/regmeta_vm.cpp b/src/md/compiler/regmeta_vm.cpp new file mode 100644 index 0000000000..beebb08b5c --- /dev/null +++ b/src/md/compiler/regmeta_vm.cpp @@ -0,0 +1,586 @@ +// 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 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> + + +#ifndef FEATURE_CORECLR + +#include <metahost.h> + +// Pointer to the activated CLR interface provided by the shim. +extern ICLRRuntimeInfo *g_pCLRRuntime; + +#ifdef FEATURE_METADATA_EMIT_ALL + +#include "iappdomainsetup.h" + +// {27FFF232-A7A8-40dd-8D4A-734AD59FCD41} +EXTERN_GUID(IID_IAppDomainSetup, 0x27FFF232, 0xA7A8, 0x40dd, 0x8D, 0x4A, 0x73, 0x4A, 0xD5, 0x9F, 0xCD, 0x41); + +#endif //FEATURE_METADATA_EMIT_ALL + +#endif // !FEATURE_CORECLR + + +#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) || defined(FEATURE_METADATA_STANDALONE_WINRT) + 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 && !FEATURE_METADATA_STANDALONE_WINRT + return S_OK; +#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT +} // 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) || defined(FEATURE_METADATA_STANDALONE_WINRT) + return LOADEDMODULES::FindCachedReadOnlyEntry(szName, dwOpenFlags, ppMeta); +#else //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT + // No cache support in standalone version. + *ppMeta = NULL; + return S_FALSE; +#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT +} // RegMeta::FindCachedReadOnlyEntry + + +#ifdef FEATURE_METADATA_EMIT_ALL + +//***************************************************************************** +// Helper function to startup the EE +// +// Notes: +// This is called by code:RegMeta.DefineSecurityAttributeSet. +//***************************************************************************** +HRESULT RegMeta::StartupEE() +{ +#ifdef FEATURE_CORECLR + UNREACHABLE_MSG_RET("About to CoCreateInstance! This code should not be " + "reachable or needs to be reimplemented for CoreCLR!"); +#else // !FEATURE_CORECLR + + struct Param + { + RegMeta *pThis; + IUnknown *pSetup; + IAppDomainSetup *pDomainSetup; + bool fDoneStart; + HRESULT hr; + } param; + param.pThis = this; + param.pSetup = NULL; + param.pDomainSetup = NULL; + param.fDoneStart = false; + param.hr = S_OK; + + PAL_TRY(Param *, pParam, ¶m) + { + HRESULT hr = S_OK; + + DWORD dwBuffer[1 + (MAX_LONGPATH+1) * sizeof(WCHAR) / sizeof(DWORD) + 1]; + BSTR bstrDir = NULL; + + // Create a hosting environment. + IfFailGo(g_pCLRRuntime->GetInterface( + CLSID_CorRuntimeHost, + IID_ICorRuntimeHost, + (void **)&pParam->pThis->m_pCorHost)); + + // Startup the runtime. + IfFailGo(pParam->pThis->m_pCorHost->Start()); + pParam->fDoneStart = true; + + // Create an AppDomain Setup so we can set the AppBase. + IfFailGo(pParam->pThis->m_pCorHost->CreateDomainSetup(&pParam->pSetup)); + + // Get the current directory (place it in a BSTR). + bstrDir = (BSTR)(dwBuffer + 1); + if ((dwBuffer[0] = (WszGetCurrentDirectory(MAX_LONGPATH + 1, bstrDir) * sizeof(WCHAR)))) + { + // QI for the IAppDomainSetup interface. + IfFailGo(pParam->pSetup->QueryInterface(IID_IAppDomainSetup, + (void**)&pParam->pDomainSetup)); + + // Set the AppBase. + pParam->pDomainSetup->put_ApplicationBase(bstrDir); + } + + // Create a new AppDomain. + IfFailGo(pParam->pThis->m_pCorHost->CreateDomainEx(W("Compilation Domain"), + pParam->pSetup, + NULL, + &pParam->pThis->m_pAppDomain)); + + // That's it, we're all set up. + _ASSERTE(pParam->pThis->m_pAppDomain != NULL); + pParam->pThis->m_fStartedEE = true; + + ErrExit: + pParam->hr = hr; + } + PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _ASSERTE(!"Unexpected exception setting up hosting environment for security attributes"); + param.hr = E_FAIL; + } + PAL_ENDTRY + + // Cleanup temporary resources. + if (m_pAppDomain && FAILED(param.hr)) + m_pAppDomain->Release(); + if (param.pDomainSetup) + param.pDomainSetup->Release(); + if (param.pSetup) + param.pSetup->Release(); + if (param.fDoneStart && FAILED(param.hr)) + m_pCorHost->Stop(); + if (m_pCorHost && FAILED(param.hr)) + m_pCorHost->Release(); + return param.hr; +#endif // FEATURE_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. +{ +#ifdef FEATURE_METADATA_EMIT_ALL + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + NewArrayHolder <CORSEC_ATTRSET> rAttrSets; + DWORD i; + mdPermission ps; + DWORD dwAction; + bool fProcessDeclarativeSecurityAtRuntime; + + LOG((LOGMD, "RegMeta::DefineSecurityAttributeSet(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", + tkObj, rSecAttrs, cSecAttrs, pulErrorAttr)); + START_MD_PERF(); + LOCKWRITE(); + + IfFailGo(m_pStgdb->m_MiniMd.PreUpdate()); + + rAttrSets = new (nothrow) CORSEC_ATTRSET[dclMaximumValue + 1]; + if (rAttrSets == NULL) + { + hr = E_OUTOFMEMORY; + goto ErrExit; + } + + memset(rAttrSets, 0, sizeof(CORSEC_ATTRSET) * (dclMaximumValue + 1)); + + // Initialize error index to indicate a general error. + if (pulErrorAttr) + *pulErrorAttr = cSecAttrs; + + fProcessDeclarativeSecurityAtRuntime = true; + + // See if we should default to old v1.0/v1.1 serialization behavior + if (m_OptionValue.m_MetadataVersion < MDVersion2) + fProcessDeclarativeSecurityAtRuntime = false; + + // Startup the EE just once, no matter how many times we're called (this is + // better on performance and the EE falls over if we try a start-stop-start + // cycle anyway). + if (!m_fStartedEE && !fProcessDeclarativeSecurityAtRuntime) + { + IfFailGo(StartupEE()); + } + + // Group the security attributes by SecurityAction (thus creating an array of CORSEC_PERM's) + IfFailGo(GroupSecurityAttributesByAction(/*OUT*/rAttrSets, rSecAttrs, cSecAttrs, tkObj, pulErrorAttr, &m_pStgdb->m_MiniMd, NULL)); + + // Put appropriate data in the metadata + for (i = 0; i <= dclMaximumValue; i++) + { + NewArrayHolder <BYTE> pbBlob(NULL); + NewArrayHolder <BYTE> pbNonCasBlob(NULL); + DWORD cbBlob = 0; + DWORD cbNonCasBlob = 0; + + rAttrSets[i].pImport = this; + rAttrSets[i].pAppDomain = m_pAppDomain; + if (rAttrSets[i].dwAttrCount == 0) + continue; + if (pulErrorAttr) + *pulErrorAttr = i; + + if(fProcessDeclarativeSecurityAtRuntime) + { + // Put a serialized CORSEC_ATTRSET in the metadata + SIZE_T cbAttrSet = 0; + IfFailGo(AttributeSetToBlob(&rAttrSets[i], NULL, &cbAttrSet, this, i)); // count size required for buffer + if (!FitsIn<DWORD>(cbAttrSet)) + { + hr = COR_E_OVERFLOW; + goto ErrExit; + } + cbBlob = static_cast<DWORD>(cbAttrSet); + + pbBlob = new (nothrow) BYTE[cbBlob]; // allocate buffer + if (pbBlob == NULL) + { + hr = E_OUTOFMEMORY; + goto ErrExit; + } + + IfFailGo(AttributeSetToBlob(&rAttrSets[i], pbBlob, NULL, this, i)); // serialize into the buffer + IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj, rAttrSets[i].dwAction, pbBlob, cbBlob, &ps)); // put it in metadata + } + else + { + // Now translate the sets of security attributes into a real permission + // set and convert this to a serialized Xml blob. We may possibly end up + // with two sets as the result of splitting CAS and non-CAS permissions + // into separate sets. + hr = TranslateSecurityAttributes(&rAttrSets[i], &pbBlob, &cbBlob, &pbNonCasBlob, &cbNonCasBlob, pulErrorAttr); + IfFailGo(hr); + + // Persist the permission set blob into the metadata. For empty CAS + // blobs this is only done if the corresponding non-CAS blob is empty + if (cbBlob || !cbNonCasBlob) + IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj, rAttrSets[i].dwAction, pbBlob, cbBlob, &ps)); + + if (pbNonCasBlob) + { + // Map the SecurityAction to a special non-CAS action so this + // blob will have its own entry in the metadata + switch (rAttrSets[i].dwAction) + { + case dclDemand: + dwAction = dclNonCasDemand; + break; + case dclLinktimeCheck: + dwAction = dclNonCasLinkDemand; + break; + case dclInheritanceCheck: + dwAction = dclNonCasInheritance; + break; + default: + PostError(CORSECATTR_E_BAD_NONCAS); + IfFailGo(CORSECATTR_E_BAD_NONCAS); + } + + // Persist to metadata + IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj, + dwAction, + pbNonCasBlob, + cbNonCasBlob, + &ps)); + } + } + } + +ErrExit: + STOP_MD_PERF(DefineSecurityAttributeSet); + + END_ENTRYPOINT_NOTHROW; + + return (hr); +#else //!FEATURE_METADATA_EMIT_ALL + return E_NOTIMPL; +#endif //!FEATURE_METADATA_EMIT_ALL +} // 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; + WCHAR rcModule[_MAX_PATH]; + + 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; + } + +#ifndef FEATURE_CORECLR + wcscpy_s(rcModule, _MAX_PATH, wzNameSpace); + + //****************** + // Try to find the module on CORPATH + //****************** + + if ((wcsncmp(rcModule, W("System."), 16) != 0) && + (wcsncmp(rcModule, W("System/"), 16) != 0)) + { + // only go through regular CORPATH lookup by fully qualified class name when + // it is not System.* + hr = CORPATHService::GetClassFromCORPath( + rcModule, + tr, + pMiniMd, + riid, + ppIScope, + ptd); + } + else + { + // force it to look for System.* in mscorlib.dll + hr = S_FALSE; + } + + if (hr == S_FALSE) + { + LPWSTR szTmp; + WszSearchPath( + NULL, + W("mscorlib.dll"), + NULL, + sizeof(rcModule) / sizeof(rcModule[0]), + rcModule, + &szTmp); + + //******************* + // Last desperate try!! + //******************* + + // Use the file name "mscorlib: + IfFailGo(CORPATHService::FindTypeDef( + rcModule, + tr, + pMiniMd, + riid, + ppIScope, + ptd)); + if (hr == S_FALSE) + { + IfFailGo(META_E_CANNOTRESOLVETYPEREF); + } + } +#else //FEATURE_CORECLR + IfFailGo(META_E_CANNOTRESOLVETYPEREF); +#endif //FEATURE_CORECLR + +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() +{ + // This is called during cleanup. We can not fail this call by probing. + // As long as we make sure the cleanup does not use too much space through + // BEGIN_CLEANUP_ENTRYPOINT, we are OK. + CONTRACT_VIOLATION (SOToleranceViolation); + BEGIN_CLEANUP_ENTRYPOINT; + +#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT) + _ASSERTE(!m_bCached || LOADEDMODULES::IsEntryInList(this)); +#else + _ASSERTE(!m_bCached); +#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT + 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) || defined(FEATURE_METADATA_STANDALONE_WINRT) + 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 && !FEATURE_METADATA_STANDALONE_WINRT + } + END_CLEANUP_ENTRYPOINT + + return cRef; +} // RegMeta::Release diff --git a/src/md/compiler/stdafx.cpp b/src/md/compiler/stdafx.cpp new file mode 100644 index 0000000000..ff341e19a7 --- /dev/null +++ b/src/md/compiler/stdafx.cpp @@ -0,0 +1,12 @@ +// 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. +//***************************************************************************** +// stdafx.cpp +// + +// +// Precompiled headers. +// +//***************************************************************************** +#include "stdafx.h" diff --git a/src/md/compiler/stdafx.h b/src/md/compiler/stdafx.h new file mode 100644 index 0000000000..520fe44d05 --- /dev/null +++ b/src/md/compiler/stdafx.h @@ -0,0 +1,26 @@ +// 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. +//***************************************************************************** +// 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 "nsutilpriv.h" + +#include "utsem.h" + +#endif // __STDAFX_H_ diff --git a/src/md/compiler/verifylayouts.cpp b/src/md/compiler/verifylayouts.cpp new file mode 100644 index 0000000000..bbf7b94b14 --- /dev/null +++ b/src/md/compiler/verifylayouts.cpp @@ -0,0 +1,14 @@ +// 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. +//***************************************************************************** +// VerifyLayouts.cpp +// + +// +// Make sure that layouts of MD data structures doesn't change accidentally +// +//***************************************************************************** + +#include "stdafx.h" +#include "verifylayouts.h" diff --git a/src/md/compiler/wks/.gitmirror b/src/md/compiler/wks/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/md/compiler/wks/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/md/compiler/wks/CMakeLists.txt b/src/md/compiler/wks/CMakeLists.txt new file mode 100644 index 0000000000..6bf6c80868 --- /dev/null +++ b/src/md/compiler/wks/CMakeLists.txt @@ -0,0 +1,4 @@ +include(../../md_wks.cmake) + +add_precompiled_header(stdafx.h ../stdafx.cpp MDCOMPILER_SOURCES) +add_library_clr(mdcompiler_wks ${MDCOMPILER_SOURCES})
\ No newline at end of file diff --git a/src/md/compiler/wks/MDCompiler_wks.nativeproj b/src/md/compiler/wks/MDCompiler_wks.nativeproj new file mode 100644 index 0000000000..8457d87c19 --- /dev/null +++ b/src/md/compiler/wks/MDCompiler_wks.nativeproj @@ -0,0 +1,19 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <PropertyGroup> + <!-- All features are set in file:..\..\MD.props --> + <MetadataFlavor>wks</MetadataFlavor> + </PropertyGroup> + + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\MD\Compiler\Compiler.settings.targets" /> + + <!--Leaf project Properties--> + <PropertyGroup> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputName>MDCompiler_wks</OutputName> + </PropertyGroup> + + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> |