summaryrefslogtreecommitdiff
path: root/src/vm/encee.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/encee.h')
-rw-r--r--src/vm/encee.h448
1 files changed, 448 insertions, 0 deletions
diff --git a/src/vm/encee.h b/src/vm/encee.h
new file mode 100644
index 0000000000..8dc96f1feb
--- /dev/null
+++ b/src/vm/encee.h
@@ -0,0 +1,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