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 | // EnCee.h |
6 | // |
7 | |
8 | // |
9 | // Defines the core VM data structures and methods for support EditAndContinue |
10 | // |
11 | // ====================================================================================== |
12 | |
13 | |
14 | #ifndef EnC_H |
15 | #define EnC_H |
16 | |
17 | #include "ceeload.h" |
18 | #include "field.h" |
19 | #include "class.h" |
20 | |
21 | #ifdef EnC_SUPPORTED |
22 | |
23 | class FieldDesc; |
24 | struct EnCAddedField; |
25 | struct EnCAddedStaticField; |
26 | class EnCFieldDesc; |
27 | class EnCEEClassData; |
28 | |
29 | typedef DPTR(EnCAddedField) PTR_EnCAddedField; |
30 | typedef DPTR(EnCAddedStaticField) PTR_EnCAddedStaticField; |
31 | typedef DPTR(EnCFieldDesc) PTR_EnCFieldDesc; |
32 | typedef DPTR(EnCEEClassData) PTR_EnCEEClassData; |
33 | |
34 | //--------------------------------------------------------------------------------------- |
35 | // |
36 | // EnCFieldDesc - A field descriptor for fields added by EnC |
37 | // |
38 | // Notes: We need to track some additional data for added fields, since they can't |
39 | // simply be glued onto existing object instances like any other field. |
40 | // |
41 | // For each field added, there is a single instance of this object tied to the type where |
42 | // the field was added. |
43 | // |
44 | class EnCFieldDesc : public FieldDesc |
45 | { |
46 | public: |
47 | // Initialize just the bare minimum necessary now. |
48 | // We'll do a proper FieldDesc initialization later when Fixup is called. |
49 | void Init( mdFieldDef token, BOOL fIsStatic); |
50 | |
51 | // Compute the address of this field for a specific object |
52 | void *GetAddress( void *o); |
53 | |
54 | // Returns true if Fixup still needs to be called |
55 | BOOL NeedsFixup() |
56 | { |
57 | LIMITED_METHOD_DAC_CONTRACT; |
58 | return m_bNeedsFixup; |
59 | } |
60 | |
61 | // Used to properly configure the FieldDesc after it has been added |
62 | // This may do things like load classes (which can trigger a GC), and so can only be |
63 | // done after the process has resumed execution. |
64 | VOID Fixup(mdFieldDef token) |
65 | { |
66 | WRAPPER_NO_CONTRACT; |
67 | EEClass::FixupFieldDescForEnC(GetEnclosingMethodTable(), this, token); |
68 | m_bNeedsFixup = FALSE; |
69 | } |
70 | |
71 | // Gets a pointer to the field's contents (assuming this is a static field) if it's |
72 | // available or NULL otherwise |
73 | EnCAddedStaticField *GetStaticFieldData(); |
74 | |
75 | // Gets a pointer to the field's contents (assuming this is a static field) if it's |
76 | // available or allocates space for it and returns the address to the allocated field |
77 | // Returns a valid address or throws OOM |
78 | EnCAddedStaticField * GetOrAllocateStaticFieldData(); |
79 | |
80 | |
81 | private: |
82 | // True if Fixup() has been called on this instance |
83 | BOOL m_bNeedsFixup; |
84 | |
85 | // For static fields, pointer to where the field value is held |
86 | PTR_EnCAddedStaticField m_pStaticFieldData; |
87 | }; |
88 | |
89 | // EnCAddedFieldElement |
90 | // A node in the linked list representing fields added to a class with EnC |
91 | typedef DPTR(struct EnCAddedFieldElement) PTR_EnCAddedFieldElement; |
92 | struct EnCAddedFieldElement |
93 | { |
94 | // Pointer to the next element in the list |
95 | PTR_EnCAddedFieldElement m_next; |
96 | |
97 | // Details about this field |
98 | EnCFieldDesc m_fieldDesc; |
99 | |
100 | // Initialize this entry. |
101 | // Basically just sets a couple fields to default values. |
102 | // We'll have to go back later and call Fixup on the fieldDesc. |
103 | void Init(mdFieldDef token, BOOL fIsStatic) |
104 | { |
105 | WRAPPER_NO_CONTRACT; |
106 | m_next = NULL; |
107 | m_fieldDesc.Init(token, fIsStatic); |
108 | } |
109 | }; |
110 | |
111 | //--------------------------------------------------------------------------------------- |
112 | // |
113 | // EnCEEClassData - EnC specific information about this class |
114 | // |
115 | class EnCEEClassData |
116 | { |
117 | public: |
118 | #ifndef DACCESS_COMPILE |
119 | // Initialize all the members |
120 | // pClass - the EEClass we're tracking EnC data for |
121 | void Init(MethodTable * pMT) |
122 | { |
123 | LIMITED_METHOD_CONTRACT; |
124 | m_pMT = pMT; |
125 | m_dwNumAddedInstanceFields = 0; |
126 | m_dwNumAddedStaticFields = 0; |
127 | m_pAddedInstanceFields = NULL; |
128 | m_pAddedStaticFields = NULL; |
129 | } |
130 | #endif |
131 | |
132 | // Adds the provided new field to the appropriate linked list and updates the appropriate count |
133 | void AddField(EnCAddedFieldElement *pAddedField); |
134 | |
135 | // Get the number of instance fields that have been added to this class. |
136 | // Since we can only add private fields, these fields can't be seen from any other class but this one. |
137 | int GetAddedInstanceFields() |
138 | { |
139 | SUPPORTS_DAC; |
140 | return m_dwNumAddedInstanceFields; |
141 | } |
142 | |
143 | // Get the number of static fields that have been added to this class. |
144 | int GetAddedStaticFields() |
145 | { |
146 | SUPPORTS_DAC; |
147 | return m_dwNumAddedStaticFields; |
148 | } |
149 | |
150 | // Get the methodtable that this EnC data refers to |
151 | MethodTable * GetMethodTable() |
152 | { |
153 | LIMITED_METHOD_DAC_CONTRACT; |
154 | return m_pMT; |
155 | } |
156 | |
157 | #ifdef DACCESS_COMPILE |
158 | void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
159 | #endif |
160 | |
161 | private: |
162 | friend class EEClass; |
163 | friend class EncApproxFieldDescIterator; |
164 | |
165 | // The class that this EnC data refers to |
166 | PTR_MethodTable m_pMT; |
167 | |
168 | // The number of instance fields that have been added to this class |
169 | int m_dwNumAddedInstanceFields; |
170 | |
171 | // The number of static fields that have been added to this class |
172 | int m_dwNumAddedStaticFields; |
173 | |
174 | // Linked list of EnCFieldDescs for all the added instance fields |
175 | PTR_EnCAddedFieldElement m_pAddedInstanceFields; |
176 | |
177 | // Linked list of EnCFieldDescs for all the added static fields |
178 | PTR_EnCAddedFieldElement m_pAddedStaticFields; |
179 | }; |
180 | |
181 | //--------------------------------------------------------------------------------------- |
182 | // |
183 | // EditAndContinueModule - specialization of the Module class which adds EnC support |
184 | // |
185 | // Assumptions: |
186 | // |
187 | // Notes: |
188 | // |
189 | class EditAndContinueModule : public Module |
190 | { |
191 | VPTR_VTABLE_CLASS(EditAndContinueModule, Module) |
192 | |
193 | // keep track of the number of changes - this is used to apply a version number |
194 | // to an updated function. The version number for a function is the overall edit count, |
195 | // ie the number of times ApplyChanges has been called, not the number of times that |
196 | // function itself has been edited. |
197 | int m_applyChangesCount; |
198 | |
199 | // Holds a table of EnCEEClassData object for classes in this module that have been modified |
200 | CUnorderedArray<EnCEEClassData*, 5> m_ClassList; |
201 | |
202 | #ifndef DACCESS_COMPILE |
203 | // Return the minimum permissable address for new IL to be stored at |
204 | // This can't be less than the current load address because then we'd |
205 | // have negative RVAs. |
206 | BYTE *GetEnCBase() { return (BYTE *) GetFile()->GetManagedFileContents(); } |
207 | #endif // DACCESS_COMPILE |
208 | |
209 | private: |
210 | // Constructor is invoked only by Module::Create |
211 | friend Module *Module::Create(Assembly *pAssembly, mdToken moduleRef, PEFile *file, AllocMemTracker *pamTracker); |
212 | EditAndContinueModule(Assembly *pAssembly, mdToken moduleRef, PEFile *file); |
213 | |
214 | protected: |
215 | #ifndef DACCESS_COMPILE |
216 | // Initialize the module |
217 | virtual void Initialize(AllocMemTracker *pamTracker); |
218 | #endif |
219 | |
220 | public: |
221 | #ifndef DACCESS_COMPILE |
222 | // Destruct the module when it's finished being unloaded |
223 | // Note that due to the loader's allocation mechanism, C++ consturctors and destructors |
224 | // wouldn't be called. |
225 | virtual void Destruct(); |
226 | #endif |
227 | |
228 | // Apply an EnC edit |
229 | HRESULT ApplyEditAndContinue(DWORD cbMetadata, |
230 | BYTE *pMetadata, |
231 | DWORD cbIL, |
232 | BYTE *pIL); |
233 | |
234 | // Called when a method has been modified (new IL) |
235 | HRESULT UpdateMethod(MethodDesc *pMethod); |
236 | |
237 | // Called when a new method has been added to the module's metadata |
238 | HRESULT AddMethod(mdMethodDef token); |
239 | |
240 | // Called when a new field has been added to the module's metadata |
241 | HRESULT AddField(mdFieldDef token); |
242 | |
243 | // JIT the new version of a function for EnC |
244 | PCODE JitUpdatedFunction(MethodDesc *pMD, T_CONTEXT *pContext); |
245 | |
246 | // Remap execution to the latest version of an edited method |
247 | HRESULT ResumeInUpdatedFunction(MethodDesc *pMD, |
248 | void *oldDebuggerFuncHandle, |
249 | SIZE_T newILOffset, |
250 | T_CONTEXT *pContext); |
251 | |
252 | // Modify the thread context for EnC remap and resume execution |
253 | void FixContextAndResume(MethodDesc *pMD, |
254 | void *oldDebuggerFuncHandle, |
255 | T_CONTEXT *pContext, |
256 | EECodeInfo *pOldCodeInfo, |
257 | EECodeInfo *pNewCodeInfo); |
258 | |
259 | // Get a pointer to the value of a field added by EnC or return NULL if it doesn't exist |
260 | PTR_CBYTE ResolveField(OBJECTREF thisPointer, |
261 | EnCFieldDesc *pFD); |
262 | |
263 | // Get a pointer to the value of a field added by EnC. Allocates if it doesn't exist, so we'll |
264 | // return a valid address or throw OOM |
265 | PTR_CBYTE ResolveOrAllocateField(OBJECTREF thisPointer, |
266 | EnCFieldDesc * pFD); |
267 | |
268 | |
269 | // Get class-specific EnC data for a class in this module |
270 | // Note: For DAC build, getOnly must be TRUE |
271 | PTR_EnCEEClassData GetEnCEEClassData(MethodTable * pMT, BOOL getOnly = FALSE); |
272 | |
273 | // Get the number of times edits have been applied to this module |
274 | int GetApplyChangesCount() |
275 | { |
276 | return m_applyChangesCount; |
277 | } |
278 | |
279 | #ifdef DACCESS_COMPILE |
280 | virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, |
281 | bool enumThis); |
282 | #endif |
283 | }; |
284 | |
285 | // Information about an instance field value added by EnC |
286 | // When an instance field is added to an object, we will lazily create an EnCAddedField |
287 | // for EACH instance of that object, but there will be a single EnCFieldDesc. |
288 | // |
289 | // Note that if we were concerned about the overhead when there are lots of instances of |
290 | // an object, we could slim this down to just the m_FieldData field by storing a pointer |
291 | // to a growable array of these in the EnCSyncBlockInfo, instead of using a linked list, and |
292 | // have the EnCFieldDesc specify a field index number. |
293 | // |
294 | struct EnCAddedField |
295 | { |
296 | // This field data hangs off the SyncBlock in a linked list. |
297 | // This is the pointer to the next field in the list. |
298 | PTR_EnCAddedField m_pNext; |
299 | |
300 | // Pointer to the fieldDesc describing which field this refers to |
301 | PTR_EnCFieldDesc m_pFieldDesc; |
302 | |
303 | // A dependent handle whose primary object points to the object instance which has been modified, |
304 | // and whose secondary object points to an EnC helper object containing a reference to the field value. |
305 | OBJECTHANDLE m_FieldData; |
306 | |
307 | // Allocates a new EnCAddedField and hook it up to the object |
308 | static EnCAddedField *Allocate(OBJECTREF thisPointer, EnCFieldDesc *pFD); |
309 | }; |
310 | |
311 | // Information about a static field value added by EnC |
312 | // We can't change the MethodTable, so these are hung off the FieldDesc |
313 | // Note that the actual size of this type is variable. |
314 | struct EnCAddedStaticField |
315 | { |
316 | // Pointer back to the fieldDesc describing which field this refers to |
317 | // This isn't strictly necessary since our callers always know it, but the overhead |
318 | // in minimal (per type, not per instance) and this is cleaner and permits an extra sanity check. |
319 | PTR_EnCFieldDesc m_pFieldDesc; |
320 | |
321 | // For primitive types, this is the beginning of the actual value. |
322 | // For reference types and user-defined value types, it's the beginning of a pointer |
323 | // to the object. |
324 | // Note that this is intentionally the last field of this structure as it is variably-sized. |
325 | // NOTE: It looks like we did the same thing for instance fields in EnCAddedField but then simplified |
326 | // it by always storing just an OBJREF which may point to a boxed value type. I suggest we do the |
327 | // same here unless we can demonstrate that the extra indirection makes a noticable perf difference |
328 | // in scenarios which are important for EnC. |
329 | BYTE m_FieldData; |
330 | |
331 | // Get a pointer to the contents of this field |
332 | PTR_CBYTE GetFieldData(); |
333 | |
334 | // Allocate a new instance appropriate for the specified field |
335 | static EnCAddedStaticField *Allocate(EnCFieldDesc *pFD); |
336 | }; |
337 | |
338 | // EnCSyncBlockInfo lives off an object's SyncBlock and contains a lazily-created linked |
339 | // list of the values of all the fields added to the object by EnC |
340 | // |
341 | // Note that much of the logic here would probably belong better in EnCAddedField since it is |
342 | // specific to the implementation there. Perhaps this should ideally just be a container |
343 | // that holds a bunch of EnCAddedFields and can iterate over them and map from EnCFieldDesc |
344 | // to them. |
345 | class EnCSyncBlockInfo |
346 | { |
347 | public: |
348 | // Initialize the list |
349 | EnCSyncBlockInfo() : |
350 | m_pList(PTR_NULL) |
351 | { |
352 | } |
353 | |
354 | // Get a pointer to the data in a specific field on this object or return NULL if it |
355 | // doesn't exist |
356 | PTR_CBYTE ResolveField(OBJECTREF thisPointer, |
357 | EnCFieldDesc * pFieldDesc); |
358 | |
359 | // Get a pointer to the data in a specific field on this object. We'll allocate if it doesn't already |
360 | // exist, so we'll only fail on OOM |
361 | PTR_CBYTE ResolveOrAllocateField(OBJECTREF thisPointer, EnCFieldDesc *pFD); |
362 | |
363 | |
364 | // Free the data used by this field value. Called after the object instance the |
365 | // fields belong to is collected. |
366 | void Cleanup(); |
367 | |
368 | private: |
369 | // Gets the address of an EnC field accounting for its type: valuetype, class or primitive |
370 | PTR_CBYTE GetEnCFieldAddrFromHelperFieldDesc(FieldDesc * pHelperFieldDesc, |
371 | OBJECTREF pHelper, |
372 | EnCFieldDesc * pFD); |
373 | |
374 | // Pointer to the head of the list |
375 | PTR_EnCAddedField m_pList; |
376 | }; |
377 | |
378 | // The DPTR is actually defined in syncblk.h to make it visible to SyncBlock |
379 | // typedef DPTR(EnCSyncBlockInfo) PTR_EnCSyncBlockInfo; |
380 | |
381 | #endif // !EnC_SUPPORTED |
382 | |
383 | |
384 | //--------------------------------------------------------------------------------------- |
385 | // |
386 | // EncApproxFieldDescIterator - Iterates through all fields of a class including ones |
387 | // added by EnC |
388 | // |
389 | // Notes: |
390 | // This is just like ApproxFieldDescIterator, but it also includes EnC fields if |
391 | // EnC is supported. |
392 | // This does not include inherited fields. |
393 | // The order the fields returned here is unspecified. |
394 | // |
395 | // We don't bother maintaining an accurate total and remaining field count like |
396 | // ApproxFieldDescIterator because none of our clients need it. But it would |
397 | // be easy to add this using the data from m_classData |
398 | // |
399 | class EncApproxFieldDescIterator |
400 | { |
401 | public: |
402 | #ifdef EnC_SUPPORTED |
403 | // Create and initialize the iterator |
404 | EncApproxFieldDescIterator(MethodTable *pMT, int iteratorType, BOOL fixupEnC); |
405 | |
406 | // Get the next fieldDesc (either EnC or non-EnC) |
407 | PTR_FieldDesc Next(); |
408 | |
409 | #else |
410 | // Non-EnC version - simple wrapper |
411 | EncApproxFieldDescIterator(MethodTable *pMT, int iteratorType, BOOL fixupEnC) : |
412 | m_nonEnCIter( pMT, iteratorType ) {} |
413 | |
414 | PTR_FieldDesc Next() { WRAPPER_NO_CONTRACT; return m_nonEnCIter.Next(); } |
415 | #endif // EnC_SUPPORTED |
416 | |
417 | int GetIteratorType() |
418 | { |
419 | LIMITED_METHOD_CONTRACT; |
420 | SUPPORTS_DAC; |
421 | return m_nonEnCIter.GetIteratorType(); |
422 | } |
423 | |
424 | private: |
425 | // The iterator for the non-EnC fields. |
426 | // We delegate to this for alll non-EnC specific stuff |
427 | ApproxFieldDescIterator m_nonEnCIter; |
428 | |
429 | #ifdef EnC_SUPPORTED |
430 | // Return the next available EnC FieldDesc or NULL when done |
431 | PTR_EnCFieldDesc NextEnC(); |
432 | |
433 | // True if our client wants us to fixup any EnC fieldDescs before handing them back |
434 | BOOL m_fixupEnC; |
435 | |
436 | // A count of how many EnC fields have been returned so far |
437 | int m_encFieldsReturned; |
438 | |
439 | // The current pointer into one of the EnC field lists when enumerating EnC fields |
440 | PTR_EnCAddedFieldElement m_pCurrListElem; |
441 | |
442 | // EnC specific data for the class of interest. |
443 | // NULL if EnC is disabled or this class doesn't have any EnC data |
444 | PTR_EnCEEClassData m_encClassData; |
445 | #endif |
446 | }; |
447 | |
448 | #endif // #ifndef EnC_H |
449 | |