Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions docs/design/datacontracts/RuntimeMutableTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This contract exposes runtime type system information about changes that occurre
```csharp
IEnumerable<TargetPointer> EnumerateAddedFieldDescs(TypeHandle typeHandle, bool staticFields);
bool IsFieldDescEnCNew(TargetPointer fieldDescPointer);
bool DoesEnCFieldDescNeedFixup(TargetPointer encFieldDescPointer);
TargetPointer GetEnCStaticFieldDataAddress(TargetPointer encFieldDescPointer);
TargetPointer GetEnCInstanceFieldAddress(TargetPointer objectAddress, TargetPointer encFieldDescPointer);
```

## Version 1
Expand All @@ -23,7 +26,17 @@ bool IsFieldDescEnCNew(TargetPointer fieldDescPointer);
| `EnCEEClassData` | `AddedStaticFields` | Head of the linked list of `EnCAddedFieldElement` for added static fields. |
| `EnCAddedFieldElement` | `Next` | Pointer to the next `EnCAddedFieldElement` in the linked list. |
| `EnCAddedFieldElement` | `FieldDesc` | Address of the embedded `EnCFieldDesc` (layout-compatible with `FieldDesc`). |
| `EnCFieldDesc` | `NeedsFixup` | Non-zero when the `EnCFieldDesc` still needs fixup (i.e., it has not been fully initialized). |
| `EnCFieldDesc` | `StaticFieldData` | Pointer to the `EnCAddedStaticField` that backs this static field (NULL until storage has been allocated). |
| `EnCAddedStaticField` | `FieldDesc` | Pointer back to the `EnCFieldDesc` that owns this storage. |
| `EnCAddedStaticField` | `FieldData` | Address of the first byte of static field storage on this entry. |
| `EnCAddedField` | `Next` | Pointer to the next `EnCAddedField` entry in the per-object linked list hanging off the SyncBlock. |
| `EnCAddedField` | `FieldDesc` | Pointer to the `EnCFieldDesc` for the added instance field. |
| `EnCAddedField` | `FieldData` | The dependent-handle pair (typed as `ObjectHandle`) whose primary OBJECTREF is the dependency anchor and whose secondary OBJECTREF is the `System.Diagnostics.EditAndContinueHelper` instance. |
| `EnCSyncBlockInfo` | `List` | Head of the linked list of `EnCAddedField` entries for the EnC-added instance fields associated with an object. |
| `SyncBlock` | `EnCInfo` | Pointer to the `EnCSyncBlockInfo` for this object (NULL if the object has no added EnC fields). Optional: only present when EditAndContinue is configured. |
| `FieldDesc` | `DWord2` | Packed flags/offset word containing the field's offset; the EnC-new sentinel `FieldOffsetNewEnc` is stored here for fields that have been added but do not yet have storage assigned. |
| `System.Diagnostics.EditAndContinueHelper` | `_objectReference` | Holds the per-field storage for an EnC-added instance field. |

### Globals used

Expand All @@ -37,6 +50,8 @@ bool IsFieldDescEnCNew(TargetPointer fieldDescPointer);
| --- |
| `IRuntimeTypeSystem` |
| `ILoader` |
| `IObject` |
| `IGC` |

