| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | //***************************************************************************** |
| 5 | // RWUtil.h |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | // Contains utility code for MD directory |
| 10 | // |
| 11 | //***************************************************************************** |
| 12 | #ifndef __RWUtil__h__ |
| 13 | #define __RWUtil__h__ |
| 14 | |
| 15 | class UTSemReadWrite; |
| 16 | |
| 17 | #define UTF8STR(wszInput, szOutput) \ |
| 18 | do { \ |
| 19 | if ((wszInput) == NULL) \ |
| 20 | { \ |
| 21 | (szOutput) = NULL; \ |
| 22 | } \ |
| 23 | else \ |
| 24 | { \ |
| 25 | int cbBuffer = ((int)wcslen(wszInput) * 3) + 1; \ |
| 26 | (szOutput) = (char *)_alloca(cbBuffer); \ |
| 27 | Unicode2UTF((wszInput), (szOutput), cbBuffer); \ |
| 28 | } \ |
| 29 | } while (0) |
| 30 | |
| 31 | //***************************************************************************** |
| 32 | // Helper methods |
| 33 | //***************************************************************************** |
| 34 | void |
| 35 | Unicode2UTF( |
| 36 | LPCWSTR wszSrc, // The string to convert. |
| 37 | __out_ecount(cbDst) |
| 38 | LPUTF8 szDst, // Buffer for the output UTF8 string. |
| 39 | int cbDst); // Size of the buffer for UTF8 string. |
| 40 | |
| 41 | //********************************************************************* |
| 42 | // The token remap record. |
| 43 | //********************************************************************* |
| 44 | struct TOKENREC |
| 45 | { |
| 46 | mdToken m_tkFrom; // The imported token |
| 47 | bool m_isDuplicate; // Is record duplicate? This information is recorded during merge |
| 48 | bool m_isDeleted; // This information is recorded during RegMeta::ProcessFilter when we might have deleted a record |
| 49 | bool m_isFoundInImport; // This information is also recorded during RegMeta::ProcessFilter |
| 50 | mdToken m_tkTo; // The new token in the merged scope |
| 51 | |
| 52 | void SetEmpty() {m_tkFrom = m_tkTo = (mdToken) -1;} |
| 53 | BOOL IsEmpty() {return m_tkFrom == (mdToken) -1;} |
| 54 | }; |
| 55 | |
| 56 | |
| 57 | //********************************************************************* |
| 58 | // |
| 59 | // This structure keeps track on token remap for an imported scope. This map is initially sorted by from |
| 60 | // tokens. It can then become sorted by To tokens. This usually happen during PreSave remap lookup. Thus |
| 61 | // we assert if we try to look up or sort by From token. |
| 62 | // |
| 63 | //********************************************************************* |
| 64 | class MDTOKENMAP : public CDynArray<TOKENREC> |
| 65 | { |
| 66 | public: |
| 67 | |
| 68 | enum SortKind{ |
| 69 | Unsorted = 0, |
| 70 | SortByFromToken = 1, |
| 71 | SortByToToken = 2, |
| 72 | Indexed = 3, // Indexed by table/rid. Implies that strings are sorted by "From". |
| 73 | }; |
| 74 | |
| 75 | MDTOKENMAP() |
| 76 | : m_pNextMap(NULL), |
| 77 | m_pMap(NULL), |
| 78 | m_iCountTotal(0), |
| 79 | m_iCountSorted(0), |
| 80 | m_sortKind(SortByFromToken), |
| 81 | m_iCountIndexed(0) |
| 82 | #if defined(_DEBUG) |
| 83 | ,m_pImport(0) |
| 84 | #endif |
| 85 | { } |
| 86 | ~MDTOKENMAP(); |
| 87 | |
| 88 | HRESULT Init(IUnknown *pImport); |
| 89 | |
| 90 | // find a token in the tokenmap. |
| 91 | bool Find(mdToken tkFrom, TOKENREC **ppRec); |
| 92 | |
| 93 | // remap a token. We assert if we don't find the tkFind in the table |
| 94 | HRESULT Remap(mdToken tkFrom, mdToken *ptkTo); |
| 95 | |
| 96 | // Insert a record. This function will keep the inserted record in a sorted sequence |
| 97 | HRESULT InsertNotFound(mdToken tkFrom, bool fDuplicate, mdToken tkTo, TOKENREC **ppRec); |
| 98 | |
| 99 | // This function will just append the record to the end of the list |
| 100 | HRESULT AppendRecord( |
| 101 | mdToken tkFrom, |
| 102 | bool fDuplicate, |
| 103 | mdToken tkTo, |
| 104 | TOKENREC **ppRec); |
| 105 | |
| 106 | // This is a safe remap. *tpkTo will be tkFind if we cannot find tkFind in the lookup table. |
| 107 | mdToken SafeRemap(mdToken tkFrom); // [IN] the token value to find |
| 108 | |
| 109 | bool FindWithToToken( |
| 110 | mdToken tkFind, // [IN] the token value to find |
| 111 | int *piPosition); // [OUT] return the first from-token that has the matching to-token |
| 112 | |
| 113 | FORCEINLINE void SortTokensByFromToken() |
| 114 | { |
| 115 | _ASSERTE(m_sortKind == SortByFromToken || m_sortKind == Indexed); |
| 116 | // Only sort if there are unsorted records. |
| 117 | if (m_iCountSorted < m_iCountTotal) |
| 118 | { |
| 119 | SortRangeFromToken(m_iCountIndexed, m_iCountIndexed+m_iCountTotal - 1); |
| 120 | m_iCountSorted = m_iCountTotal; |
| 121 | } |
| 122 | } // void MDTOKENMAP::SortTokensByFromToken() |
| 123 | |
| 124 | HRESULT EmptyMap(); |
| 125 | |
| 126 | void SortTokensByToToken(); |
| 127 | |
| 128 | MDTOKENMAP *m_pNextMap; |
| 129 | IMapToken *m_pMap; |
| 130 | |
| 131 | private: |
| 132 | FORCEINLINE int CompareFromToken( // -1, 0, or 1 |
| 133 | int iLeft, // First item to compare. |
| 134 | int iRight) // Second item to compare. |
| 135 | { |
| 136 | if ( Get(iLeft)->m_tkFrom < Get(iRight)->m_tkFrom ) |
| 137 | return -1; |
| 138 | if ( Get(iLeft)->m_tkFrom == Get(iRight)->m_tkFrom ) |
| 139 | return 0; |
| 140 | return 1; |
| 141 | } |
| 142 | |
| 143 | FORCEINLINE int CompareToToken( // -1, 0, or 1 |
| 144 | int iLeft, // First item to compare. |
| 145 | int iRight) // Second item to compare. |
| 146 | { |
| 147 | if ( Get(iLeft)->m_tkTo < Get(iRight)->m_tkTo ) |
| 148 | return -1; |
| 149 | if ( Get(iLeft)->m_tkTo == Get(iRight)->m_tkTo ) |
| 150 | return 0; |
| 151 | return 1; |
| 152 | } |
| 153 | |
| 154 | FORCEINLINE void Swap( |
| 155 | int iFirst, |
| 156 | int iSecond) |
| 157 | { |
| 158 | if ( iFirst == iSecond ) return; |
| 159 | memcpy( &m_buf, Get(iFirst), sizeof(TOKENREC) ); |
| 160 | memcpy( Get(iFirst), Get(iSecond),sizeof(TOKENREC) ); |
| 161 | memcpy( Get(iSecond), &m_buf, sizeof(TOKENREC) ); |
| 162 | } |
| 163 | |
| 164 | void SortRangeFromToken(int iLeft, int iRight); |
| 165 | void SortRangeToToken(int iLeft, int iRight); |
| 166 | |
| 167 | TOKENREC m_buf; |
| 168 | ULONG m_iCountTotal; // total entry in the map |
| 169 | ULONG m_iCountSorted; // number of entries that are sorted |
| 170 | |
| 171 | SortKind m_sortKind; |
| 172 | |
| 173 | ULONG m_TableOffset[TBL_COUNT+1]; // Start of each table in map. |
| 174 | ULONG m_iCountIndexed; // number of entries that are indexed. |
| 175 | #if defined(_DEBUG) |
| 176 | IMetaDataImport *m_pImport; // For data validation. |
| 177 | #endif |
| 178 | }; |
| 179 | |
| 180 | |
| 181 | |
| 182 | //********************************************************************* |
| 183 | // |
| 184 | // This CMapToken class implemented the IMapToken. It is used in RegMeta for |
| 185 | // filter process. This class can track all of the tokens are mapped. It also |
| 186 | // supplies a Find function. |
| 187 | // |
| 188 | //********************************************************************* |
| 189 | class CMapToken : public IMapToken |
| 190 | { |
| 191 | friend class RegMeta; |
| 192 | |
| 193 | public: |
| 194 | STDMETHODIMP QueryInterface(REFIID riid, PVOID *pp); |
| 195 | STDMETHODIMP_(ULONG) AddRef(); |
| 196 | STDMETHODIMP_(ULONG) Release(); |
| 197 | STDMETHODIMP Map(mdToken tkImp, mdToken tkEmit); |
| 198 | bool Find(mdToken tkFrom, TOKENREC **pRecTo); |
| 199 | CMapToken(); |
| 200 | virtual ~CMapToken(); |
| 201 | MDTOKENMAP *m_pTKMap; |
| 202 | private: |
| 203 | LONG m_cRef; |
| 204 | bool m_isSorted; |
| 205 | }; |
| 206 | |
| 207 | typedef CDynArray<mdToken> TOKENMAP; |
| 208 | |
| 209 | //********************************************************************* |
| 210 | // |
| 211 | // This class records all sorts of token movement during optimization phase. |
| 212 | // This including Ref to Def optimization. This also includes token movement |
| 213 | // due to sorting or eleminating the pointer tables. |
| 214 | // |
| 215 | //********************************************************************* |
| 216 | class TokenRemapManager |
| 217 | { |
| 218 | public: |
| 219 | //********************************************************************* |
| 220 | // |
| 221 | // This function is called when a TypeRef is resolved to a TypeDef. |
| 222 | // |
| 223 | //********************************************************************* |
| 224 | FORCEINLINE void RecordTypeRefToTypeDefOptimization( |
| 225 | mdToken tkFrom, |
| 226 | mdToken tkTo) |
| 227 | { |
| 228 | _ASSERTE( TypeFromToken(tkFrom) == mdtTypeRef ); |
| 229 | _ASSERTE( TypeFromToken(tkTo) == mdtTypeDef ); |
| 230 | |
| 231 | m_TypeRefToTypeDefMap[RidFromToken(tkFrom)] = tkTo; |
| 232 | } // RecordTypeRefToTypeDefOptimization |
| 233 | |
| 234 | |
| 235 | //********************************************************************* |
| 236 | // |
| 237 | // This function is called when a MemberRef is resolved to a MethodDef or FieldDef. |
| 238 | // |
| 239 | //********************************************************************* |
| 240 | FORCEINLINE void RecordMemberRefToMemberDefOptimization( |
| 241 | mdToken tkFrom, |
| 242 | mdToken tkTo) |
| 243 | { |
| 244 | _ASSERTE( TypeFromToken(tkFrom) == mdtMemberRef ); |
| 245 | _ASSERTE( TypeFromToken(tkTo) == mdtMethodDef || TypeFromToken(tkTo) == mdtFieldDef); |
| 246 | |
| 247 | m_MemberRefToMemberDefMap[RidFromToken(tkFrom)] = tkTo; |
| 248 | } // RecordMemberRefToMemberDefOptimization |
| 249 | |
| 250 | //********************************************************************* |
| 251 | // |
| 252 | // This function is called when the token kind does not change but token |
| 253 | // is moved. For example, when we sort CustomAttribute table or when we optimize |
| 254 | // away MethodPtr table. These operation will not change the token type. |
| 255 | // |
| 256 | //********************************************************************* |
| 257 | FORCEINLINE HRESULT RecordTokenMovement( |
| 258 | mdToken tkFrom, |
| 259 | mdToken tkTo) |
| 260 | { |
| 261 | TOKENREC *pTokenRec; |
| 262 | |
| 263 | _ASSERTE( TypeFromToken(tkFrom) == TypeFromToken(tkTo) ); |
| 264 | return m_TKMap.AppendRecord( tkFrom, false, tkTo, &pTokenRec ); |
| 265 | } // RecordTokenMovement |
| 266 | |
| 267 | bool ResolveRefToDef( |
| 268 | mdToken tkRef, // [IN] ref token |
| 269 | mdToken *ptkDef); // [OUT] def token that it resolves to. If it does not resolve to a def |
| 270 | |
| 271 | FORCEINLINE TOKENMAP *GetTypeRefToTypeDefMap() { return &m_TypeRefToTypeDefMap; } |
| 272 | FORCEINLINE TOKENMAP *GetMemberRefToMemberDefMap() { return &m_MemberRefToMemberDefMap; } |
| 273 | FORCEINLINE MDTOKENMAP *GetTokenMovementMap() { return &m_TKMap; } |
| 274 | |
| 275 | ~TokenRemapManager(); |
| 276 | HRESULT ClearAndEnsureCapacity(ULONG cTypeRef, ULONG cMemberRef); |
| 277 | private: |
| 278 | MDTOKENMAP m_TKMap; |
| 279 | TOKENMAP m_TypeRefToTypeDefMap; |
| 280 | TOKENMAP m_MemberRefToMemberDefMap; |
| 281 | }; // class TokenRemapManager |
| 282 | |
| 283 | // value that can be set by SetOption APIs |
| 284 | struct OptionValue |
| 285 | { |
| 286 | CorCheckDuplicatesFor m_DupCheck; // Bit Map for checking duplicates during emit. |
| 287 | CorRefToDefCheck m_RefToDefCheck; // Bit Map for specifying whether to do a ref to def optimization. |
| 288 | CorNotificationForTokenMovement m_NotifyRemap; // Bit Map for token remap notification. |
| 289 | ULONG m_UpdateMode; // (CorSetENC) Specifies whether ENC or Extension mode is on. |
| 290 | CorErrorIfEmitOutOfOrder m_ErrorIfEmitOutOfOrder; // Do not generate pointer tables |
| 291 | CorThreadSafetyOptions m_ThreadSafetyOptions; // specify if thread safety is turn on or not. |
| 292 | CorImportOptions m_ImportOption; // import options such as to skip over deleted items or not |
| 293 | CorLinkerOptions m_LinkerOption; // Linker option. Currently only used in UnmarkAll |
| 294 | BOOL m_GenerateTCEAdapters; // Do not generate the TCE adapters for COM CPC. |
| 295 | LPSTR m_RuntimeVersion; // CLR Version stamp |
| 296 | MetadataVersion m_MetadataVersion; // Version of the metadata to emit |
| 297 | MergeFlags m_MergeOptions; // Options to pass to the merger |
| 298 | UINT32 m_InitialSize; // Initial size of MetaData with values: code:CorMetaDataInitialSize. |
| 299 | CorLocalRefPreservation m_LocalRefPreservation; // Preserve module-local refs instead of optimizing them to defs |
| 300 | }; // struct OptionValue |
| 301 | |
| 302 | //********************************************************************* |
| 303 | // |
| 304 | // Helper class to ensure calling UTSemReadWrite correctly. |
| 305 | // The destructor will call the correct UnlockRead or UnlockWrite depends what lock it is holding. |
| 306 | // User should use macro defined in below instead of calling functions on this class directly. |
| 307 | // They are LOCKREAD(), LOCKWRITE(), and CONVERT_READ_TO_WRITE_LOCK. |
| 308 | // |
| 309 | //********************************************************************* |
| 310 | class CMDSemReadWrite |
| 311 | { |
| 312 | public: |
| 313 | CMDSemReadWrite(UTSemReadWrite *pSem); |
| 314 | ~CMDSemReadWrite(); |
| 315 | HRESULT LockRead(); |
| 316 | HRESULT LockWrite(); |
| 317 | void UnlockWrite(); |
| 318 | HRESULT ConvertReadLockToWriteLock(); |
| 319 | private: |
| 320 | bool m_fLockedForRead; |
| 321 | bool m_fLockedForWrite; |
| 322 | UTSemReadWrite *m_pSem; |
| 323 | }; |
| 324 | |
| 325 | |
| 326 | #define LOCKREADIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\ |
| 327 | IfFailRet(cSem.LockRead()); |
| 328 | #define LOCKWRITEIFFAILRET() CMDSemReadWrite cSem(m_pSemReadWrite);\ |
| 329 | IfFailRet(cSem.LockWrite()); |
| 330 | |
| 331 | #define LOCKREADNORET() CMDSemReadWrite cSem(m_pSemReadWrite);\ |
| 332 | hr = cSem.LockRead(); |
| 333 | #define LOCKWRITENORET() CMDSemReadWrite cSem(m_pSemReadWrite);\ |
| 334 | hr = cSem.LockWrite(); |
| 335 | |
| 336 | #define LOCKREAD() CMDSemReadWrite cSem(m_pSemReadWrite);\ |
| 337 | IfFailGo(cSem.LockRead()); |
| 338 | #define LOCKWRITE() CMDSemReadWrite cSem(m_pSemReadWrite);\ |
| 339 | IfFailGo(cSem.LockWrite()); |
| 340 | |
| 341 | #define UNLOCKWRITE() cSem.UnlockWrite(); |
| 342 | #define CONVERT_READ_TO_WRITE_LOCK() IfFailGo(cSem.ConvertReadLockToWriteLock()); |
| 343 | |
| 344 | |
| 345 | #endif // __RWUtil__h__ |
| 346 | |