summaryrefslogtreecommitdiff
path: root/src/md/inc/rwutil.h
blob: 81966b7fb9b9028350df931d149afe6f36a18b3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
// 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    
};



//*********************************************************************
//
// 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__