Skip to content
Draft
12 changes: 8 additions & 4 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ partial interface IRuntimeTypeSystem : IContract
// True if the MethodTable is the System.Object MethodTable (g_pObjectClass)
public virtual bool IsObject(TypeHandle typeHandle);
public virtual bool IsString(TypeHandle typeHandle);
// True if the type is a GC-collectable object reference.
public virtual bool IsObjRef(TypeHandle typeHandle);
// True if the CorElementType represents a GC-collectable object reference.
public virtual bool IsCorElementTypeObjRef(CorElementType elementType);
// True if the MethodTable represents a type that contains managed references
public virtual bool ContainsGCPointers(TypeHandle typeHandle);
// True if the type requires 8-byte alignment on platforms that don't 8-byte align by default (FEATURE_64BIT_ALIGNMENT)
Expand Down Expand Up @@ -611,8 +611,12 @@ Contracts used:

public bool IsString(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsString;

public bool IsObjRef(TypeHandle typeHandle) => // Returns true if GetSignatureCorElementType returns Class, Array, or SzArray.

public bool IsCorElementTypeObjRef(CorElementType elementType) =>
elementType is CorElementType.Class
or CorElementType.Object
or CorElementType.String
or CorElementType.Array
or CorElementType.SzArray;
public bool ContainsGCPointers(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.ContainsGCPointers;

public bool RequiresAlign8(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.RequiresAlign8;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ public interface IRuntimeTypeSystem : IContract
// True if the MethodTable is the System.Object MethodTable (g_pObjectClass)
bool IsObject(TypeHandle typeHandle) => throw new NotImplementedException();
bool IsString(TypeHandle typeHandle) => throw new NotImplementedException();
bool IsObjRef(TypeHandle typeHandle) => throw new NotImplementedException();
// True if the CorElementType represents a GC-collectable object reference.
bool IsCorElementTypeObjRef(CorElementType elementType) => throw new NotImplementedException();
// True if the MethodTable represents a type that contains managed references
Comment on lines 132 to 136
Comment on lines +134 to 136
bool ContainsGCPointers(TypeHandle typeHandle) => throw new NotImplementedException();
// True if the type requires 8-byte alignment on platforms that don't 8-byte align by default (FEATURE_64BIT_ALIGNMENT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,12 +562,13 @@ private Data.EEClass GetClassData(TypeHandle typeHandle)
public bool IsObject(TypeHandle typeHandle) => ObjectMethodTablePointer != TargetPointer.Null && ObjectMethodTablePointer == typeHandle.Address;

public bool IsString(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsString;
public bool IsObjRef(TypeHandle typeHandle)
{
CorElementType elementType = GetSignatureCorElementType(typeHandle);
// Keep this aligned with CorTypeInfo::IsObjRef semantics for signature element types.
return elementType is CorElementType.Class or CorElementType.Array or CorElementType.SzArray;
}
public bool IsCorElementTypeObjRef(CorElementType elementType)
=> elementType is CorElementType.Class
or CorElementType.Object
or CorElementType.String
or CorElementType.Array
or CorElementType.SzArray;

public bool ContainsGCPointers(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.ContainsGCPointers;
public bool RequiresAlign8(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.RequiresAlign8;
public bool IsContinuationWithoutMetadata(TypeHandle typeHandle) => typeHandle.IsMethodTable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2286,8 +2286,45 @@ public int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal)
return hr;
}

public int GetApproxTypeHandle(nint pTypeData, ulong* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetApproxTypeHandle(pTypeData, pRetVal) : HResults.E_NOTIMPL;
public int GetApproxTypeHandle(TypeInfoList* pTypeData, ulong* pRetVal)
{
if (pTypeData == null || pRetVal == null)
return HResults.E_POINTER;
*pRetVal = 0;
int hr = HResults.S_OK;
try
{
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;

TargetPointer canonMtPtr = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.CanonMethodTable));
TypeHandle canonTh = rts.GetTypeHandle(canonMtPtr);

if (pTypeData->m_nEntries <= 0 || pTypeData->m_pList == null)
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!;

TypeDataWalk walk = new TypeDataWalk(_target, rts, canonTh, pTypeData->m_pList, (uint)pTypeData->m_nEntries);
TypeHandle th = walk.ReadLoadedTypeHandle();
if (th.IsNull)
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!;
*pRetVal = th.Address.Value;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
ulong vmLocal;
int hrLocal = _legacy.GetApproxTypeHandle(pTypeData, &vmLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == vmLocal, $"cDAC: {*pRetVal:x}, DAC: {vmLocal:x}");
}
#endif
return hr;
}
Comment thread
rcj1 marked this conversation as resolved.


public int GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData* pTypeData, ArgInfoList* pArgInfo, ulong* pVmTypeHandle)
{
Expand Down Expand Up @@ -2379,29 +2416,7 @@ private TypeHandle GetClassOrValueTypeHandle(IRuntimeTypeSystem rts, DebuggerIPC

ulong vmAssembly = ReadLittleEndian(pData->vmAssembly);
uint metadataToken = ReadLittleEndian(pData->metadataToken);
return LookupTypeDefOrRefInAssembly(rts, vmAssembly, metadataToken);
}

private TypeHandle LookupTypeDefOrRefInAssembly(IRuntimeTypeSystem rts, ulong vmAssembly, uint metadataToken)
{
Contracts.ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly));
Contracts.ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
TargetPointer mt;
switch ((EcmaMetadataUtils.TokenType)(metadataToken & EcmaMetadataUtils.TokenTypeMask))
{
case EcmaMetadataUtils.TokenType.mdtTypeDef:
mt = loader.GetModuleLookupMapElement(lookupTables.TypeDefToMethodTable, metadataToken, out _);
break;
case EcmaMetadataUtils.TokenType.mdtTypeRef:
mt = loader.GetModuleLookupMapElement(lookupTables.TypeRefToMethodTable, metadataToken, out _);
break;
default:
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!;
}
if (mt == TargetPointer.Null)
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!;
return rts.GetTypeHandle(mt);
return DbiHelpers.LookupTypeDefOrRefInAssembly(_target, rts, vmAssembly, metadataToken);
}

private TypeHandle GetExactArrayTypeHandle(IRuntimeTypeSystem rts, DebuggerIPCE_ExpandedTypeData* pTopLevel, ArgInfoList* pArgInfo)
Expand All @@ -2427,7 +2442,7 @@ private TypeHandle GetExactClassTypeHandle(IRuntimeTypeSystem rts, DebuggerIPCE_
{
ulong vmAssembly = ReadLittleEndian(pTopLevel->ClassTypeData_vmAssembly);
uint metadataToken = ReadLittleEndian(pTopLevel->ClassTypeData_metadataToken);
TypeHandle typeConstructor = LookupTypeDefOrRefInAssembly(rts, vmAssembly, metadataToken);
TypeHandle typeConstructor = DbiHelpers.LookupTypeDefOrRefInAssembly(_target, rts, vmAssembly, metadataToken);

int argCount = pArgInfo->m_nEntries;
if (argCount == 0)
Expand Down Expand Up @@ -3604,7 +3619,7 @@ public int GetObjectFields(ulong id, uint celt, COR_FIELD* layout, uint* pceltFe
// count actually written. Preserve this behavior for compatibility w/ICorDebug.
*pceltFetched = celt;

bool isReferenceType = rts.IsObjRef(typeHandle);
bool isReferenceType = rts.IsCorElementTypeObjRef(rts.GetInternalCorElementType(typeHandle));
uint firstFieldOffset = isReferenceType ? _target.GetTypeInfo(DataType.Object).Size!.Value : 0;

TargetPointer[] fieldDescList = rts.GetFieldDescList(typeHandle).Take((int)cFields).ToArray();
Expand Down Expand Up @@ -3734,8 +3749,11 @@ public int GetTypeLayout(ulong id, COR_TYPE_LAYOUT* pLayout)
numInstanceFields -= rts.GetNumInstanceFields(parentHandle);
}
pLayout->numFields = numInstanceFields;
pLayout->boxOffset = rts.IsObjRef(typeHandle) ? 0u : (uint)_target.PointerSize;
pLayout->type = (int)(rts.IsString(typeHandle) ? CorElementType.String : rts.GetInternalCorElementType(typeHandle));
CorElementType componentType = rts.IsString(typeHandle)
? CorElementType.String
: rts.GetInternalCorElementType(typeHandle);
pLayout->type = (int)componentType;
pLayout->boxOffset = rts.IsCorElementTypeObjRef(componentType) ? 0u : (uint)_target.PointerSize;
}
catch (System.Exception ex)
{
Expand Down Expand Up @@ -4675,7 +4693,7 @@ private static void WriteLittleEndian<T>(ref T dest, T value) where T : unmanage
}
}

private static T ReadLittleEndian<T>(T value) where T : unmanaged, IBinaryInteger<T>
internal static T ReadLittleEndian<T>(T value) where T : unmanaged, IBinaryInteger<T>
{
if (BitConverter.IsLittleEndian)
return value;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;
internal static class DbiHelpers
{
public static TypeHandle LookupTypeDefOrRefInAssembly(Target target, IRuntimeTypeSystem rts, ulong vmAssembly, uint metadataToken)
{
TypeHandle th = TryLookupTypeDefOrRefInAssembly(target, rts, vmAssembly, metadataToken);
if (th.IsNull)
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!;
return th;
}

public static TypeHandle TryLookupTypeDefOrRefInAssembly(Target target, IRuntimeTypeSystem rts, ulong vmAssembly, uint metadataToken)
{
ILoader loader = target.Contracts.Loader;
ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly));
ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
TargetPointer mt;
switch ((EcmaMetadataUtils.TokenType)(metadataToken & EcmaMetadataUtils.TokenTypeMask))
{
case EcmaMetadataUtils.TokenType.mdtTypeDef:
mt = loader.GetModuleLookupMapElement(lookupTables.TypeDefToMethodTable, metadataToken, out _);
break;
case EcmaMetadataUtils.TokenType.mdtTypeRef:
mt = loader.GetModuleLookupMapElement(lookupTables.TypeRefToMethodTable, metadataToken, out _);
break;
default:
return default;
}
if (mt == TargetPointer.Null)
return default;
return rts.GetTypeHandle(mt);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,20 @@ public unsafe struct ArgInfoList
public int m_nEntries;
}

[StructLayout(LayoutKind.Sequential, Size = 48)]
public struct DebuggerIPCE_TypeArgData
{
public DebuggerIPCE_ExpandedTypeData data;
public uint numTypeArgs; // Portable<UINT>
Comment on lines +310 to +314
}

[StructLayout(LayoutKind.Sequential)]
public unsafe struct TypeInfoList
{
public DebuggerIPCE_TypeArgData* m_pList;
public int m_nEntries;
Comment on lines +317 to +321
}

public enum DynamicMethodType
{
kNone = 0,
Expand Down Expand Up @@ -571,7 +585,7 @@ public unsafe partial interface IDacDbiInterface
int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal);

[PreserveSig]
int GetApproxTypeHandle(nint pTypeData, ulong* pRetVal);
int GetApproxTypeHandle(TypeInfoList* pTypeData, ulong* pRetVal);

[PreserveSig]
int GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData* pTypeData, ArgInfoList* pArgInfo, ulong* pVmTypeHandle);
Expand Down
Loading
Loading