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 | |