summaryrefslogtreecommitdiff
path: root/src/vm/encee.h
blob: 8dc96f1feb42fa59523a74784a0c7fa8b0411628 (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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
// 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.
//
// EnCee.h
//

//
// Defines the core VM data structures and methods for support EditAndContinue
// 
// ======================================================================================


#ifndef EnC_H 
#define EnC_H

#include "ceeload.h"
#include "field.h"
#include "class.h"

#ifdef EnC_SUPPORTED

class FieldDesc;
struct EnCAddedField;
struct EnCAddedStaticField;
class EnCFieldDesc;
class EnCEEClassData;

typedef DPTR(EnCAddedField) PTR_EnCAddedField;
typedef DPTR(EnCAddedStaticField) PTR_EnCAddedStaticField;
typedef DPTR(EnCFieldDesc) PTR_EnCFieldDesc;
typedef DPTR(EnCEEClassData) PTR_EnCEEClassData;

//---------------------------------------------------------------------------------------
//
// EnCFieldDesc - A field descriptor for fields added by EnC
//
// Notes: We need to track some additional data for added fields, since they can't 
// simply be glued onto existing object instances like any other field.
//
// For each field added, there is a single instance of this object tied to the type where
// the field was added.  
//
class EnCFieldDesc : public FieldDesc 
{
public:
    // Initialize just the bare minimum necessary now.  
    // We'll do a proper FieldDesc initialization later when Fixup is called.
    void Init( mdFieldDef token, BOOL fIsStatic);

    // Compute the address of this field for a specific object
    void *GetAddress( void *o);
    
    // Returns true if Fixup still needs to be called
    BOOL NeedsFixup() 
    {
        LIMITED_METHOD_DAC_CONTRACT;
        return m_bNeedsFixup;
    }

    // Used to properly configure the FieldDesc after it has been added
    // This may do things like load classes (which can trigger a GC), and so can only be 
    // done after the process has resumed execution.
    VOID Fixup(mdFieldDef token) 
    {
        WRAPPER_NO_CONTRACT;
        EEClass::FixupFieldDescForEnC(GetEnclosingMethodTable(), this, token);
        m_bNeedsFixup = FALSE;
    }

    // Gets a pointer to the field's contents (assuming this is a static field) if it's
    // available or NULL otherwise
    EnCAddedStaticField *GetStaticFieldData();

    // Gets a pointer to the field's contents (assuming this is a static field) if it's
    // available or allocates space for it and returns the address to the allocated field
    // Returns a valid address or throws OOM
    EnCAddedStaticField * GetOrAllocateStaticFieldData();


private:
    // True if Fixup() has been called on this instance
    BOOL m_bNeedsFixup;

    // For static fields, pointer to where the field value is held
    PTR_EnCAddedStaticField m_pStaticFieldData;  
};

// EnCAddedFieldElement 
// A node in the linked list representing fields added to a class with EnC
typedef DPTR(struct EnCAddedFieldElement) PTR_EnCAddedFieldElement;
struct EnCAddedFieldElement 
{
    // Pointer to the next element in the list
    PTR_EnCAddedFieldElement m_next;

    // Details about this field 
    EnCFieldDesc m_fieldDesc;

    // Initialize this entry.  
    // Basically just sets a couple fields to default values.
    // We'll have to go back later and call Fixup on the fieldDesc.
    void Init(mdFieldDef token, BOOL fIsStatic) 
    {
        WRAPPER_NO_CONTRACT;
        m_next = NULL;
        m_fieldDesc.Init(token, fIsStatic);
    }
};

//---------------------------------------------------------------------------------------
//
// EnCEEClassData - EnC specific information about this class
//
class EnCEEClassData
{
public:
#ifndef DACCESS_COMPILE
    // Initialize all the members
    //  pClass - the EEClass we're tracking EnC data for
    void Init(MethodTable * pMT) 
    {
        LIMITED_METHOD_CONTRACT;
        m_pMT = pMT;
        m_dwNumAddedInstanceFields = 0;
        m_dwNumAddedStaticFields = 0;
        m_pAddedInstanceFields = NULL;
        m_pAddedStaticFields = NULL;
    }
#endif
    
    // Adds the provided new field to the appropriate linked list and updates the appropriate count
    void AddField(EnCAddedFieldElement *pAddedField);

    // Get the number of instance fields that have been added to this class. 
    // Since we can only add private fields, these fields can't be seen from any other class but this one.
    int GetAddedInstanceFields()
    {
        SUPPORTS_DAC;
        return m_dwNumAddedInstanceFields;
    }
    
    // Get the number of static fields that have been added to this class.
    int GetAddedStaticFields()
    {
        SUPPORTS_DAC;
        return m_dwNumAddedStaticFields;
    }        
    
    // Get the methodtable that this EnC data refers to
    MethodTable * GetMethodTable() 
    {
        LIMITED_METHOD_DAC_CONTRACT;
        return m_pMT;
    }

#ifdef DACCESS_COMPILE
    void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
#endif

private:
    friend class EEClass;
    friend class EncApproxFieldDescIterator;

    // The class that this EnC data refers to
    PTR_MethodTable       m_pMT;

    // The number of instance fields that have been added to this class
    int                   m_dwNumAddedInstanceFields;

    // The number of static fields that have been added to this class
    int                   m_dwNumAddedStaticFields;

    // Linked list of EnCFieldDescs for all the added instance fields 
    PTR_EnCAddedFieldElement m_pAddedInstanceFields;

    // Linked list of EnCFieldDescs for all the added static fields 
    PTR_EnCAddedFieldElement m_pAddedStaticFields;
};

//---------------------------------------------------------------------------------------
//
// EditAndContinueModule - specialization of the Module class which adds EnC support
//
// Assumptions:
//
// Notes:
//
class EditAndContinueModule : public Module 
{
    VPTR_VTABLE_CLASS(EditAndContinueModule, Module)
    
    // keep track of the number of changes - this is used to apply a version number
    // to an updated function. The version number for a function is the overall edit count,
    // ie the number of times ApplyChanges has been called, not the number of times that
    // function itself has been edited.
    int m_applyChangesCount;

    // Holds a table of EnCEEClassData object for classes in this module that have been modified
    CUnorderedArray<EnCEEClassData*, 5> m_ClassList;

#ifndef DACCESS_COMPILE
    // Return the minimum permissable address for new IL to be stored at
    // This can't be less than the current load address because then we'd
    // have negative RVAs.
    BYTE *GetEnCBase() { return (BYTE *) GetFile()->GetManagedFileContents(); }
#endif // DACCESS_COMPILE

private:
    // Constructor is invoked only by Module::Create
    friend Module *Module::Create(Assembly *pAssembly, mdToken moduleRef, PEFile *file, AllocMemTracker *pamTracker);
    EditAndContinueModule(Assembly *pAssembly, mdToken moduleRef, PEFile *file);

protected:
#ifndef DACCESS_COMPILE
    // Initialize the module
    virtual void Initialize(AllocMemTracker *pamTracker);
#endif

public:
#ifndef DACCESS_COMPILE
    // Destruct the module when it's finished being unloaded
    // Note that due to the loader's allocation mechanism, C++ consturctors and destructors
    // wouldn't be called.
    virtual void Destruct(); 
#endif
    
    // Apply an EnC edit
    HRESULT ApplyEditAndContinue(DWORD cbMetadata,
                            BYTE *pMetadata,
                            DWORD cbIL,
                            BYTE *pIL);

    // Called when a method has been modified (new IL)
    HRESULT UpdateMethod(MethodDesc *pMethod);

    // Called when a new method has been added to the module's metadata
    HRESULT AddMethod(mdMethodDef token);

    // Called when a new field has been added to the module's metadata
    HRESULT AddField(mdFieldDef token);

    // JIT the new version of a function for EnC
    PCODE JitUpdatedFunction(MethodDesc *pMD, T_CONTEXT *pContext);
    
    // Remap execution to the latest version of an edited method
    HRESULT ResumeInUpdatedFunction(MethodDesc *pMD,
                                    void *oldDebuggerFuncHandle,
                                    SIZE_T newILOffset,
                                    T_CONTEXT *pContext);

    // Modify the thread context for EnC remap and resume execution
    void FixContextAndResume(MethodDesc *pMD, 
                             void *oldDebuggerFuncHandle,
                             T_CONTEXT *pContext,
                             EECodeInfo *pOldCodeInfo,
                             EECodeInfo *pNewCodeInfo);
    
    // Get a pointer to the value of a field added by EnC or return NULL if it doesn't exist
    PTR_CBYTE ResolveField(OBJECTREF thisPointer, 
                           EnCFieldDesc *pFD);

    // Get a pointer to the value of a field added by EnC. Allocates if it doesn't exist, so we'll
    // return a valid address or throw OOM
    PTR_CBYTE ResolveOrAllocateField(OBJECTREF      thisPointer, 
                                     EnCFieldDesc * pFD);


    // Get class-specific EnC data for a class in this module
    // Note: For DAC build, getOnly must be TRUE
    PTR_EnCEEClassData GetEnCEEClassData(MethodTable * pMT, BOOL getOnly = FALSE);

    // Get the number of times edits have been applied to this module
    int GetApplyChangesCount()
    {
        return m_applyChangesCount;
    }

#ifdef DACCESS_COMPILE
    virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
                                   bool enumThis);
#endif
};