```csharp
internal enum FieldDescFlags2 : uint
Expand Down Expand Up @@ -81,4 +96,80 @@ bool IsFieldDescEnCNew(TargetPointer fieldDescPointer)
uint offset = DWord2 & (uint)FieldDescFlags2.OffsetMask;
return offset == target.ReadGlobal<uint>("FieldOffsetNewEnc");
}

bool DoesEnCFieldDescNeedFixup(TargetPointer encFieldDescPointer)
{
int needsFixup = target.Read<int>(encFieldDescPointer + /* EnCFieldDesc::NeedsFixup offset */);
return needsFixup != 0;
}

TargetPointer GetEnCStaticFieldDataAddress(TargetPointer encFieldDescPointer)
{
TargetPointer staticFieldData = target.ReadPointer(encFieldDescPointer + /* EnCFieldDesc::StaticFieldData offset */);
if (staticFieldData == TargetPointer.Null)
return TargetPointer.Null;

// [FieldAddress] on EnCAddedStaticField::FieldData returns the address of the field slot
return staticFieldData + /* EnCAddedStaticField::FieldData offset */;
}

TargetPointer GetEnCInstanceFieldAddress(TargetPointer objectAddress, TargetPointer encFieldDescPointer)
{
// get the SyncBlock from the object header via IObject
TargetPointer syncBlockAddress = target.Contracts.Object.GetSyncBlockAddress(objectAddress);
if (syncBlockAddress == TargetPointer.Null)
return TargetPointer.Null;

// SyncBlock::EnCInfo is an optional field; if absent the object has no EnC-added fields
TargetPointer encInfoAddress = target.ReadPointer(syncBlockAddress + /* SyncBlock::EnCInfo offset */);
if (encInfoAddress == TargetPointer.Null)
return TargetPointer.Null;

// Walk the linked list of EnCAddedField entries to find the matching FieldDesc
TargetPointer entryPtr = target.ReadPointer(encInfoAddress + /* EnCSyncBlockInfo::List offset */);
while (entryPtr != TargetPointer.Null)
{
TargetPointer entryFieldDesc = target.ReadPointer(entryPtr + /* EnCAddedField::FieldDesc offset */);
if (entryFieldDesc == encFieldDescPointer)
{
// The FieldData is a dependent handle; the secondary is the EnCHelper object
TargetPointer handleAddress = ReadObjectHandle(entryPtr + /* EnCAddedField::FieldData offset */);
if (handleAddress == TargetPointer.Null)
return TargetPointer.Null;

TargetNUInt secondary = target.Contracts.GC.GetHandleExtraInfo(handleAddress);
TargetPointer helperObjectAddress = new TargetPointer(secondary.Value);
if (helperObjectAddress == TargetPointer.Null)
return TargetPointer.Null;

// [FieldAddress] on _objectReference yields the address of the OBJECTREF slot
TargetPointer objectReferenceAddress = helperObjectAddress + /* EditAndContinueHelper::_objectReference offset */;
TargetPointer fieldObject = target.ReadPointer(objectReferenceAddress);

CorElementType fieldType = target.Contracts.RuntimeTypeSystem.GetFieldDescType(encFieldDescPointer);
if (fieldType == CorElementType.ValueType)
{
// Value-typed field stored as a boxed object; unbox to get the data.
if (fieldObject == TargetPointer.Null)
return TargetPointer.Null;
return fieldObject + /* Object::Data offset */;
}
else if (fieldType == CorElementType.Class)
{
// The OBJECTREF slot itself is the field's value location.
return objectReferenceAddress;
}
else
{
// Primitive stored in a 1-element array. Return the address of the first element.
if (fieldObject == TargetPointer.Null)
return TargetPointer.Null;
return target.Contracts.Object.GetArrayData(fieldObject, out _, out _, out _);
}
}
entryPtr = target.ReadPointer(entryPtr + /* EnCAddedField::Next offset */);
}

return TargetPointer.Null;
}
```
4 changes: 1 addition & 3 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3678,7 +3678,7 @@ void DacDbiInterfaceImpl::InitFieldData(const FieldDesc * pFD,
// GENERICS: TODO: this method will need to be modified if we ever support EnC on
// generic classes.
//-----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, OUT FieldData * pFieldData, OUT BOOL * pfStatic)
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, OUT FieldData * pFieldData)
{
DD_ENTER_MAY_THROW;

Expand All @@ -3704,8 +3704,6 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetEnCHangingFieldInfo(const EnCH
#endif // FEATURE_METADATA_UPDATER

InitFieldData(pFD, pORField, pEnCFieldInfo, pFieldData);
*pfStatic = (pFD->IsStatic() != 0);

}
EX_CATCH_HRESULT(hr);
return hr;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/daccess/dacdbiimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ class DacDbiInterfaceImpl :
HRESULT STDMETHODCALLTYPE GetCollectibleTypeStaticAddress(VMPTR_FieldDesc vmField, OUT CORDB_ADDRESS * pRetVal);

// Get information about a field added with Edit And Continue.
HRESULT STDMETHODCALLTYPE GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, OUT FieldData * pFieldData, OUT BOOL * pfStatic);
HRESULT STDMETHODCALLTYPE GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, OUT FieldData * pFieldData);

