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
15class 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//*****************************************************************************
34void
35Unicode2UTF(
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//*********************************************************************
44struct 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//*********************************************************************
64class MDTOKENMAP : public CDynArray<TOKENREC>
65{
66public:
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
131private:
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//*********************************************************************
189class CMapToken : public IMapToken
190{
191 friend class RegMeta;
192
193public:
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;
202private:
203 LONG m_cRef;
204 bool m_isSorted;
205};
206
207typedef 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//*********************************************************************
216class TokenRemapManager
217{
218public:
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);
277private:
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
284struct 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//*********************************************************************
310class CMDSemReadWrite
311{
312public:
313 CMDSemReadWrite(UTSemReadWrite *pSem);
314 ~CMDSemReadWrite();
315 HRESULT LockRead();
316 HRESULT LockWrite();
317 void UnlockWrite();
318 HRESULT ConvertReadLockToWriteLock();
319private:
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