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
25 changes: 25 additions & 0 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ partial interface IRuntimeTypeSystem : IContract
public virtual bool IsGenericTypeDefinition(TypeHandle typeHandle);

public virtual bool IsCollectible(TypeHandle typeHandle);
public virtual bool ContainsGenericVariables(TypeHandle typeHandle);
public virtual bool HasTypeParam(TypeHandle typeHandle);

// Element type of the type. NOTE: this drops the CorElementType.GenericInst, and CorElementType.String is returned as CorElementType.Class.
Expand Down Expand Up @@ -226,6 +227,8 @@ bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer);
bool IsFieldDescStatic(TargetPointer fieldDescPointer);
uint GetFieldDescType(TargetPointer fieldDescPointer);
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef);
TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer);
TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread);
```

### Other APIs
Expand Down Expand Up @@ -271,6 +274,7 @@ internal partial struct RuntimeTypeSystem_1
Category_Interface = 0x000C0000,
Collectible = 0x00200000,
ContainsGCPointers = 0x01000000,
ContainsGenericVariables = 0x20000000,
HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size,
// otherwise the lower bits are used for WFLAGS_LOW
}
Expand Down Expand Up @@ -677,6 +681,14 @@ Contracts used:
return typeHandle.Flags.IsCollectible;
}

public bool ContainsGenericVariables(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
return _methodTables[typeHandle.Address].Flags.ContainsGenericVariables;
// For TypeDescs: Var/MVar are generic variables; for parameterized types (Ptr, ByRef),
// recurse through GetTypeParam; for FnPtr, check each signature type argument.
}

public bool HasTypeParam(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
Expand Down Expand Up @@ -1813,6 +1825,19 @@ uint GetFieldDescOffset(TargetPointer fieldDescPointer)
}
return DWord2 & (uint)FieldDescFlags2.OffsetMask;
}

TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer)
{
// Resolves the base pointer (GC or non-GC statics) for the enclosing type,
// then applies the field's metadata-based offset within that region.
// Uses GetGCStaticsBasePointer / GetNonGCStaticsBasePointer depending on the field's CorElementType.
}

TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread)
{
// Like GetFieldDescStaticAddress, but resolves thread-local base pointers instead.
// Uses GetGCThreadStaticsBasePointer / GetNonGCThreadStaticsBasePointer.
}
```

### Other APIs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ public interface IRuntimeTypeSystem : IContract
public bool IsClassInited(TypeHandle typeHandle) => throw new NotImplementedException();
public bool IsInitError(TypeHandle typeHandle) => throw new NotImplementedException();
bool IsGenericTypeDefinition(TypeHandle typeHandle) => throw new NotImplementedException();
bool ContainsGenericVariables(TypeHandle typeHandle) => throw new NotImplementedException();
bool IsCollectible(TypeHandle typeHandle) => throw new NotImplementedException();

bool HasTypeParam(TypeHandle typeHandle) => throw new NotImplementedException();
Expand Down Expand Up @@ -232,6 +233,7 @@ public interface IRuntimeTypeSystem : IContract
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef) => throw new NotImplementedException();
TargetPointer GetFieldDescByName(TypeHandle typeHandle, string fieldName) => throw new NotImplementedException();
TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer) => throw new NotImplementedException();
TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread) => throw new NotImplementedException();
#endregion FieldDesc inspection APIs
#region Other APIs
void GetCoreLibFieldDescAndDef(string typeNamespace, string typeName, string fieldName, out TargetPointer fieldDescAddr, out FieldDefinition fieldDef) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public static class CorDbgHResults
public const int CORDBG_E_NOTREADY = unchecked((int)0x80131c10);
public const int CORDBG_E_READVIRTUAL_FAILURE = unchecked((int)0x80131c49);
public const int ERROR_BUFFER_OVERFLOW = unchecked((int)0x8007006F); // HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW)
public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303);
}
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,34 @@ private TypeInstantiation(Target target, TargetPointer typePointer)
}