// EnumerateTypeHandleParams gets the necessary data for a type handle, i.e. its type
// parameters, e.g. "String" and "List<int>" from the type handle for
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/debug/di/rsclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -996,16 +996,15 @@ FieldData * CordbClass::GetEnCFieldFromDac(BOOL fStatic,
mdTypeDef metadataToken;
FieldData fieldData,
* pInfo = NULL;
BOOL fDacStatic;
CordbProcess * pProcess = GetModule()->GetProcess();

_ASSERTE(pProcess != NULL);
IfFailThrow(GetToken(&metadataToken));
InitEnCFieldInfo(&encField, fStatic, pObject, fieldToken, metadataToken);

// Go get this particular field.
IfFailThrow(pProcess->GetDAC()->GetEnCHangingFieldInfo(&encField, &fieldData, &fDacStatic));
_ASSERTE(fStatic == fDacStatic);
IfFailThrow(pProcess->GetDAC()->GetEnCHangingFieldInfo(&encField, &fieldData));
_ASSERTE((fStatic != 0) == fieldData.m_fFldIsStatic);

// Save the field results in our cache and get a stable pointer to the data
if (fStatic)
Expand Down
3 changes: 1 addition & 2 deletions src/coreclr/debug/inc/dacdbiinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -1673,8 +1673,7 @@ IDacDbiInterface : public IUnknown
// the assembly
// an indication of the type: whether it's a class or value type
// output: pFieldData - information about the EnC added field
// pfStatic - flag to indicate whether the field is static
virtual HRESULT STDMETHODCALLTYPE GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, OUT FieldData * pFieldData, OUT BOOL * pfStatic) = 0;
virtual HRESULT STDMETHODCALLTYPE GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, OUT FieldData * pFieldData) = 0;


// EnumerateTypeHandleParams gets the necessary data for a type handle, i.e. its
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/debug/inc/dacdbistructures.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ class MSLAYOUT EnCHangingFieldInfo
public:
// Init will initialize fields, taking into account whether the field is static or not.
void Init(VMPTR_Object pObject,
SIZE_T offset,
UINT offset,
mdFieldDef fieldToken,
CorElementType elementType,
mdTypeDef metadataToken,
Expand All @@ -748,13 +748,13 @@ class MSLAYOUT EnCHangingFieldInfo
DebuggerIPCE_BasicTypeData GetObjectTypeData() const { return m_objectTypeData; };
mdFieldDef GetFieldToken() const { return m_fldToken; };
VMPTR_Object GetVmObject() const { return m_vmObject; };
SIZE_T GetOffsetToVars() const { return m_offsetToVars; };
UINT GetOffsetToVars() const { return m_offsetToVars; };

private:
DebuggerIPCE_BasicTypeData m_objectTypeData; // type data for the EnC field
VMPTR_Object m_vmObject; // object instance to which the field has been added--if the field is
// static, this will be NULL instead of pointing to an instance
SIZE_T m_offsetToVars; // offset to the beginning of variable storage in the object
UINT m_offsetToVars; // offset to the beginning of variable storage in the object
mdFieldDef m_fldToken; // metadata token for the added field

}; // EnCHangingFieldInfo
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/inc/dacdbistructures.inl
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ CORDB_ADDRESS FieldData::GetStaticAddress()

