Skip to content
Open
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
64 changes: 64 additions & 0 deletions docs/design/datacontracts/EditAndContinue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Contract EditAndContinue

This contract exposes the runtime's Edit-and-Continue (EnC) bookkeeping.

## APIs of contract

```csharp
IEnumerable<TargetPointer> EnumerateAddedFieldDescs(TypeHandle typeHandle, bool staticFields);
```

## Version 1

### Data descriptors

| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `Module` | `EnCClassList` | Address of the embedded `CUnorderedArrayBaseWithAllocator` holding the module's `EnCEEClassData*` entries. Optional: only present when EditAndContinue is configured. |
| `UnorderedArrayBase` | `Count` | Number of valid entries currently stored in the array. |
| `UnorderedArrayBase` | `Table` | Pointer to the backing storage holding the array's entries. |
| `EnCEEClassData` | `MethodTable` | Pointer to the `MethodTable` whose EnC data is held by this entry. |
| `EnCEEClassData` | `AddedInstanceFields` | Head of the linked list of `EnCAddedFieldElement` for added instance fields. |
| `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`). |

### Required contracts

| Contract |
| --- |
| `IRuntimeTypeSystem` |
| `ILoader` |

```csharp
IEnumerable<TargetPointer> EnumerateAddedFieldDescs(TypeHandle typeHandle, bool staticFields)
{
// get modulePtr and moduleHandle from typeHandle
// if there is no EnC data, yield break
TargetPointer classList = modulePtr + /* Module::EnCClassList offset */;
uint classListCount = target.Read<uint>(classList + /* UnorderedArrayBase::Count offset */);
TargetPointer classListTable = target.ReadPointer(classList + /* UnorderedArrayBase::Table offset */);
TargetPointer classDataPtr = TargetPointer.Null;

for (uint i = 0; i < classListCount; i++)
{
// search on EnC data for data that matches the method table
TargetPointer entry = target.ReadPointer(classListTable + i * target.PointerSize);
TargetPointer mt = target.ReadPointer(entry + /* EnCEEClassData::MethodTable offset */);
if (mt == typeHandle.Address)
{
classDataPtr = entry;
break;
}
}

// enumerate fields that have been added
TargetPointer node = staticFields ? target.ReadPointer(classDataPtr + /* EnCEEClassData::AddedStaticFields offset */)
: target.ReadPointer(classDataPtr + /* EnCEEClassData::AddedInstanceFields offset */);
while (node != TargetPointer.Null)
{
yield return node + /* EnCAddedFieldElement::FieldDesc offset */;
node = target.ReadPointer(node + /* EnCAddedFieldElement::Next offset */);
}
}
```
38 changes: 34 additions & 4 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ partial interface IRuntimeTypeSystem : IContract
public virtual TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot);

public virtual uint GetBaseSize(TypeHandle typeHandle);
// The number of bytes of instance fields stored in an object of this type on the GC heap.
// Equivalent to the native MethodTable::GetNumInstanceFieldBytes(), which is computed as
// GetBaseSize() minus the EEClass base-size padding (the trailing alignment/min-object-size
// bytes included in BaseSize but not occupied by actual instance fields).
public virtual uint GetNumInstanceFieldBytes(TypeHandle typeHandle);
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
public virtual uint GetComponentSize(TypeHandle typeHandle);

