summaryrefslogtreecommitdiff
path: root/src/md/compiler/regmeta_compilersupport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/md/compiler/regmeta_compilersupport.cpp')
-rw-r--r--src/md/compiler/regmeta_compilersupport.cpp506
1 files changed, 506 insertions, 0 deletions
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