inline
void EnCHangingFieldInfo::Init(VMPTR_Object pObject,
SIZE_T offset,
UINT offset,
mdFieldDef fieldToken,
CorElementType elementType,
mdTypeDef metadataToken,
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/inc/dacdbi.idl
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ interface IDacDbiInterface : IUnknown
// Field
HRESULT GetThreadStaticAddress([in] VMPTR_FieldDesc vmField, [in] VMPTR_Thread vmRuntimeThread, [out] CORDB_ADDRESS * pRetVal);
HRESULT GetCollectibleTypeStaticAddress([in] VMPTR_FieldDesc vmField, [out] CORDB_ADDRESS * pRetVal);
HRESULT GetEnCHangingFieldInfo([in] const struct EnCHangingFieldInfo * pEnCFieldInfo, [out] struct FieldData * pFieldData, [out] BOOL * pfStatic);
HRESULT GetEnCHangingFieldInfo([in] const struct EnCHangingFieldInfo * pEnCFieldInfo, [out] struct FieldData * pFieldData);
HRESULT GetTypeHandleParams([in] VMPTR_TypeHandle vmTypeHandle, [out] TypeParamsList * pParams);
HRESULT GetSimpleType(
[in] CorElementType simpleType,
Expand Down
31 changes: 29 additions & 2 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ CDAC_TYPE_FIELD(SyncBlock, TYPE(ObjectHandle), Lock, cdac_data<SyncBlock>::Lock)
CDAC_TYPE_FIELD(SyncBlock, T_UINT32, ThinLock, cdac_data<SyncBlock>::ThinLock)
CDAC_TYPE_FIELD(SyncBlock, T_POINTER, LinkNext, cdac_data<SyncBlock>::LinkNext)
CDAC_TYPE_FIELD(SyncBlock, T_UINT32, HashCode, cdac_data<SyncBlock>::HashCode)
#ifdef FEATURE_METADATA_UPDATER
CDAC_TYPE_FIELD(SyncBlock, T_POINTER, EnCInfo, cdac_data<SyncBlock>::EnCInfo)
#endif // FEATURE_METADATA_UPDATER
CDAC_TYPE_END(SyncBlock)


Expand Down Expand Up @@ -325,9 +328,33 @@ CDAC_TYPE_END(EnCEEClassData)

CDAC_TYPE_BEGIN(EnCAddedFieldElement)
CDAC_TYPE_INDETERMINATE(EnCAddedFieldElement)
CDAC_TYPE_FIELD(EnCAddedFieldElement, T_POINTER, Next, cdac_data<EnCAddedFieldElement>::Next)
CDAC_TYPE_FIELD(EnCAddedFieldElement, T_POINTER, FieldDesc, cdac_data<EnCAddedFieldElement>::FieldDesc)
CDAC_TYPE_FIELD(EnCAddedFieldElement, T_POINTER, Next, offsetof(EnCAddedFieldElement, m_next))
CDAC_TYPE_FIELD(EnCAddedFieldElement, T_POINTER, FieldDesc, offsetof(EnCAddedFieldElement, m_fieldDesc))
CDAC_TYPE_END(EnCAddedFieldElement)

CDAC_TYPE_BEGIN(EnCFieldDesc)
CDAC_TYPE_INDETERMINATE(EnCFieldDesc)
CDAC_TYPE_FIELD(EnCFieldDesc, T_INT32, NeedsFixup, cdac_data<EnCFieldDesc>::NeedsFixup)
CDAC_TYPE_FIELD(EnCFieldDesc, T_POINTER, StaticFieldData, cdac_data<EnCFieldDesc>::StaticFieldData)
CDAC_TYPE_END(EnCFieldDesc)

CDAC_TYPE_BEGIN(EnCAddedField)
CDAC_TYPE_INDETERMINATE(EnCAddedField)
CDAC_TYPE_FIELD(EnCAddedField, T_POINTER, Next, offsetof(EnCAddedField, m_pNext))
CDAC_TYPE_FIELD(EnCAddedField, T_POINTER, FieldDesc, offsetof(EnCAddedField, m_pFieldDesc))
CDAC_TYPE_FIELD(EnCAddedField, TYPE(ObjectHandle), FieldData, offsetof(EnCAddedField, m_FieldData))
CDAC_TYPE_END(EnCAddedField)

CDAC_TYPE_BEGIN(EnCAddedStaticField)
CDAC_TYPE_INDETERMINATE(EnCAddedStaticField)
CDAC_TYPE_FIELD(EnCAddedStaticField, T_POINTER, FieldDesc, offsetof(EnCAddedStaticField, m_pFieldDesc))
CDAC_TYPE_FIELD(EnCAddedStaticField, T_UINT8, FieldData, offsetof(EnCAddedStaticField, m_FieldData))
CDAC_TYPE_END(EnCAddedStaticField)

CDAC_TYPE_BEGIN(EnCSyncBlockInfo)
CDAC_TYPE_INDETERMINATE(EnCSyncBlockInfo)
CDAC_TYPE_FIELD(EnCSyncBlockInfo, T_POINTER, List, cdac_data<EnCSyncBlockInfo>::List)
CDAC_TYPE_END(EnCSyncBlockInfo)
#endif // FEATURE_METADATA_UPDATER

CDAC_TYPE_BEGIN(ModuleLookupMap)
Expand Down
16 changes: 13 additions & 3 deletions src/coreclr/vm/encee.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class EnCFieldDesc : public FieldDesc


private:
friend struct ::cdac_data<EnCFieldDesc>;

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

Expand Down Expand Up @@ -188,10 +190,10 @@ struct cdac_data<EnCEEClassData>
};

template<>
struct cdac_data<EnCAddedFieldElement>
struct cdac_data<EnCFieldDesc>
{
static constexpr size_t Next = offsetof(EnCAddedFieldElement, m_next);
static constexpr size_t FieldDesc = offsetof(EnCAddedFieldElement, m_fieldDesc);
static constexpr size_t NeedsFixup = offsetof(EnCFieldDesc, m_bNeedsFixup);
static constexpr size_t StaticFieldData = offsetof(EnCFieldDesc, m_pStaticFieldData);
};

//---------------------------------------------------------------------------------------
Expand Down Expand Up @@ -379,6 +381,8 @@ class EnCSyncBlockInfo
void Cleanup();

private:
friend struct ::cdac_data<EnCSyncBlockInfo>;

// Gets the address of an EnC field accounting for its type: valuetype, class or primitive
PTR_CBYTE GetEnCFieldAddrFromHelperFieldDesc(FieldDesc * pHelperFieldDesc,
OBJECTREF pHelper,
Expand All @@ -388,6 +392,12 @@ class EnCSyncBlockInfo
PTR_EnCAddedField m_pList;
};

template<>
struct cdac_data<EnCSyncBlockInfo>
{
static constexpr size_t List = offsetof(EnCSyncBlockInfo, m_pList);
};

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

Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/vm/syncblk.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,9 @@ struct cdac_data<SyncBlock>
static constexpr size_t ThinLock = offsetof(SyncBlock, m_thinLock);
static constexpr size_t LinkNext = offsetof(SyncBlock, m_pNext);
static constexpr size_t HashCode = offsetof(SyncBlock, m_dwHashCode);

#ifdef FEATURE_METADATA_UPDATER
static constexpr size_t EnCInfo = offsetof(SyncBlock, m_pEnCInfo);
#endif // FEATURE_METADATA_UPDATER
};