Expand Down Expand Up @@ -274,8 +279,10 @@ TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer);
uint GetFieldDescMemberDef(TargetPointer fieldDescPointer);
bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer);
bool IsFieldDescStatic(TargetPointer fieldDescPointer);
bool IsFieldDescRVA(TargetPointer fieldDescPointer);
bool IsFieldDescEnCNew(TargetPointer fieldDescPointer);
uint GetFieldDescType(TargetPointer fieldDescPointer);
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef);
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition? fieldDef);
TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer, bool unboxValueTypes = true);
TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread, bool unboxValueTypes = true);
```
Expand Down Expand Up @@ -576,6 +583,8 @@ Contracts used:

public uint GetBaseSize(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[TypeHandle.Address].Flags.BaseSize;

public uint GetNumInstanceFieldBytes(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[TypeHandle.Address].Flags.BaseSize - GetClassData(TypeHandle).BaseSizePadding;

public uint GetComponentSize(TypeHandle TypeHandle) =>!typeHandle.IsMethodTable() ? (uint)0 : GetComponentSize(_methodTables[TypeHandle.Address]);

public TargetPointer GetClassPointer(TypeHandle TypeHandle)
Expand Down Expand Up @@ -2023,6 +2032,13 @@ Getting a MethodDesc for a certain slot in a MethodTable

### FieldDesc

The version 1 FieldDesc APIs depend on the following globals:

| Global name | Meaning |
| --- | --- |
| `FieldOffsetBigRVA` | Sentinel value of `FieldDesc::DWord2` indicating the field is an RVA static whose offset is too large to encode in the bitfield; the real offset must be read from the field's metadata (`FieldDefinition.GetRelativeVirtualAddress`). |
| `FieldOffsetNewEnc` | Sentinel value of `FieldDesc`'s offset bitfield indicating the field was added via Edit-and-Continue and does not yet have backing storage. |

The version 1 FieldDesc APIs depend on the following data descriptors:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
Expand All @@ -2036,6 +2052,7 @@ internal enum FieldDescFlags1 : uint
TokenMask = 0xffffff,
IsStatic = 0x1000000,
IsThreadStatic = 0x2000000,
IsRVA = 0x4000000,
}

internal enum FieldDescFlags2 : uint
Expand Down Expand Up @@ -2067,18 +2084,31 @@ bool IsFieldDescStatic(TargetPointer fieldDescPointer)
return (DWord1 & (uint)FieldDescFlags1.IsStatic) != 0;
}

bool IsFieldDescRVA(TargetPointer fieldDescPointer)
{
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
return (DWord1 & (uint)FieldDescFlags1.IsRVA) != 0;
}

bool IsFieldDescEnCNew(TargetPointer fieldDescPointer)
{
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
uint offset = DWord2 & (uint)FieldDescFlags2.OffsetMask;
return offset == _target.ReadGlobal<uint>("FieldOffsetNewEnc");
}

uint GetFieldDescType(TargetPointer fieldDescPointer)
{
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
return (DWord2 & (uint)FieldDescFlags2.TypeMask) >> 27;
}

uint GetFieldDescOffset(TargetPointer fieldDescPointer)
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition? fieldDef)
{
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
if (DWord2 == _target.ReadGlobal<uint>("FieldOffsetBigRVA"))
if (DWord2 == _target.ReadGlobal<uint>("FieldOffsetBigRVA") && fieldDef.HasValue)
{
return (uint)fieldDef.GetRelativeVirtualAddress();
return (uint)fieldDef.Value.GetRelativeVirtualAddress();
}
Comment on lines +2109 to 2112
return DWord2 & (uint)FieldDescFlags2.OffsetMask;
}
Expand Down
134 changes: 38 additions & 96 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1404,69 +1404,6 @@ void DacDbiInterfaceImpl::GetTypeHandles(VMPTR_TypeHandle vmThExact,
}
} // DacDbiInterfaceImpl::GetTypeHandles

//-----------------------------------------------------------------------------
// DacDbiInterfaceImpl::GetTotalFieldCount
// Gets the total number of fields for a type.
// Input Argument: thApprox - type handle used to determine the number of fields
// Return Value: count of the total fields of the type.
//-----------------------------------------------------------------------------
unsigned int DacDbiInterfaceImpl::GetTotalFieldCount(TypeHandle thApprox)
{
MethodTable *pMT = thApprox.GetMethodTable();

// Count the instance and static fields for this class (not including parent).
// This will not include any newly added EnC fields.
unsigned int IFCount = pMT->GetNumIntroducedInstanceFields();
unsigned int SFCount = pMT->GetNumStaticFields();

#ifdef FEATURE_METADATA_UPDATER
PTR_Module pModule = pMT->GetModule();

// Stats above don't include EnC fields. So add them now.
if (pModule->IsEditAndContinueEnabled())
{
PTR_EnCEEClassData pEncData =
(dac_cast<PTR_EditAndContinueModule>(pModule))->GetEnCEEClassData(pMT, TRUE);

if (pEncData != NULL)
{
_ASSERTE(pEncData->GetMethodTable() == pMT);

// EnC only adds fields, never removes them.
IFCount += pEncData->GetAddedInstanceFields();
SFCount += pEncData->GetAddedStaticFields();
}
}
#endif
return IFCount + SFCount;
} // DacDbiInterfaceImpl::GetTotalFieldCount

//-----------------------------------------------------------------------------
// DacDbiInterfaceImpl::InitClassData
// initializes various values of the ClassInfo data structure, including the
// field count, generic args count, size and value class flag
// Arguments:
// input: thApprox - used to get access to all the necessary values
// fIsInstantiatedType - used to determine how to compute the size
// output: pData - contains fields to be initialized
//-----------------------------------------------------------------------------
void DacDbiInterfaceImpl::InitClassData(TypeHandle thApprox,
BOOL fIsInstantiatedType,
ClassInfo * pData)
{
pData->m_fieldList.Alloc(GetTotalFieldCount(thApprox));

// For Generic classes you must get the object size via the type handle, which
// will get you to the right information for the particular instantiation
// you're working with...
pData->m_objectSize = 0;
if ((!thApprox.GetNumGenericArgs()) || fIsInstantiatedType)
{
pData->m_objectSize = thApprox.GetMethodTable()->GetNumInstanceFieldBytes();
}

} // DacDbiInterfaceImpl::InitClassData

//-----------------------------------------------------------------------------
// DacDbiInterfaceImpl::GetStaticsBases
// Gets the base table addresses for both GC and non-GC statics
Expand Down Expand Up @@ -1541,7 +1478,7 @@ void DacDbiInterfaceImpl::ComputeFieldData(PTR_FieldDesc pFD,
PTR_VOID addr = pFD->GetStaticAddressHandle(NULL);
if (pCurrentFieldData->OkToGetOrSetStaticAddress())
{
pCurrentFieldData->SetStaticAddress(PTR_TO_TADDR(addr));
pCurrentFieldData->SetStaticAddress(PTR_TO_CORDB_ADDRESS(dac_cast<TADDR>(addr)));
}
}
else if (pFD->IsThreadStatic() ||
Expand All @@ -1561,15 +1498,15 @@ void DacDbiInterfaceImpl::ComputeFieldData(PTR_FieldDesc pFD,

if (pCurrentFieldData->OkToGetOrSetStaticAddress())
{
pCurrentFieldData->SetStaticAddress((TADDR)NULL);
pCurrentFieldData->SetStaticAddress((CORDB_ADDRESS)NULL);
}
}
else
{
if (pCurrentFieldData->OkToGetOrSetStaticAddress())
{
// calculate the absolute address using the base and the offset from the base
pCurrentFieldData->SetStaticAddress(PTR_TO_TADDR(base) + pFD->GetOffset());
pCurrentFieldData->SetStaticAddress(PTR_TO_CORDB_ADDRESS(dac_cast<TADDR>(base)) + pFD->GetOffset());
}
}
}
Expand All @@ -1588,18 +1525,17 @@ void DacDbiInterfaceImpl::ComputeFieldData(PTR_FieldDesc pFD,

//-----------------------------------------------------------------------------
// DacDbiInterfaceImpl::CollectFields
// Gets information for all the fields for a given type
// Reports per-field FieldData entries for a given type to the supplied callback.
// Arguments:
// input: thExact - used to determine whether we need to get statics base tables
// thApprox - used to get the field desc iterator
// output:
// pFieldList - contains fields to be initialized
// Note: the caller must ensure that *ppFields is NULL (i.e., any previously allocated memory
// must have been deallocated.
// input: thExact - used to determine whether we need to get statics base tables
// thApprox - used to get the field desc iterator
// fpCallback - invoked once per field. Must not throw.
// pUserData - opaque user data passed back to the callback.
//-----------------------------------------------------------------------------
void DacDbiInterfaceImpl::CollectFields(TypeHandle thExact,
TypeHandle thApprox,
DacDbiArrayList<FieldData> * pFieldList)
FP_FIELDDATA_CALLBACK fpCallback,
CALLBACK_DATA pUserData)
{
PTR_BYTE pGCStaticsBase = NULL;
PTR_BYTE pNonGCStaticsBase = NULL;
Expand All @@ -1609,8 +1545,6 @@ void DacDbiInterfaceImpl::CollectFields(TypeHandle thExact,
GetStaticsBases(thExact, &pGCStaticsBase, &pNonGCStaticsBase);
}

unsigned int fieldCount = 0;

// <TODO> we are losing exact type information for static fields in generic types. We have
// field desc iterators only for approximate types, but statics are per instantiation, so we
// need an exact type to be able to handle these correctly. We need to use
Expand All @@ -1620,17 +1554,12 @@ void DacDbiInterfaceImpl::CollectFields(TypeHandle thExact,
ApproxFieldDescIterator::ALL_FIELDS); // don't fixup EnC (we can't, we're stopped)

PTR_FieldDesc pCurrentFD;
unsigned int index = 0;
while (((pCurrentFD = fdIterator.Next()) != NULL) && (index < pFieldList->Count()))
while ((pCurrentFD = fdIterator.Next()) != NULL)
{
// fill in the pCurrentEntry structure
ComputeFieldData(pCurrentFD, pGCStaticsBase, pNonGCStaticsBase, &((*pFieldList)[index]));

// Bump our counts and pointers.
fieldCount++;
index++;
FieldData currentFieldData;
ComputeFieldData(pCurrentFD, pGCStaticsBase, pNonGCStaticsBase, &currentFieldData);
fpCallback(&currentFieldData, pUserData);
}
_ASSERTE(fieldCount == (unsigned int)pFieldList->Count());

} // DacDbiInterfaceImpl::CollectFields

Expand Down Expand Up @@ -1667,8 +1596,11 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::HasTypeParams(VMPTR_TypeHandle th
return hr;
}

// DacDbi API: Get type information for a class
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetClassInfo(VMPTR_TypeHandle thExact, ClassInfo * pData)
// DacDbi API: Enumerate the FieldData entries for a class.
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::EnumerateClassFields(VMPTR_TypeHandle thExact,
OUT SIZE_T *pObjectSize,
FP_FIELDDATA_CALLBACK fpCallback,
CALLBACK_DATA pUserData)
{
DD_ENTER_MAY_THROW;

Expand All @@ -1681,17 +1613,29 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetClassInfo(VMPTR_TypeHandle thE

GetTypeHandles(thExact, thExact, &typeHandleExact, &thApprox);

// initialize field count, generic args count, size and value class flag
InitClassData(thApprox, false, pData);
// For Generic classes you must get the object size via the type handle, which
// will get you to the right information for the particular instantiation
// you're working with...
*pObjectSize = 0;
if (!thApprox.GetNumGenericArgs())
{
*pObjectSize = thApprox.GetMethodTable()->GetNumInstanceFieldBytes();
}

CollectFields(typeHandleExact, thApprox, &(pData->m_fieldList));
CollectFields(typeHandleExact, thApprox, fpCallback, pUserData);
}
EX_CATCH_HRESULT(hr);
return hr;
}

// DacDbi API: Get field information and object size for an instantiated generic type
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetInstantiationFieldInfo(VMPTR_Assembly vmAssembly, VMPTR_TypeHandle vmThExact, VMPTR_TypeHandle vmThApprox, OUT DacDbiArrayList<FieldData> * pFieldList, OUT SIZE_T * pObjectSize)
// DacDbi API: Enumerate the FieldData entries for an instantiated generic type
// and report its instantiation-specific object size.
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::EnumerateInstantiationFields(VMPTR_Assembly vmAssembly,
VMPTR_TypeHandle vmThExact,
VMPTR_TypeHandle vmThApprox,
OUT SIZE_T *pObjectSize,
FP_FIELDDATA_CALLBACK fpCallback,
CALLBACK_DATA pUserData)
{
DD_ENTER_MAY_THROW;

Expand All @@ -1706,9 +1650,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetInstantiationFieldInfo(VMPTR_A

*pObjectSize = thApprox.GetMethodTable()->GetNumInstanceFieldBytes();

pFieldList->Alloc(GetTotalFieldCount(thApprox));

CollectFields(thExact, thApprox, pFieldList);
CollectFields(thExact, thApprox, fpCallback, pUserData);

}
EX_CATCH_HRESULT(hr);
Expand Down Expand Up @@ -3773,7 +3715,7 @@ void DacDbiInterfaceImpl::InitFieldData(const FieldDesc * pFD,
_ASSERTE(!pFieldData->m_fFldIsRVA);

// pORField contains the absolute address
pFieldData->SetStaticAddress(PTR_TO_TADDR(pORField));
pFieldData->SetStaticAddress(PTR_TO_CORDB_ADDRESS(PTR_TO_TADDR(pORField)));
}
else
{
Expand Down
Loading
Loading