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