class SyncTableEntry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts;
public interface IRuntimeMutableTypeSystem : IContract
{
static string IContract.Name { get; } = nameof(RuntimeMutableTypeSystem);

IEnumerable<TargetPointer> EnumerateAddedFieldDescs(TypeHandle typeHandle, bool staticFields)
=> throw new NotImplementedException();

IEnumerable<TargetPointer> EnumerateAddedFieldDescs(TypeHandle typeHandle, bool staticFields) => throw new NotImplementedException();
bool IsFieldDescEnCNew(TargetPointer fieldDescPointer) => throw new NotImplementedException();
bool DoesEnCFieldDescNeedFixup(TargetPointer encFieldDescPointer) => throw new NotImplementedException();
TargetPointer GetEnCStaticFieldDataAddress(TargetPointer encFieldDescPointer) => throw new NotImplementedException();
TargetPointer GetEnCInstanceFieldAddress(TargetPointer objectAddress, TargetPointer encFieldDescPointer) => throw new NotImplementedException();
Comment thread
max-charlamb marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
}

public readonly struct RuntimeMutableTypeSystem : IRuntimeMutableTypeSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public static class CorDbgHResults
public const int CORDBG_S_NOT_ALL_BITS_SET = unchecked((int)0x00131c13);
public const int CORDBG_E_NON_MATCHING_CONTEXT = unchecked((int)0x80131327);
public const int CORDBG_E_UNSUPPORTED_DELEGATE = unchecked((int)0x80131c68);
public const int CORDBG_E_ENC_HANGING_FIELD = unchecked((int)0x80131342);
Comment thread
max-charlamb marked this conversation as resolved.
}
Loading
Loading