// Information about an instance field value added by EnC
// When an instance field is added to an object, we will lazily create an EnCAddedField
// for EACH instance of that object, but there will be a single EnCFieldDesc.
//
// Note that if we were concerned about the overhead when there are lots of instances of
// an object, we could slim this down to just the m_FieldData field by storing a pointer
// to a growable array of these in the EnCSyncBlockInfo, instead of using a linked list, and
// have the EnCFieldDesc specify a field index number.
//
struct EnCAddedField 
{
    // This field data hangs off the SyncBlock in a linked list.  
    // This is the pointer to the next field in the list.
    PTR_EnCAddedField m_pNext;

    // Pointer to the fieldDesc describing which field this refers to
    PTR_EnCFieldDesc m_pFieldDesc;

    // A dependent handle whose primary object points to the object instance which has been modified,
    // and whose secondary object points to an EnC helper object containing a reference to the field value.
    OBJECTHANDLE m_FieldData;

    // Allocates a new EnCAddedField and hook it up to the object
    static EnCAddedField *Allocate(OBJECTREF thisPointer, EnCFieldDesc *pFD);
};

// Information about a static field value added by EnC
// We can't change the MethodTable, so these are hung off the FieldDesc
// Note that the actual size of this type is variable.
struct EnCAddedStaticField 
{
    // Pointer back to the fieldDesc describing which field this refers to
    // This isn't strictly necessary since our callers always know it, but the overhead
    // in minimal (per type, not per instance) and this is cleaner and permits an extra sanity check.
    PTR_EnCFieldDesc m_pFieldDesc;

