diff options
Diffstat (limited to 'src/md/inc/rwutil.h')
-rw-r--r-- | src/md/inc/rwutil.h | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/src/md/inc/rwutil.h b/src/md/inc/rwutil.h new file mode 100644 index 0000000000..5d7f98919c --- /dev/null +++ b/src/md/inc/rwutil.h @@ -0,0 +1,369 @@ +// 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. +//***************************************************************************** +// RWUtil.h +// + +// +// Contains utility code for MD directory +// +//***************************************************************************** +#ifndef __RWUtil__h__ +#define __RWUtil__h__ + +class UTSemReadWrite; + +#define UTF8STR(wszInput, szOutput) \ + do { \ + if ((wszInput) == NULL) \ + { \ + (szOutput) = NULL; \ + } \ + else \ + { \ + int cbBuffer = ((int)wcslen(wszInput) * 3) + 1; \ + (szOutput) = (char *)_alloca(cbBuffer); \ + Unicode2UTF((wszInput), (szOutput), cbBuffer); \ + } \ + } while (0) + +//***************************************************************************** +// Helper methods +//***************************************************************************** +void +Unicode2UTF( + LPCWSTR wszSrc, // The string to convert. + __out_ecount(cbDst) + LPUTF8 szDst, // Buffer for the output UTF8 string. + int cbDst); // Size of the buffer for UTF8 string. + +//********************************************************************* +// The token remap record. +//********************************************************************* +struct TOKENREC +{ + mdToken m_tkFrom; // The imported token + bool m_isDuplicate; // Is record duplicate? This information is recorded during merge + bool m_isDeleted; // This information is recorded during RegMeta::ProcessFilter when we might have deleted a record + bool m_isFoundInImport; // This information is also recorded during RegMeta::ProcessFilter + mdToken m_tkTo; // The new token in the merged scope + + void SetEmpty() {m_tkFrom = m_tkTo = (mdToken) -1;} + BOOL IsEmpty() {return m_tkFrom == (mdToken) -1;} +}; + + +//********************************************************************* +// +// This structure keeps track on token remap for an imported scope. This map is initially sorted by from +// tokens. It can then become sorted by To tokens. This usually happen during PreSave remap lookup. Thus +// we assert if we try to look up or sort by From token. +// +//********************************************************************* +class MDTOKENMAP : public CDynArray<TOKENREC> +{ +public: + + enum SortKind{ + Unsorted = 0, + SortByFromToken = 1, + SortByToToken = 2, + Indexed = 3, // Indexed by table/rid. Implies that strings are sorted by "From". + }; + + MDTOKENMAP() + : m_pNextMap(NULL), + m_pMap(NULL), + m_iCountTotal(0), + m_iCountSorted(0), + m_sortKind(SortByFromToken), + m_iCountIndexed(0) +#if defined(_DEBUG) + ,m_pImport(0) +#endif + { } + ~MDTOKENMAP(); + + HRESULT Init(IUnknown *pImport); + + // find a token in the tokenmap. + bool Find(mdToken tkFrom, TOKENREC **ppRec); + + // remap a token. We assert if we don't find the tkFind in the table + HRESULT Remap(mdToken tkFrom, mdToken *ptkTo); + + // Insert a record. This function will keep the inserted record in a sorted sequence + HRESULT InsertNotFound(mdToken tkFrom, bool fDuplicate, mdToken tkTo, TOKENREC **ppRec); + + // This function will just append the record to the end of the list + HRESULT AppendRecord( + mdToken tkFrom, + bool fDuplicate, + mdToken tkTo, + TOKENREC **ppRec); + + // This is a safe remap. *tpkTo will be tkFind if we cannot find tkFind in the lookup table. + mdToken SafeRemap(mdToken tkFrom); // [IN] the token value to find + + bool FindWithToToken( + mdToken tkFind, // [IN] the token value to find + int *piPosition); // [OUT] return the first from-token that has the matching to-token + + FORCEINLINE void SortTokensByFromToken() + { + _ASSERTE(m_sortKind == SortByFromToken || m_sortKind == Indexed); + // Only sort if there are unsorted records. + if (m_iCountSorted < m_iCountTotal) + { + SortRangeFromToken(m_iCountIndexed, m_iCountIndexed+m_iCountTotal - 1); + m_iCountSorted = m_iCountTotal; + } + } // void MDTOKENMAP::SortTokensByFromToken() + + HRESULT EmptyMap(); + + void SortTokensByToToken(); + + MDTOKENMAP *m_pNextMap; + IMapToken *m_pMap; + +private: + FORCEINLINE int CompareFromToken( // -1, 0, or 1 + int iLeft, // First item to compare. + int iRight) // Second item to compare. + { + if ( Get(iLeft)->m_tkFrom < Get(iRight)->m_tkFrom ) + return -1; + if ( Get(iLeft)->m_tkFrom == Get(iRight)->m_tkFrom ) + return 0; + return 1; + } + + FORCEINLINE int CompareToToken( // -1, 0, or 1 + int iLeft, // First item to compare. + int iRight) // Second item to compare. + { + if ( Get(iLeft)->m_tkTo < Get(iRight)->m_tkTo ) + return -1; + if ( Get(iLeft)->m_tkTo == Get(iRight)->m_tkTo ) + return 0; + return 1; + } + + FORCEINLINE void Swap( + int iFirst, + int iSecond) + { + if ( iFirst == iSecond ) return; + memcpy( &m_buf, Get(iFirst), sizeof(TOKENREC) ); + memcpy( Get(iFirst), Get(iSecond),sizeof(TOKENREC) ); + memcpy( Get(iSecond), &m_buf, sizeof(TOKENREC) ); + } + + void SortRangeFromToken(int iLeft, int iRight); + void SortRangeToToken(int iLeft, int iRight); + + TOKENREC m_buf; + ULONG m_iCountTotal; // total entry in the map + ULONG m_iCountSorted; // number of entries that are sorted + + SortKind m_sortKind; + + ULONG m_TableOffset[TBL_COUNT+1]; // Start of each table in map. + ULONG m_iCountIndexed; // number of entries that are indexed. +#if defined(_DEBUG) + IMetaDataImport *m_pImport; // For data validation. +#endif +}; + + + +//********************************************************************* +// +// Merge Token manager. This class is created in GetSaveSize as an agent to +// notify linker regarding token movements. It does not have the ability to +// keep track token movement. +// +//********************************************************************* +class MergeTokenManager : public IMapToken +{ +public: + STDMETHODIMP QueryInterface(REFIID riid, PVOID *pp); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP Map(mdToken tkImp, mdToken tkEmit); + MergeTokenManager(MDTOKENMAP *pTkMapList, IUnknown *pHandler); + virtual ~MergeTokenManager(); +private: + LONG m_cRef; + MDTOKENMAP *m_pTkMapList; + IMapToken *m_pDefaultHostRemap; +}; + + + +//********************************************************************* +// +// This CMapToken class implemented the IMapToken. It is used in RegMeta for +// filter process. This class can track all of the tokens are mapped. It also +// supplies a Find function. +// +//********************************************************************* +class CMapToken : public IMapToken +{ + friend class RegMeta; + +public: + STDMETHODIMP QueryInterface(REFIID riid, PVOID *pp); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP Map(mdToken tkImp, mdToken tkEmit); + bool Find(mdToken tkFrom, TOKENREC **pRecTo); + CMapToken(); + virtual ~CMapToken(); + MDTOKENMAP *m_pTKMap; +private: + LONG m_cRef; + bool m_isSorted; +}; + +typedef CDynArray<mdToken> TOKENMAP; + +//********************************************************************* +// +// This class records all sorts of token movement during optimization phase. +// This including Ref to Def optimization. This also includes token movement +// due to sorting or eleminating the pointer tables. +// +//********************************************************************* +class TokenRemapManager +{ +public: + //********************************************************************* + // + // This function is called when a TypeRef is resolved to a TypeDef. + // + //********************************************************************* + FORCEINLINE void RecordTypeRefToTypeDefOptimization( + mdToken tkFrom, + mdToken tkTo) + { + _ASSERTE( TypeFromToken(tkFrom) == mdtTypeRef ); + _ASSERTE( TypeFromToken(tkTo) == mdtTypeDef ); + + m_TypeRefToTypeDefMap[RidFromToken(tkFrom)] = tkTo; + } // RecordTypeRefToTypeDefOptimization + + + //********************************************************************* + // + // This function is called when a MemberRef is resolved to a MethodDef or FieldDef. + // + //********************************************************************* + FORCEINLINE void RecordMemberRefToMemberDefOptimization( + mdToken tkFrom, + mdToken tkTo) + { + _ASSERTE( TypeFromToken(tkFrom) == mdtMemberRef ); + _ASSERTE( TypeFromToken(tkTo) == mdtMethodDef || TypeFromToken(tkTo) == mdtFieldDef); + + m_MemberRefToMemberDefMap[RidFromToken(tkFrom)] = tkTo; + } // RecordMemberRefToMemberDefOptimization + + //********************************************************************* + // + // This function is called when the token kind does not change but token + // is moved. For example, when we sort CustomAttribute table or when we optimize + // away MethodPtr table. These operation will not change the token type. + // + //********************************************************************* + FORCEINLINE HRESULT RecordTokenMovement( + mdToken tkFrom, + mdToken tkTo) + { + TOKENREC *pTokenRec; + + _ASSERTE( TypeFromToken(tkFrom) == TypeFromToken(tkTo) ); + return m_TKMap.AppendRecord( tkFrom, false, tkTo, &pTokenRec ); + } // RecordTokenMovement + + bool ResolveRefToDef( + mdToken tkRef, // [IN] ref token + mdToken *ptkDef); // [OUT] def token that it resolves to. If it does not resolve to a def + + FORCEINLINE TOKENMAP *GetTypeRefToTypeDefMap() { return &m_TypeRefToTypeDefMap; } + FORCEINLINE TOKENMAP *GetMemberRefToMemberDefMap() { return &m_MemberRefToMemberDefMap; } + FORCEINLINE MDTOKENMAP *GetTokenMovementMap() { return &m_TKMap; } + + ~TokenRemapManager(); + HRESULT ClearAndEnsureCapacity(ULONG cTypeRef, ULONG cMemberRef); +private: + MDTOKENMAP m_TKMap; + TOKENMAP m_TypeRefToTypeDefMap; + TOKENMAP m_MemberRefToMemberDefMap; +}; // class TokenRemapManager + +// value that can be set by SetOption APIs +struct OptionValue +{ + CorCheckDuplicatesFor m_DupCheck; // Bit Map for checking duplicates during emit. + CorRefToDefCheck m_RefToDefCheck; // Bit Map for specifying whether to do a ref to def optimization. + CorNotificationForTokenMovement m_NotifyRemap; // Bit Map for token remap notification. + ULONG m_UpdateMode; // (CorSetENC) Specifies whether ENC or Extension mode is on. + CorErrorIfEmitOutOfOrder m_ErrorIfEmitOutOfOrder; // Do not generate pointer tables + CorThreadSafetyOptions m_ThreadSafetyOptions; // specify if thread safety is turn on or not. + CorImportOptions m_ImportOption; // import options such as to skip over deleted items or not + CorLinkerOptions m_LinkerOption; // Linker option. Currently only used in UnmarkAll + BOOL m_GenerateTCEAdapters; // Do not generate the TCE adapters for COM CPC. + LPSTR m_RuntimeVersion; // CLR Version stamp + MetadataVersion m_MetadataVersion; // Version of the metadata to emit + MergeFlags m_MergeOptions; // Options to pass to the merger + UINT32 m_InitialSize; // Initial size of MetaData with values: code:CorMetaDataInitialSize. + CorLocalRefPreservation m_LocalRefPreservation; // Preserve module-local refs instead of optimizing them to defs +}; // struct OptionValue + +//********************************************************************* +// +// Helper class to ensure calling UTSemReadWrite correctly. +// The destructor will call the correct UnlockRead or UnlockWrite depends what lock it is holding. +// User should use macro defined in below instead of calling functions on this class directly. +// They are LOCKREAD(), LOCKWRITE(), and CONVERT_READ_TO_WRITE_LOCK. +// +//********************************************************************* +class CMDSemReadWrite +{ +public: + CMDSemReadWrite(UTSemReadWrite *pSem); + ~CMDSemReadWrite(); + HRESULT LockRead(); + HRESULT LockWrite(); + void UnlockWrite(); + HRESULT ConvertReadLockToWriteLock(); +private: + bool m_fLockedForRead; + bool m_fLockedForWrite; + UTSemReadWrite *m_pSem; +}; + + +#define LOCKREADIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailRet(cSem.LockRead()); +#define LOCKWRITEIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailRet(cSem.LockWrite()); + +#define LOCKREADNORET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + hr = cSem.LockRead(); +#define LOCKWRITENORET() CMDSemReadWrite cSem(m_pSemReadWrite);\ + hr = cSem.LockWrite(); + +#define LOCKREAD() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailGo(cSem.LockRead()); +#define LOCKWRITE() CMDSemReadWrite cSem(m_pSemReadWrite);\ + IfFailGo(cSem.LockWrite()); + +#define UNLOCKWRITE() cSem.UnlockWrite(); +#define CONVERT_READ_TO_WRITE_LOCK() IfFailGo(cSem.ConvertReadLockToWriteLock()); + + +#endif // __RWUtil__h__ |