// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. //***************************************************************************** // RWUtil.h // // // Contains utility code for MD directory // //***************************************************************************** #ifndef __RWUtil__h__ #define __RWUtil__h__ class UTSemReadWrite; #define UTF8STR(wszInput, szOutput) \ do { \ if ((wszInput) == NULL) \ { \ (szOutput) = NULL; \ } \ else \ { \ int cbBuffer = ((int)wcslen(wszInput) * 3) + 1; \ (szOutput) = (char *)_alloca(cbBuffer); \ Unicode2UTF((wszInput), (szOutput), cbBuffer); \ } \ } while (0) //***************************************************************************** // Helper methods //***************************************************************************** void Unicode2UTF( LPCWSTR wszSrc, // The string to convert. __out_ecount(cbDst) LPUTF8 szDst, // Buffer for the output UTF8 string. int cbDst); // Size of the buffer for UTF8 string. //********************************************************************* // The token remap record. //********************************************************************* struct TOKENREC { mdToken m_tkFrom; // The imported token bool m_isDuplicate; // Is record duplicate? This information is recorded during merge bool m_isDeleted; // This information is recorded during RegMeta::ProcessFilter when we might have deleted a record bool m_isFoundInImport; // This information is also recorded during RegMeta::ProcessFilter mdToken m_tkTo; // The new token in the merged scope void SetEmpty() {m_tkFrom = m_tkTo = (mdToken) -1;} BOOL IsEmpty() {return m_tkFrom == (mdToken) -1;} }; //********************************************************************* // // This structure keeps track on token remap for an imported scope. This map is initially sorted by from // tokens. It can then become sorted by To tokens. This usually happen during PreSave remap lookup. Thus // we assert if we try to look up or sort by From token. // //********************************************************************* class MDTOKENMAP : public CDynArray { public: enum SortKind{ Unsorted = 0, SortByFromToken = 1, SortByToToken = 2, Indexed = 3, // Indexed by table/rid. Implies that strings are sorted by "From". }; MDTOKENMAP() : m_pNextMap(NULL), m_pMap(NULL), m_iCountTotal(0), m_iCountSorted(0), m_sortKind(SortByFromToken), m_iCountIndexed(0) #if defined(_DEBUG) ,m_pImport(0) #endif { } ~MDTOKENMAP(); HRESULT Init(IUnknown *pImport); // find a token in the tokenmap. bool Find(mdToken tkFrom, TOKENREC **ppRec); // remap a token. We assert if we don't find the tkFind in the table HRESULT Remap(mdToken tkFrom, mdToken *ptkTo); // Insert a record. This function will keep the inserted record in a sorted sequence HRESULT InsertNotFound(mdToken tkFrom, bool fDuplicate, mdToken tkTo, TOKENREC **ppRec); // This function will just append the record to the end of the list HRESULT AppendRecord( mdToken tkFrom, bool fDuplicate, mdToken tkTo, TOKENREC **ppRec); // This is a safe remap. *tpkTo will be tkFind if we cannot find tkFind in the lookup table. mdToken SafeRemap(mdToken tkFrom); // [IN] the token value to find bool FindWithToToken( mdToken tkFind, // [IN] the token value to find int *piPosition); // [OUT] return the first from-token that has the matching to-token FORCEINLINE void SortTokensByFromToken() { _ASSERTE(m_sortKind == SortByFromToken || m_sortKind == Indexed); // Only sort if there are unsorted records. if (m_iCountSorted < m_iCountTotal) { SortRangeFromToken(m_iCountIndexed, m_iCountIndexed+m_iCountTotal - 1); m_iCountSorted = m_iCountTotal; } } // void MDTOKENMAP::SortTokensByFromToken() HRESULT EmptyMap(); void SortTokensByToToken(); MDTOKENMAP *m_pNextMap; IMapToken *m_pMap; private: FORCEINLINE int CompareFromToken( // -1, 0, or 1 int iLeft, // First item to compare. int iRight) // Second item to compare. { if ( Get(iLeft)->m_tkFrom < Get(iRight)->m_tkFrom ) return -1; if ( Get(iLeft)->m_tkFrom == Get(iRight)->m_tkFrom ) return 0; return 1; } FORCEINLINE int CompareToToken( // -1, 0, or 1 int iLeft, // First item to compare. int iRight) // Second item to compare. { if ( Get(iLeft)->m_tkTo < Get(iRight)->m_tkTo ) return -1; if ( Get(iLeft)->m_tkTo == Get(iRight)->m_tkTo ) return 0; return 1; } FORCEINLINE void Swap( int iFirst, int iSecond) { if ( iFirst == iSecond ) return; memcpy( &m_buf, Get(iFirst), sizeof(TOKENREC) ); memcpy( Get(iFirst), Get(iSecond),sizeof(TOKENREC) ); memcpy( Get(iSecond), &m_buf, sizeof(TOKENREC) ); } void SortRangeFromToken(int iLeft, int iRight); void SortRangeToToken(int iLeft, int iRight); TOKENREC m_buf; ULONG m_iCountTotal; // total entry in the map ULONG m_iCountSorted; // number of entries that are sorted SortKind m_sortKind; ULONG m_TableOffset[TBL_COUNT+1]; // Start of each table in map. ULONG m_iCountIndexed; // number of entries that are indexed. #if defined(_DEBUG) IMetaDataImport *m_pImport; // For data validation. #endif }; //********************************************************************* // // This CMapToken class implemented the IMapToken. It is used in RegMeta for // filter process. This class can track all of the tokens are mapped. It also // supplies a Find function. // //********************************************************************* class CMapToken : public IMapToken { friend class RegMeta; public: STDMETHODIMP QueryInterface(REFIID riid, PVOID *pp); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP Map(mdToken tkImp, mdToken tkEmit); bool Find(mdToken tkFrom, TOKENREC **pRecTo); CMapToken(); virtual ~CMapToken(); MDTOKENMAP *m_pTKMap; private: LONG m_cRef; bool m_isSorted; }; typedef CDynArray 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__