    // For primitive types, this is the beginning of the actual value.
    // For reference types and user-defined value types, it's the beginning of a pointer
    // to the object.
    // Note that this is intentionally the last field of this structure as it is variably-sized.
    // NOTE: It looks like we did the same thing for instance fields in EnCAddedField but then simplified
    // it by always storing just an OBJREF which may point to a boxed value type.  I suggest we do the
    // same here unless we can demonstrate that the extra indirection makes a noticable perf difference
    // in scenarios which are important for EnC.
    BYTE m_FieldData;

    // Get a pointer to the contents of this field
    PTR_CBYTE GetFieldData();

    // Allocate a new instance appropriate for the specified field
    static EnCAddedStaticField *Allocate(EnCFieldDesc *pFD);
};

// EnCSyncBlockInfo lives off an object's SyncBlock and contains a lazily-created linked 
// list of the values of all the fields added to the object by EnC
//
// Note that much of the logic here would probably belong better in EnCAddedField since it is 
// specific to the implementation there.  Perhaps this should ideally just be a container
// that holds a bunch of EnCAddedFields and can iterate over them and map from EnCFieldDesc
// to them.  
class EnCSyncBlockInfo 
{
public:
    // Initialize the list
    EnCSyncBlockInfo() : 
        m_pList(PTR_NULL) 
    {
    }
    