public bool IsGenericTypeDefinition(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsGenericTypeDefinition;
public bool ContainsGenericVariables(TypeHandle typeHandle)
{
if (typeHandle.IsTypeDesc())
{
CorElementType type = GetSignatureCorElementType(typeHandle);
if (type == CorElementType.Var || type == CorElementType.MVar)
return true;

else if (HasTypeParam(typeHandle))
{
return ContainsGenericVariables(GetRootTypeParam(typeHandle));
}

else if (type == CorElementType.FnPtr)
{
_ = IsFunctionPointer(typeHandle, out ReadOnlySpan<TypeHandle> signatureTypeArgs, out _);
foreach (TypeHandle sigTypeArg in signatureTypeArgs)
{
if (ContainsGenericVariables(sigTypeArg))
return true;
}
}

return false;
}
return _methodTables[typeHandle.Address].Flags.ContainsGenericVariables;
}

public bool IsCollectible(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsCollectible;
public bool HasTypeParam(TypeHandle typeHandle)
{
Expand Down Expand Up @@ -815,6 +843,16 @@ public TypeHandle GetTypeParam(TypeHandle typeHandle)
throw new ArgumentException(nameof(typeHandle));
}

private TypeHandle GetRootTypeParam(TypeHandle typeHandle)
{
TypeHandle current = typeHandle;
while (HasTypeParam(current))
{
current = GetTypeParam(current);
}
return current;
}

private bool GenericInstantiationMatch(TypeHandle genericType, TypeHandle potentialMatch, ImmutableArray<TypeHandle> typeArguments)
{
ReadOnlySpan<TypeHandle> instantiation = GetInstantiation(potentialMatch);
Expand Down Expand Up @@ -1822,7 +1860,7 @@ private TargetPointer GetStaticAddressHandle(TargetPointer @base, uint offset, b
return new TargetPointer(@base + offset);
}

TargetPointer IRuntimeTypeSystem.GetFieldDescStaticAddress(TargetPointer fieldDescPointer)
private TargetPointer GetFieldDescStaticOrThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer? thread = null)
{
TargetPointer enclosingMT = ((IRuntimeTypeSystem)this).GetMTOfEnclosingClass(fieldDescPointer);
TypeHandle ctx = GetTypeHandle(enclosingMT);
Expand All @@ -1833,13 +1871,30 @@ TargetPointer IRuntimeTypeSystem.GetFieldDescStaticAddress(TargetPointer fieldDe
TargetPointer @base;
if (type == CorElementType.Class || type == CorElementType.ValueType)
{
@base = GetGCStaticsBasePointer(ctx);
if (thread.HasValue)
{
@base = GetGCThreadStaticsBasePointer(ctx, thread.Value);
}
else
{
@base = GetGCStaticsBasePointer(ctx);
}
}
else
{
@base = GetNonGCStaticsBasePointer(ctx);
if (thread.HasValue)
{
@base = GetNonGCThreadStaticsBasePointer(ctx, thread.Value);
}
else
{
@base = GetNonGCStaticsBasePointer(ctx);
}
}

if (@base == TargetPointer.Null)
return TargetPointer.Null;

MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!;
uint token = ((IRuntimeTypeSystem)this).GetFieldDescMemberDef(fieldDescPointer);
FieldDefinitionHandle fieldHandle = (FieldDefinitionHandle)MetadataTokens.Handle((int)token);
Expand All @@ -1857,6 +1912,10 @@ TargetPointer IRuntimeTypeSystem.GetFieldDescStaticAddress(TargetPointer fieldDe
return handleAddr;
}

TargetPointer IRuntimeTypeSystem.GetFieldDescStaticAddress(TargetPointer fieldDescPointer) => GetFieldDescStaticOrThreadStaticAddress(fieldDescPointer);

TargetPointer IRuntimeTypeSystem.GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread) => GetFieldDescStaticOrThreadStaticAddress(fieldDescPointer, thread);

void IRuntimeTypeSystem.GetCoreLibFieldDescAndDef(string @namespace, string typeName, string fieldName, out TargetPointer fieldDescAddr, out FieldDefinition fieldDef)
{
ILoader loader = _target.Contracts.Loader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Microsoft.Diagnostics.DataContractReader;

internal static class EcmaMetadataUtils
public static class EcmaMetadataUtils
{
internal const int RowIdBitCount = 24;
internal const uint RIDMask = (1 << RowIdBitCount) - 1;
Expand All @@ -24,7 +24,7 @@ public enum TokenType : uint
mdtMethodDef = 0x06 << 24,
}

internal const uint TokenTypeMask = 0xff000000;
public const uint TokenTypeMask = 0xff000000;

public static uint CreateMethodDef(uint tokenParts)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ internal enum WFLAGS_HIGH : uint
Collectible = 0x00200000, // GC depends on this bit.

ContainsGCPointers = 0x01000000,
ContainsGenericVariables = 0x20000000,
HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size,
// otherwise the lower bits are used for WFLAGS_LOW
}
Expand Down Expand Up @@ -101,6 +102,7 @@ private bool TestFlagWithMask(WFLAGS2_ENUM mask, WFLAGS2_ENUM flag)
public bool IsCollectible => GetFlag(WFLAGS_HIGH.Collectible) != 0;
public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0;
public bool IsGenericTypeDefinition => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_TypicalInstantiation);
public bool ContainsGenericVariables => GetFlag(WFLAGS_HIGH.ContainsGenericVariables) != 0;

internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,31 @@ public int IsValueType(ulong vmTypeHandle, Interop.BOOL* pResult)
}

public int HasTypeParams(ulong vmTypeHandle, Interop.BOOL* pResult)
=> _legacy is not null ? _legacy.HasTypeParams(vmTypeHandle, pResult) : HResults.E_NOTIMPL;
{
*pResult = Interop.BOOL.FALSE;
int hr = HResults.S_OK;
try
{
Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
TypeHandle typeHandle = rts.GetTypeHandle(new TargetPointer(vmTypeHandle));
*pResult = rts.ContainsGenericVariables(typeHandle) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
Interop.BOOL resultLocal;
int hrLocal = _legacy.HasTypeParams(vmTypeHandle, &resultLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pResult == resultLocal, $"cDAC: {*pResult}, DAC: {resultLocal}");
}
#endif
return hr;
}

public int GetClassInfo(ulong vmAppDomain, ulong thExact, nint pData)
=> _legacy is not null ? _legacy.GetClassInfo(vmAppDomain, thExact, pData) : HResults.E_NOTIMPL;
Expand All @@ -699,7 +723,43 @@ public int GetObjectExpandedTypeInfoFromID(int boxed, ulong vmAppDomain, COR_TYP
=> _legacy is not null ? _legacy.GetObjectExpandedTypeInfoFromID(boxed, vmAppDomain, id, pTypeInfo) : HResults.E_NOTIMPL;

public int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal)
=> _legacy is not null ? _legacy.GetTypeHandle(vmModule, metadataToken, pRetVal) : HResults.E_NOTIMPL;
{
*pRetVal = 0;
int hr = HResults.S_OK;
try
{
Contracts.ILoader loader = _target.Contracts.Loader;
TargetPointer module = new TargetPointer(vmModule);
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(module);
Contracts.ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
switch ((EcmaMetadataUtils.TokenType)(metadataToken & EcmaMetadataUtils.TokenTypeMask))
{
case EcmaMetadataUtils.TokenType.mdtTypeDef:
*pRetVal = loader.GetModuleLookupMapElement(lookupTables.TypeDefToMethodTable, metadataToken, out var _).Value;
break;
case EcmaMetadataUtils.TokenType.mdtTypeRef:
*pRetVal = loader.GetModuleLookupMapElement(lookupTables.TypeRefToMethodTable, metadataToken, out var _).Value;
break;
default:
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_CLASS_NOT_LOADED)!;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
ulong retValLocal;
int hrLocal = _legacy.GetTypeHandle(vmModule, metadataToken, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == retValLocal, $"cDAC: {*pRetVal}, DAC: {retValLocal}");
}
#endif
return hr;
}

public int GetApproxTypeHandle(nint pTypeData, ulong* pRetVal)
=> _legacy is not null ? _legacy.GetApproxTypeHandle(pTypeData, pRetVal) : HResults.E_NOTIMPL;
Expand All @@ -711,7 +771,37 @@ public int GetMethodDescParams(ulong vmAppDomain, ulong vmMethodDesc, ulong gene
=> _legacy is not null ? _legacy.GetMethodDescParams(vmAppDomain, vmMethodDesc, genericsToken, pcGenericClassTypeParams, pGenericTypeParams) : HResults.E_NOTIMPL;

public int GetThreadStaticAddress(ulong vmField, ulong vmRuntimeThread, ulong* pRetVal)
=> _legacy is not null ? _legacy.GetThreadStaticAddress(vmField, vmRuntimeThread, pRetVal) : HResults.E_NOTIMPL;
{
*pRetVal = 0;
int hr = HResults.S_OK;
try
{
Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
TargetPointer fd = new TargetPointer(vmField);
if (vmRuntimeThread == 0)
throw new ArgumentException("vmRuntimeThread cannot be null for thread static fields");
if (!rts.IsFieldDescThreadStatic(fd))
{
throw new NotImplementedException();
}
*pRetVal = rts.GetFieldDescThreadStaticAddress(fd, new TargetPointer(vmRuntimeThread)).Value;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
ulong retValLocal;
int hrLocal = _legacy.GetThreadStaticAddress(vmField, vmRuntimeThread, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == retValLocal, $"cDAC: {*pRetVal}, DAC: {retValLocal}");
}
#endif
return hr;
}

public int GetCollectibleTypeStaticAddress(ulong vmField, ulong vmAppDomain, ulong* pRetVal)
=> _legacy is not null ? _legacy.GetCollectibleTypeStaticAddress(vmField, vmAppDomain, pRetVal) : HResults.E_NOTIMPL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Runtime.InteropServices.Marshalling;
using System.Text;

using Microsoft.Diagnostics.DataContractReader;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions;
using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
Expand Down Expand Up @@ -63,15 +64,6 @@ public sealed unsafe partial class SOSDacImpl
private readonly IXCLRDataProcess2? _legacyProcess2;
private readonly ICLRDataEnumMemoryRegions? _legacyEnumMemory;

private enum CorTokenType : uint
{
mdtTypeRef = 0x01000000,
mdtTypeDef = 0x02000000,
mdtFieldDef = 0x04000000,
mdtMethodDef = 0x06000000,
typeMask = 0xff000000,
}

public SOSDacImpl(Target target, object? legacyObj)
{
_target = target;
Expand Down Expand Up @@ -1155,7 +1147,7 @@ int ISOSDacInterface.GetFieldDescData(ClrDataAddress fieldDesc, DacpFieldDescDat
else
{
// otherwise we have not found the token here, but we can encode the underlying type in sigType
data->TokenOfType = (uint)CorTokenType.mdtTypeDef;
data->TokenOfType = (uint)EcmaMetadataUtils.TokenType.mdtTypeDef;
if (data->MTOfType == 0)
data->sigType = typeCode;
}
Expand Down Expand Up @@ -2572,18 +2564,18 @@ int ISOSDacInterface.GetMethodDescFromToken(ClrDataAddress moduleAddr, uint toke
TargetPointer module = moduleAddr.ToTargetPointer(_target);
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(module);
Contracts.ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
switch ((CorTokenType)token & CorTokenType.typeMask)
switch ((EcmaMetadataUtils.TokenType)(token & EcmaMetadataUtils.TokenTypeMask))
{
case CorTokenType.mdtFieldDef:
case EcmaMetadataUtils.TokenType.mdtFieldDef:
*methodDesc = loader.GetModuleLookupMapElement(lookupTables.FieldDefToDesc, token, out var _).ToClrDataAddress(_target);
break;
case CorTokenType.mdtMethodDef:
case EcmaMetadataUtils.TokenType.mdtMethodDef:
*methodDesc = loader.GetModuleLookupMapElement(lookupTables.MethodDefToDesc, token, out var _).ToClrDataAddress(_target);
break;
case CorTokenType.mdtTypeDef:
case EcmaMetadataUtils.TokenType.mdtTypeDef:
*methodDesc = loader.GetModuleLookupMapElement(lookupTables.TypeDefToMethodTable, token, out var _).ToClrDataAddress(_target);
break;
case CorTokenType.mdtTypeRef:
case EcmaMetadataUtils.TokenType.mdtTypeRef:
*methodDesc = loader.GetModuleLookupMapElement(lookupTables.TypeRefToMethodTable, token, out var _).ToClrDataAddress(_target);
break;
default:
Expand Down
Loading
Loading