    // Get a pointer to the data in a specific field on this object or return NULL if it
    // doesn't exist
    PTR_CBYTE ResolveField(OBJECTREF      thisPointer, 
                           EnCFieldDesc * pFieldDesc);

    // Get a pointer to the data in a specific field on this object. We'll allocate if it doesn't already
    // exist, so we'll only fail on OOM
    PTR_CBYTE ResolveOrAllocateField(OBJECTREF thisPointer, EnCFieldDesc *pFD);


    // Free the data used by this field value.  Called after the object instance the 
    // fields belong to is collected.
    void Cleanup();

private:
    // Gets the address of an EnC field accounting for its type: valuetype, class or primitive
    PTR_CBYTE GetEnCFieldAddrFromHelperFieldDesc(FieldDesc *    pHelperFieldDesc, 
                                                 OBJECTREF      pHelper, 
                                                 EnCFieldDesc * pFD);

    // Pointer to the head of the list
    PTR_EnCAddedField m_pList;
};

// The DPTR is actually defined in syncblk.h to make it visible to SyncBlock 
// typedef DPTR(EnCSyncBlockInfo) PTR_EnCSyncBlockInfo;

#endif // !EnC_SUPPORTED


//---------------------------------------------------------------------------------------
//
// EncApproxFieldDescIterator - Iterates through all fields of a class including ones
//  added by EnC
//
// Notes:
//    This is just like ApproxFieldDescIterator, but it also includes EnC fields if
//    EnC is supported.
//    This does not include inherited fields.
//    The order the fields returned here is unspecified.
//
//    We don't bother maintaining an accurate total and remaining field count like
//    ApproxFieldDescIterator because none of our clients need it.  But it would
//    be easy to add this using the data from m_classData
//
class EncApproxFieldDescIterator
{
public:
#ifdef EnC_SUPPORTED
    // Create and initialize the iterator
    EncApproxFieldDescIterator(MethodTable *pMT, int iteratorType, BOOL fixupEnC); 

    // Get the next fieldDesc (either EnC or non-EnC)
    PTR_FieldDesc Next();

#else
    // Non-EnC version - simple wrapper
    EncApproxFieldDescIterator(MethodTable *pMT, int iteratorType, BOOL fixupEnC) :
      m_nonEnCIter( pMT, iteratorType ) {}

    PTR_FieldDesc Next() { WRAPPER_NO_CONTRACT; return m_nonEnCIter.Next(); }
#endif // EnC_SUPPORTED

    int GetIteratorType() 
    {
        LIMITED_METHOD_CONTRACT;
        SUPPORTS_DAC;
        return m_nonEnCIter.GetIteratorType();
    }

private:
    // The iterator for the non-EnC fields.
    // We delegate to this for alll non-EnC specific stuff
    ApproxFieldDescIterator m_nonEnCIter;

#ifdef EnC_SUPPORTED
    // Return the next available EnC FieldDesc or NULL when done
    PTR_EnCFieldDesc NextEnC();

    // True if our client wants us to fixup any EnC fieldDescs before handing them back
    BOOL m_fixupEnC;

    // A count of how many EnC fields have been returned so far
    int m_encFieldsReturned;

    // The current pointer into one of the EnC field lists when enumerating EnC fields
    PTR_EnCAddedFieldElement m_pCurrListElem;
    
    // EnC specific data for the class of interest.  
    // NULL if EnC is disabled or this class doesn't have any EnC data
    PTR_EnCEEClassData m_encClassData;
#endif
};

#endif // #ifndef EnC_H