|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata.Ecma335;
using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers;
using Microsoft.Diagnostics.DataContractReader.Data;
using System.Reflection.Metadata;
using System.Collections.Immutable;
using System.Reflection;
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
{
private const int TYPE_MASK_OFFSET = 27; // offset of type in field desc flags2
private readonly Target _target;
private readonly TargetPointer _freeObjectMethodTablePointer;
private readonly TargetPointer _continuationMethodTablePointer;
private readonly ulong _methodDescAlignment;
private readonly TypeValidation _typeValidation;
private readonly MethodValidation _methodValidation;
// TODO(cdac): we mutate this dictionary - copies of the RuntimeTypeSystem_1 struct share this instance.
// If we need to invalidate our view of memory, we should clear this dictionary.
private readonly Dictionary<TargetPointer, MethodTable> _methodTables = new();
private readonly Dictionary<TargetPointer, MethodDesc> _methodDescs = new();
private readonly Dictionary<TypeKey, TypeHandle> _typeHandles = new();
private readonly Dictionary<TypeKeyByName, TypeHandle> _typeHandlesByName = new();
public void Flush()
{
_methodTables.Clear();
_methodDescs.Clear();
_typeHandles.Clear();
_typeHandlesByName.Clear();
}
internal struct MethodTable
{
internal MethodTableFlags_1 Flags { get; }
internal ushort NumInterfaces { get; }
internal ushort NumVirtuals { get; }
internal TargetPointer ParentMethodTable { get; }
internal TargetPointer Module { get; }
internal TargetPointer EEClassOrCanonMT { get; }
internal TargetPointer PerInstInfo { get; }
internal TargetPointer AuxiliaryData { get; }
internal MethodTable(Data.MethodTable data)
{
Flags = new MethodTableFlags_1
{
MTFlags = data.MTFlags,
MTFlags2 = data.MTFlags2,
BaseSize = data.BaseSize,
};
NumInterfaces = data.NumInterfaces;
NumVirtuals = data.NumVirtuals;
EEClassOrCanonMT = data.EEClassOrCanonMT;
Module = data.Module;
ParentMethodTable = data.ParentMethodTable;
PerInstInfo = data.PerInstInfo;
AuxiliaryData = data.AuxiliaryData;
}
// this MethodTable is a canonical MethodTable if its EEClassOrCanonMT is an EEClass
internal bool IsCanonMT => MethodTableFlags_1.GetEEClassOrCanonMTBits(EEClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.EEClass;
}
private readonly struct TypeKey : IEquatable<TypeKey>
{
public TypeKey(TypeHandle typeHandle, CorElementType elementType, int rank, ImmutableArray<TypeHandle> typeArgs)
{
TypeHandle = typeHandle;
ElementType = elementType;
Rank = rank;
TypeArgs = typeArgs;
}
public TypeHandle TypeHandle { get; }
public CorElementType ElementType { get; }
public int Rank { get; }
public ImmutableArray<TypeHandle> TypeArgs { get; }
public bool Equals(TypeKey other)
{
if (ElementType != other.ElementType || Rank != other.Rank || TypeArgs.Length != other.TypeArgs.Length)
return false;
for (int i = 0; i < TypeArgs.Length; i++)
{
if (!TypeArgs[i].Equals(other.TypeArgs[i]))
return false;
}
return true;
}
public override bool Equals(object? obj) => obj is TypeKey other && Equals(other);
public override int GetHashCode()
{
int hash = HashCode.Combine(TypeHandle.GetHashCode(), (int)ElementType, Rank);
foreach (TypeHandle th in TypeArgs)
{
hash = HashCode.Combine(hash, th.GetHashCode());
}
return hash;
}
}
private readonly struct TypeKeyByName : IEquatable<TypeKeyByName>
{
public TypeKeyByName(string name, string namespaceName, TargetPointer module)
{
Name = name;
Namespace = namespaceName;
Module = module;
}
public string Name { get; }
public string Namespace { get; }
public TargetPointer Module { get; }
public bool Equals(TypeKeyByName other) => Name == other.Name && Namespace == other.Namespace && Module == other.Module;
public override bool Equals(object? obj) => obj is TypeKeyByName other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Name, Namespace, Module);
}
// Low order bits of TypeHandle address.
// If the low bits contain a 2, then it is a TypeDesc
[Flags]
internal enum TypeHandleBits
{
MethodTable = 0,
TypeDesc = 2,
ValidMask = 2,
}
internal enum InstantiatedMethodDescFlags2 : ushort
{
KindMask = 0x07,
GenericMethodDefinition = 0x01,
UnsharedMethodInstantiation = 0x02,
SharedMethodInstantiation = 0x03,
WrapperStubWithInstantiations = 0x04,
}
[Flags]
internal enum DynamicMethodDescExtendedFlags : uint
{
IsLCGMethod = 0x00004000,
IsILStub = 0x00008000,
ILStubTypeMask = 0x000007FF,
}
[Flags]
internal enum AsyncMethodFlags : uint
{
None = 0,
AsyncCall = 0x1,
Thunk = 16,
}
[Flags]
internal enum ILStubType : uint
{
StubPInvokeVarArg = 0x4,
StubCLRToCOMInterop = 0x6,
}
// on MethodDescChunk.FlagsAndTokenRange
[Flags]
internal enum MethodDescChunkFlags : ushort
{
// Has this chunk had its methods been determined eligible for tiered compilation or not
DeterminedIsEligibleForTieredCompilation = 0x4000,
// Is this chunk associated with a LoaderModule directly? If this flag is set, then the LoaderModule pointer is placed at the end of the chunk.
LoaderModuleAttachedToChunk = 0x8000,
}
internal enum MethodTableAuxiliaryFlags : uint
{
Initialized = 0x0001,
IsInitError = 0x0100,
IsNotFullyLoaded = 0x0040,
}
internal enum TypeDescFlags : uint
{
IsNotFullyLoaded = 0x00001000,
}
internal enum FieldDescFlags1 : uint
{
TokenMask = 0xffffff,
IsStatic = 0x1000000,
IsThreadStatic = 0x2000000,
IsRVA = 0x4000000,
}
internal enum FieldDescFlags2 : uint
{
TypeMask = 0xf8000000,
OffsetMask = 0x07ffffff,
}
internal struct MethodDesc
{
private readonly Data.MethodDesc _desc;
private readonly Data.MethodDescChunk _chunk;
private readonly Target _target;
internal TargetPointer Address { get; init; }
internal TargetPointer ChunkAddress { get; init; }
internal MethodDesc(Target target, TargetPointer methodDescPointer, Data.MethodDesc desc, TargetPointer methodDescChunkAddress, Data.MethodDescChunk chunk)
{
_target = target;
_desc = desc;
_chunk = chunk;
ChunkAddress = methodDescChunkAddress;
Address = methodDescPointer;
Token = ComputeToken(target, desc, chunk);
Size = ComputeSize(target, desc);
}
public TargetPointer MethodTable => _chunk.MethodTable;
public ushort Slot => _desc.Slot;
public uint Token { get; }
public uint Size { get; }
private static uint ComputeToken(Target target, Data.MethodDesc desc, Data.MethodDescChunk chunk)
{
int tokenRemainderBitCount = target.ReadGlobal<byte>(Constants.Globals.MethodDescTokenRemainderBitCount);
int tokenRangeBitCount = EcmaMetadataUtils.RowIdBitCount - tokenRemainderBitCount;
uint allRidBitsSet = EcmaMetadataUtils.RIDMask;
uint tokenRemainderMask = allRidBitsSet >> tokenRangeBitCount;
uint tokenRangeMask = allRidBitsSet >> tokenRemainderBitCount;
uint tokenRemainder = (uint)(desc.Flags3AndTokenRemainder & tokenRemainderMask);
uint tokenRange = ((uint)(chunk.FlagsAndTokenRange & tokenRangeMask)) << tokenRemainderBitCount;
return EcmaMetadataUtils.CreateMethodDef(tokenRange | tokenRemainder);
}
private static uint ComputeSize(Target target, Data.MethodDesc desc)
{
// See s_ClassificationSizeTable in method.cpp in the runtime for how the size is determined based on the method classification and flags.
uint baseSize;
switch ((MethodClassification)(desc.Flags & (ushort)MethodDescFlags_1.MethodDescFlags.ClassificationMask))
{
case MethodClassification.IL:
baseSize = target.GetTypeInfo(DataType.MethodDesc).Size ?? throw new InvalidOperationException("MethodDesc type size must be known");
break;
case MethodClassification.FCall:
baseSize = target.GetTypeInfo(DataType.FCallMethodDesc).Size ?? throw new InvalidOperationException("FCallMethodDesc type size must be known");
break;
case MethodClassification.PInvoke:
baseSize = target.GetTypeInfo(DataType.PInvokeMethodDesc).Size ?? throw new InvalidOperationException("PInvokeMethodDesc type size must be known");
break;
case MethodClassification.EEImpl:
baseSize = target.GetTypeInfo(DataType.EEImplMethodDesc).Size ?? throw new InvalidOperationException("EEImplMethodDesc type size must be known");
break;
case MethodClassification.Array:
baseSize = target.GetTypeInfo(DataType.ArrayMethodDesc).Size ?? throw new InvalidOperationException("ArrayMethodDesc type size must be known");
break;
case MethodClassification.Instantiated:
baseSize = target.GetTypeInfo(DataType.InstantiatedMethodDesc).Size ?? throw new InvalidOperationException("InstantiatedMethodDesc type size must be known");
break;
case MethodClassification.ComInterop:
baseSize = target.GetTypeInfo(DataType.CLRToCOMCallMethodDesc).Size ?? throw new InvalidOperationException("CLRToCOMCallMethodDesc type size must be known");
break;
case MethodClassification.Dynamic:
baseSize = target.GetTypeInfo(DataType.DynamicMethodDesc).Size ?? throw new InvalidOperationException("DynamicMethodDesc type size must be known");
break;
default:
throw new InvalidOperationException("Invalid method classification");
}
MethodDescFlags_1.MethodDescFlags flags = (MethodDescFlags_1.MethodDescFlags)desc.Flags;
if (flags.HasFlag(MethodDescFlags_1.MethodDescFlags.HasNonVtableSlot))
baseSize += target.GetTypeInfo(DataType.NonVtableSlot).Size ?? throw new InvalidOperationException("NonVtableSlot type size must be known");
if (flags.HasFlag(MethodDescFlags_1.MethodDescFlags.HasMethodImpl))
baseSize += target.GetTypeInfo(DataType.MethodImpl).Size ?? throw new InvalidOperationException("MethodImpl type size must be known");
if (flags.HasFlag(MethodDescFlags_1.MethodDescFlags.HasNativeCodeSlot))
baseSize += target.GetTypeInfo(DataType.NativeCodeSlot).Size ?? throw new InvalidOperationException("NativeCodeSlot type size must be known");
if (flags.HasFlag(MethodDescFlags_1.MethodDescFlags.HasAsyncMethodData))
baseSize += target.GetTypeInfo(DataType.AsyncMethodData).Size ?? throw new InvalidOperationException("AsyncMethodData type size must be known");
return baseSize;
}
public MethodClassification Classification => (MethodClassification)((int)_desc.Flags & (int)MethodDescFlags_1.MethodDescFlags.ClassificationMask);
private bool HasFlags(MethodDescFlags_1.MethodDescFlags flags) => (_desc.Flags & (ushort)flags) != 0;
private bool HasFlags(MethodDescFlags_1.MethodDescFlags3 flags) => (_desc.Flags3AndTokenRemainder & (ushort)flags) != 0;
internal bool HasFlags(MethodDescChunkFlags flags) => (_chunk.FlagsAndTokenRange & (ushort)flags) != 0;
public bool IsEligibleForTieredCompilation => HasFlags(MethodDescFlags_1.MethodDescFlags3.IsEligibleForTieredCompilation);
public bool IsUnboxingStub => HasFlags(MethodDescFlags_1.MethodDescFlags3.IsUnboxingStub);
public TargetPointer CodeData => _desc.CodeData;
public TargetPointer? GCCoverageInfo => _desc.GCCoverageInfo;
public bool IsIL => Classification == MethodClassification.IL || Classification == MethodClassification.Instantiated;
internal bool HasNonVtableSlot => MethodDescOptionalSlots.HasNonVtableSlot(_desc.Flags);
internal bool HasNativeCodeSlot => MethodDescOptionalSlots.HasNativeCodeSlot(_desc.Flags);
internal bool HasAsyncMethodData => MethodDescOptionalSlots.HasAsyncMethodData(_desc.Flags);
internal bool HasStableEntryPoint => HasFlags(MethodDescFlags_1.MethodDescFlags3.HasStableEntryPoint);
internal bool HasPrecode => HasFlags(MethodDescFlags_1.MethodDescFlags3.HasPrecode);
internal bool IsStatic => HasFlags(MethodDescFlags_1.MethodDescFlags.Static);
internal TargetPointer GetAddressOfNonVtableSlot() => MethodDescOptionalSlots.GetAddressOfNonVtableSlot(Address, Classification, _desc.Flags, _target);
internal TargetPointer GetAddressOfNativeCodeSlot() => MethodDescOptionalSlots.GetAddressOfNativeCodeSlot(Address, Classification, _desc.Flags, _target);
internal TargetPointer GetAddressOfAsyncMethodData() => MethodDescOptionalSlots.GetAddressOfAsyncMethodData(Address, Classification, _desc.Flags, _target);
internal bool IsLoaderModuleAttachedToChunk => HasFlags(MethodDescChunkFlags.LoaderModuleAttachedToChunk);
public ulong SizeOfChunk
{
get
{
ulong typeSize = _target.GetTypeInfo(DataType.MethodDescChunk).Size!.Value;
ulong chunkSize = (ulong)(_chunk.Size + 1) * _target.ReadGlobal<ulong>(Constants.Globals.MethodDescAlignment);
ulong extra = IsLoaderModuleAttachedToChunk ? (ulong)_target.PointerSize : 0;
return typeSize + chunkSize + extra;
}
}
}
private enum OptimizationTier_1 : uint
{
OptimizationTier0,
OptimizationTier1,
OptimizationTier1OSR,
OptimizationTierOptimized,
OptimizationTier0Instrumented,
OptimizationTier1Instrumented,
OptimizationTierUnknown = 0xFFFFFFFF
}
private sealed class InstantiatedMethodDesc : IData<InstantiatedMethodDesc>
{
public static InstantiatedMethodDesc Create(Target target, TargetPointer address) => new InstantiatedMethodDesc(target, address);
private readonly TargetPointer _address;
private readonly Data.InstantiatedMethodDesc _desc;
private InstantiatedMethodDesc(Target target, TargetPointer methodDescPointer)
{
_address = methodDescPointer;
RuntimeTypeSystem_1 rts = (RuntimeTypeSystem_1)target.Contracts.RuntimeTypeSystem;
_desc = target.ProcessedData.GetOrAdd<Data.InstantiatedMethodDesc>(methodDescPointer);
int numGenericArgs = _desc.NumGenericArgs;
TargetPointer perInstInfo = _desc.PerInstInfo;
if ((perInstInfo == TargetPointer.Null) || (numGenericArgs == 0))
{
Instantiation = System.Array.Empty<TypeHandle>();
}
else
{
Instantiation = new TypeHandle[numGenericArgs];
for (int i = 0; i < numGenericArgs; i++)
{
Instantiation[i] = rts.GetTypeHandle(target.ReadPointer(perInstInfo + (ulong)target.PointerSize * (ulong)i));
}
}
}
private bool HasFlags(InstantiatedMethodDescFlags2 mask, InstantiatedMethodDescFlags2 flags) => (_desc.Flags2 & (ushort)mask) == (ushort)flags;
internal bool IsWrapperStubWithInstantiations => HasFlags(InstantiatedMethodDescFlags2.KindMask, InstantiatedMethodDescFlags2.WrapperStubWithInstantiations);
internal bool IsGenericMethodDefinition => HasFlags(InstantiatedMethodDescFlags2.KindMask, InstantiatedMethodDescFlags2.GenericMethodDefinition);
internal bool HasPerInstInfo => _desc.PerInstInfo != TargetPointer.Null;
internal bool HasMethodInstantiation => IsGenericMethodDefinition || HasPerInstInfo;
public TypeHandle[] Instantiation { get; }
}
private sealed class DynamicMethodDesc : IData<DynamicMethodDesc>
{
public static DynamicMethodDesc Create(Target target, TargetPointer address) => new DynamicMethodDesc(target, address);
private readonly TargetPointer _address;
private readonly Data.DynamicMethodDesc _desc;
private readonly Data.StoredSigMethodDesc _storedSigDesc;
private DynamicMethodDesc(Target target, TargetPointer methodDescPointer)
{
_address = methodDescPointer;
_desc = target.ProcessedData.GetOrAdd<Data.DynamicMethodDesc>(methodDescPointer);
MethodName = _desc.MethodName != TargetPointer.Null
? target.ReadUtf8String(_desc.MethodName)
: string.Empty;
_storedSigDesc = target.ProcessedData.GetOrAdd<Data.StoredSigMethodDesc>(methodDescPointer);
}
public string MethodName { get; }
public DynamicMethodDescExtendedFlags ExtendedFlags => (DynamicMethodDescExtendedFlags)_storedSigDesc.ExtendedFlags;
public bool IsDynamicMethod => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod);
public bool IsILStub => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsILStub);
public ILStubType ILStubType => (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask);
public bool IsCLRToCOMStub => ILStubType == ILStubType.StubCLRToCOMInterop;
public bool IsPInvokeVarArgStub => ILStubType == ILStubType.StubPInvokeVarArg;
public bool HasMDContextArg => IsCLRToCOMStub || IsPInvokeVarArgStub;
}
private sealed class StoredSigMethodDesc : IData<StoredSigMethodDesc>
{
public static StoredSigMethodDesc Create(Target target, TargetPointer address) => new StoredSigMethodDesc(target, address);
public byte[] Signature { get; }
private StoredSigMethodDesc(Target target, TargetPointer methodDescPointer)
{
Data.StoredSigMethodDesc storedSigMethodDesc = target.ProcessedData.GetOrAdd<Data.StoredSigMethodDesc>(methodDescPointer);
Signature = new byte[storedSigMethodDesc.cSig];
target.ReadBuffer(storedSigMethodDesc.Sig, Signature.AsSpan());
}
}
internal RuntimeTypeSystem_1(Target target)
{
_target = target;
_freeObjectMethodTablePointer = target.ReadPointer(
target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable));
_continuationMethodTablePointer = target.ReadPointer(
target.ReadGlobalPointer(Constants.Globals.ContinuationMethodTable));
_methodDescAlignment = target.ReadGlobal<ulong>(Constants.Globals.MethodDescAlignment);
_typeValidation = new TypeValidation(target, _continuationMethodTablePointer);
_methodValidation = new MethodValidation(target, _methodDescAlignment);
_methodValidation.SetMethodTableQueries(new NonValidatedMethodTableQueries(this));
}
internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer;
internal TargetPointer ContinuationMethodTablePointer => _continuationMethodTablePointer;
internal ulong MethodDescAlignment => _methodDescAlignment;
public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
{
TypeHandleBits addressLowBits = (TypeHandleBits)((ulong)typeHandlePointer & ((ulong)_target.PointerSize - 1));
if ((addressLowBits != TypeHandleBits.MethodTable) && (addressLowBits != TypeHandleBits.TypeDesc))
{
throw new ArgumentException("Invalid type handle pointer", nameof(typeHandlePointer));
}
// if we already validated this address, return a handle
if (_methodTables.ContainsKey(typeHandlePointer))
{
return new TypeHandle(typeHandlePointer);
}
// Check for a TypeDesc
if (addressLowBits == TypeHandleBits.TypeDesc)
{
// This is a TypeDesc
return new TypeHandle(typeHandlePointer);
}
TargetPointer methodTablePointer = typeHandlePointer;
// Check if we cached the underlying data already
if (_target.ProcessedData.TryGet(methodTablePointer, out Data.MethodTable? methodTableData))
{
// we already cached the data, we must have validated the address, create the representation struct for our use
MethodTable trustedMethodTable = new MethodTable(methodTableData);
_ = _methodTables.TryAdd(methodTablePointer, trustedMethodTable);
return new TypeHandle(methodTablePointer);
}
// If it's the free object method table, we trust it to be valid
if (methodTablePointer == FreeObjectMethodTablePointer)
{
Data.MethodTable freeObjectMethodTableData = _target.ProcessedData.GetOrAdd<Data.MethodTable>(methodTablePointer);
MethodTable trustedMethodTable = new MethodTable(freeObjectMethodTableData);
_ = _methodTables.TryAdd(methodTablePointer, trustedMethodTable);
return new TypeHandle(methodTablePointer);
}
// Otherwse, get ready to validate
if (!_typeValidation.TryValidateMethodTablePointer(methodTablePointer))
{
throw new ArgumentException("Invalid method table pointer", nameof(typeHandlePointer));
}
// ok, we validated it, cache the data and add the MethodTable_1 struct to the dictionary
Data.MethodTable trustedMethodTableData = _target.ProcessedData.GetOrAdd<Data.MethodTable>(methodTablePointer);
MethodTable trustedMethodTableF = new MethodTable(trustedMethodTableData);
_ = _methodTables.TryAdd(methodTablePointer, trustedMethodTableF);
return new TypeHandle(methodTablePointer);
}
public TargetPointer GetModule(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
{
return _methodTables[typeHandle.Address].Module;
}
else if (typeHandle.IsTypeDesc())
{
if (HasTypeParam(typeHandle))
{
return GetModule(GetTypeParam(typeHandle));
}
else if (IsGenericVariable(typeHandle, out TargetPointer genericParamModule, out _))
{
return genericParamModule;
}
else
{
System.Diagnostics.Debug.Assert(IsFunctionPointer(typeHandle, out _, out _));
return TargetPointer.Null;
}
}
else
{
return TargetPointer.Null;
}
}
public TargetPointer GetCanonicalMethodTable(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(typeHandle).MethodTable;
public TargetPointer GetParentMethodTable(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : _methodTables[typeHandle.Address].ParentMethodTable;
public uint GetBaseSize(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[typeHandle.Address].Flags.BaseSize;
public uint GetComponentSize(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[typeHandle.Address].Flags.ComponentSize;
private TargetPointer GetClassPointer(TypeHandle typeHandle)
{
MethodTable methodTable = _methodTables[typeHandle.Address];
switch (MethodTableFlags_1.GetEEClassOrCanonMTBits(methodTable.EEClassOrCanonMT))
{
case MethodTableFlags_1.EEClassOrCanonMTBits.EEClass:
return methodTable.EEClassOrCanonMT;
case MethodTableFlags_1.EEClassOrCanonMTBits.CanonMT:
TargetPointer canonMTPtr = MethodTableFlags_1.UntagEEClassOrCanonMT(methodTable.EEClassOrCanonMT);
TypeHandle canonMTHandle = GetTypeHandle(canonMTPtr);
MethodTable canonMT = _methodTables[canonMTHandle.Address];
return canonMT.EEClassOrCanonMT; // canonical method table EEClassOrCanonMT is always EEClass
default:
throw new InvalidOperationException();
}
}
// only called on validated method tables, so we don't need to re-validate the EEClass
private Data.EEClass GetClassData(TypeHandle typeHandle)
{
TargetPointer clsPtr = GetClassPointer(typeHandle);
return _target.ProcessedData.GetOrAdd<Data.EEClass>(clsPtr);
}
public bool IsFreeObjectMethodTable(TypeHandle typeHandle) => FreeObjectMethodTablePointer == 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 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 IsContinuation(TypeHandle typeHandle) => typeHandle.IsMethodTable()
&& _continuationMethodTablePointer != TargetPointer.Null
&& _methodTables[typeHandle.Address].ParentMethodTable == _continuationMethodTablePointer;
IEnumerable<(uint Offset, uint Size)> IRuntimeTypeSystem.GetGCDescSeries(TypeHandle typeHandle, uint numComponents)
{
if (!typeHandle.IsMethodTable())
yield break;
if (!ContainsGCPointers(typeHandle))
yield break;
uint baseSize = GetBaseSize(typeHandle);
uint componentSize = GetComponentSize(typeHandle);
uint objectSize = baseSize + numComponents * componentSize;
ulong mtAddress = typeHandle.Address;
ulong pointerSize = (ulong)_target.PointerSize;
// Sign-extend NumSeries from native pointer width.
long numSeries = _target.PointerSize == sizeof(uint)
? (long)(int)_target.ReadPointer(mtAddress - pointerSize).Value
: (long)_target.ReadPointer(mtAddress - pointerSize).Value;
if (numSeries == 0)
yield break;
if (numSeries > 0)
{
// Regular series: iterate from highest (closest to MT) to lowest.
for (ulong i = 0; i < (ulong)numSeries; i++)
{
ulong seriesBase = mtAddress - (3 + 2 * i) * pointerSize;
ulong rawSeriesSize = _target.ReadPointer(seriesBase).Value;
ulong seriesOffset = _target.ReadPointer(seriesBase + pointerSize).Value;
yield return ((uint)seriesOffset, (uint)(rawSeriesSize + objectSize));
}
}
else
{
long absNumSeries = -numSeries;
ulong startOffset = _target.ReadPointer(mtAddress - 2 * pointerSize).Value;
var seriesItems = new (uint Nptrs, uint Skip)[absNumSeries];
for (long i = 0; i < absNumSeries; i++)
{
ulong itemAddress = mtAddress - (3 + (ulong)i) * pointerSize;
// Read val_serie_item fields individually for endianness safety.
uint nptrs, skip;
if (_target.PointerSize == sizeof(uint))
{
nptrs = _target.Read<ushort>(itemAddress);
skip = _target.Read<ushort>(itemAddress + sizeof(ushort));
}
else
{
nptrs = _target.Read<uint>(itemAddress);
skip = _target.Read<uint>(itemAddress + sizeof(uint));
}
seriesItems[i] = (nptrs, skip);
}
ulong currentOffset = startOffset;
for (int i = 0; i < numComponents; i++)
{
for (long j = 0; j < absNumSeries; j++)
{
if (currentOffset > objectSize - pointerSize)
yield break;
uint runBytes = seriesItems[j].Nptrs * (uint)pointerSize;
yield return ((uint)currentOffset, runBytes);
currentOffset += runBytes + seriesItems[j].Skip;
}
}
}
}
public bool IsDynamicStatics(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsDynamicStatics;
public ushort GetNumInterfaces(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : _methodTables[typeHandle.Address].NumInterfaces;
public uint GetTypeDefToken(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
return 0;
MethodTable methodTable = _methodTables[typeHandle.Address];
return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
}
public ushort GetNumVtableSlots(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
return 0;
MethodTable methodTable = _methodTables[typeHandle.Address];
ushort numNonVirtualSlots = methodTable.IsCanonMT ? GetClassData(typeHandle).NumNonVirtualSlots : (ushort)0;
return checked((ushort)(methodTable.NumVirtuals + numNonVirtualSlots));
}
public ushort GetNumMethods(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumMethods;
public uint GetTypeDefTypeAttributes(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : GetClassData(typeHandle).CorTypeAttr;
public ushort GetNumInstanceFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumInstanceFields;
public ushort GetNumStaticFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumStaticFields;
public ushort GetNumThreadStaticFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumThreadStaticFields;
public TargetPointer GetFieldDescList(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(typeHandle).FieldDescList;
public bool IsTrackedReferenceWithFinalizer(TypeHandle typeHandle) => typeHandle.IsMethodTable() && _methodTables[typeHandle.Address].Flags.IsTrackedReferenceWithFinalizer;
private TargetPointer GetDynamicStaticsInfo(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
return default;
MethodTable methodTable = _methodTables[typeHandle.Address];
if (!methodTable.Flags.IsDynamicStatics)
return default;
TargetPointer dynamicStaticsInfoSize = _target.GetTypeInfo(DataType.DynamicStaticsInfo).Size!.Value;
TargetPointer dynamicStaticsInfoAddr = methodTable.AuxiliaryData - dynamicStaticsInfoSize;
return dynamicStaticsInfoAddr;
}
private Data.ThreadStaticsInfo GetThreadStaticsInfo(TypeHandle typeHandle)
{
MethodTable methodTable = _methodTables[typeHandle.Address];
TargetPointer threadStaticsInfoSize = _target.GetTypeInfo(DataType.ThreadStaticsInfo).Size!.Value;
TargetPointer threadStaticsInfoAddr = methodTable.AuxiliaryData - threadStaticsInfoSize;
Data.ThreadStaticsInfo threadStaticsInfo = _target.ProcessedData.GetOrAdd<Data.ThreadStaticsInfo>(threadStaticsInfoAddr);
return threadStaticsInfo;
}
public TargetPointer GetGCThreadStaticsBasePointer(TypeHandle typeHandle, TargetPointer threadPtr)
{
if (!typeHandle.IsMethodTable())
return TargetPointer.Null;
TargetPointer tlsIndexPtr = GetThreadStaticsInfo(typeHandle).GCTlsIndex;
Contracts.IThread threadContract = _target.Contracts.Thread;
return threadContract.GetThreadLocalStaticBase(threadPtr, tlsIndexPtr);
}
public TargetPointer GetNonGCThreadStaticsBasePointer(TypeHandle typeHandle, TargetPointer threadPtr)
{
if (!typeHandle.IsMethodTable())
return TargetPointer.Null;
TargetPointer tlsIndexPtr = GetThreadStaticsInfo(typeHandle).NonGCTlsIndex;
Contracts.IThread threadContract = _target.Contracts.Thread;
return threadContract.GetThreadLocalStaticBase(threadPtr, tlsIndexPtr);
}
public TargetPointer GetGCStaticsBasePointer(TypeHandle typeHandle)
{
TargetPointer dynamicStaticsInfoAddr = GetDynamicStaticsInfo(typeHandle);
if (dynamicStaticsInfoAddr == TargetPointer.Null)
return TargetPointer.Null;
Data.DynamicStaticsInfo dynamicStaticsInfo = _target.ProcessedData.GetOrAdd<Data.DynamicStaticsInfo>(dynamicStaticsInfoAddr);
return dynamicStaticsInfo.GCStatics;
}
public TargetPointer GetNonGCStaticsBasePointer(TypeHandle typeHandle)
{
TargetPointer dynamicStaticsInfoAddr = GetDynamicStaticsInfo(typeHandle);
if (dynamicStaticsInfoAddr == TargetPointer.Null)
return TargetPointer.Null;
Data.DynamicStaticsInfo dynamicStaticsInfo = _target.ProcessedData.GetOrAdd<Data.DynamicStaticsInfo>(dynamicStaticsInfoAddr);
return dynamicStaticsInfo.NonGCStatics;
}
public ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
return default;
MethodTable methodTable = _methodTables[typeHandle.Address];
if (!methodTable.Flags.HasInstantiation)
return default;
return _target.ProcessedData.GetOrAdd<TypeInstantiation>(typeHandle.Address).TypeHandles;
}
public bool IsClassInited(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
return false;
MethodTable methodTable = _methodTables[typeHandle.Address];
MethodTableAuxiliaryData auxiliaryData = _target.ProcessedData.GetOrAdd<MethodTableAuxiliaryData>(methodTable.AuxiliaryData);
return (auxiliaryData.Flags & (uint)MethodTableAuxiliaryFlags.Initialized) != 0;
}
public bool IsInitError(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
return false;
MethodTable methodTable = _methodTables[typeHandle.Address];
MethodTableAuxiliaryData auxiliaryData = _target.ProcessedData.GetOrAdd<MethodTableAuxiliaryData>(methodTable.AuxiliaryData);
return (auxiliaryData.Flags & (uint)MethodTableAuxiliaryFlags.IsInitError) != 0;
}
private sealed class TypeInstantiation : IData<TypeInstantiation>
{
public static TypeInstantiation Create(Target target, TargetPointer address) => new TypeInstantiation(target, address);
public TypeHandle[] TypeHandles { get; }
private TypeInstantiation(Target target, TargetPointer typePointer)
{
RuntimeTypeSystem_1 rts = (RuntimeTypeSystem_1)target.Contracts.RuntimeTypeSystem;
MethodTable methodTable = rts._methodTables[typePointer];
Debug.Assert(methodTable.Flags.HasInstantiation);
TargetPointer perInstInfo = methodTable.PerInstInfo;
TargetPointer genericsDictInfoAddr = perInstInfo - (ulong)target.PointerSize;
GenericsDictInfo genericsDictInfo = target.ProcessedData.GetOrAdd<GenericsDictInfo>(genericsDictInfoAddr);
// Use the last dictionary. This corresponds to the specific type - any previous ones are for superclasses
// See PerInstInfo in methodtable.h for details in coreclr
TargetPointer dictionaryPointer = target.ReadPointer(perInstInfo + (ulong)target.PointerSize * (ulong)(genericsDictInfo.NumDicts - 1));
int numberOfGenericArgs = genericsDictInfo.NumTypeArgs;
TypeHandles = new TypeHandle[numberOfGenericArgs];
for (int i = 0; i < numberOfGenericArgs; i++)
{
TypeHandles[i] = rts.GetTypeHandle(target.ReadPointer(dictionaryPointer + (ulong)target.PointerSize * (ulong)i));
}
}
}
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)
{
if (typeHandle.IsMethodTable())
{
MethodTable methodTable = _methodTables[typeHandle.Address];
return methodTable.Flags.IsArray;
}
else if (typeHandle.IsTypeDesc())
{
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
switch (elemType)
{
case CorElementType.ValueType:
case CorElementType.Byref:
case CorElementType.Ptr:
return true;
}
}
return false;
}
public CorElementType GetSignatureCorElementType(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
{
MethodTable methodTable = _methodTables[typeHandle.Address];
switch (methodTable.Flags.GetFlag(MethodTableFlags_1.WFLAGS_HIGH.Category_Mask))
{
case MethodTableFlags_1.WFLAGS_HIGH.Category_Array:
return CorElementType.Array;
case MethodTableFlags_1.WFLAGS_HIGH.Category_Array | MethodTableFlags_1.WFLAGS_HIGH.Category_IfArrayThenSzArray:
return CorElementType.SzArray;
case MethodTableFlags_1.WFLAGS_HIGH.Category_ValueType:
case MethodTableFlags_1.WFLAGS_HIGH.Category_Nullable:
case MethodTableFlags_1.WFLAGS_HIGH.Category_PrimitiveValueType:
return CorElementType.ValueType;
case MethodTableFlags_1.WFLAGS_HIGH.Category_TruePrimitive:
return (CorElementType)GetClassData(typeHandle).InternalCorElementType;
default:
return CorElementType.Class;
}
}
else if (typeHandle.IsTypeDesc())
{
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
return (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
}
return default;
}
public CorElementType GetInternalCorElementType(TypeHandle typeHandle)
{
CorElementType sigType = GetSignatureCorElementType(typeHandle);
if (sigType == CorElementType.ValueType && typeHandle.IsMethodTable())
{
CorElementType internalType = (CorElementType)GetClassData(typeHandle).InternalCorElementType;
if (internalType != CorElementType.ValueType)
return internalType;
}
return sigType;
}
public bool IsValueType(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
{
MethodTable methodTable = _methodTables[typeHandle.Address];
return methodTable.Flags.IsValueType;
}
else if (typeHandle.IsTypeDesc())
{
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
return (CorElementType)(typeDesc.TypeAndFlags & 0xFF) == CorElementType.ValueType;
}
return false;
}
public bool IsEnum(TypeHandle typeHandle)
{
// Enums have Category_PrimitiveValueType in their MethodTable flags and their
// InternalCorElementType is a primitive type (I1, U1, I2, U2, I4, U4, I8, U8),
// not ValueType. Regular primitive value types (IntPtr/UIntPtr) have Category_TruePrimitive.
if (!typeHandle.IsMethodTable())
return false;
CorElementType sigType = GetSignatureCorElementType(typeHandle);
if (sigType != CorElementType.ValueType)
return false;
CorElementType internalType = (CorElementType)GetClassData(typeHandle).InternalCorElementType;
return internalType != CorElementType.ValueType;
}
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
public bool IsArray(TypeHandle typeHandle, out uint rank)
{
if (typeHandle.IsMethodTable())
{
MethodTable methodTable = _methodTables[typeHandle.Address];
switch (methodTable.Flags.GetFlag(MethodTableFlags_1.WFLAGS_HIGH.Category_Mask))
{
case MethodTableFlags_1.WFLAGS_HIGH.Category_Array:
// Multidim array: BaseSize = ArrayBaseSize + Rank * sizeof(uint) * 2
uint arrayBaseSize = _target.ReadGlobal<uint>(Constants.Globals.ArrayBaseSize);
uint boundsSize = methodTable.Flags.BaseSize - arrayBaseSize;
rank = boundsSize / (sizeof(uint) * 2);
return true;
case MethodTableFlags_1.WFLAGS_HIGH.Category_Array | MethodTableFlags_1.WFLAGS_HIGH.Category_IfArrayThenSzArray:
rank = 1;
return true;
}
}
rank = 0;
return false;
}
public TypeHandle GetTypeParam(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
{
MethodTable methodTable = _methodTables[typeHandle.Address];
if (!methodTable.Flags.IsArray)
throw new ArgumentException(nameof(typeHandle));
return GetTypeHandle(methodTable.PerInstInfo);
}
else if (typeHandle.IsTypeDesc())
{
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
switch (elemType)
{
case CorElementType.ValueType:
case CorElementType.Byref:
case CorElementType.Ptr:
ParamTypeDesc paramTypeDesc = _target.ProcessedData.GetOrAdd<ParamTypeDesc>(typeHandle.TypeDescAddress());
return GetTypeHandle(paramTypeDesc.TypeArg);
}
}
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);
if (instantiation.Length != typeArguments.Length)
return false;
if (GetTypeDefToken(genericType) != GetTypeDefToken(potentialMatch))
return false;
if (GetModule(genericType) != GetModule(potentialMatch))
return false;
for (int i = 0; i < instantiation.Length; i++)
{
if (!(instantiation[i].Address == typeArguments[i].Address))
return false;
}
return true;
}
private bool ArrayPtrMatch(TypeHandle elementType, CorElementType corElementType, int rank, TypeHandle potentialMatch)
{
IsArray(potentialMatch, out uint typeHandleRank);
return GetSignatureCorElementType(potentialMatch) == corElementType &&
GetTypeParam(potentialMatch).Address == elementType.Address &&
(corElementType == CorElementType.SzArray || corElementType == CorElementType.Byref ||
corElementType == CorElementType.Ptr || (rank == typeHandleRank));
}
private bool IsLoaded(TypeHandle typeHandle)
{
if (typeHandle.Address == TargetPointer.Null)
return false;
if (typeHandle.IsTypeDesc())
{
Data.TypeDesc typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
return (typeDesc.TypeAndFlags & (uint)TypeDescFlags.IsNotFullyLoaded) == 0; // IsUnloaded
}
MethodTable methodTable = _methodTables[typeHandle.Address];
Data.MethodTableAuxiliaryData auxData = _target.ProcessedData.GetOrAdd<Data.MethodTableAuxiliaryData>(methodTable.AuxiliaryData);
return (auxData.Flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0; // IsUnloaded
}
TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments)
{
if (typeHandle.Address == TargetPointer.Null)
return new TypeHandle(TargetPointer.Null);
if (_typeHandles.TryGetValue(new TypeKey(typeHandle, corElementType, rank, typeArguments), out TypeHandle existing))
return existing;
ILoader loaderContract = _target.Contracts.Loader;
TargetPointer loaderModule = GetLoaderModule(typeHandle);
ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule);
TypeHandle potentialMatch;
foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle))
{
potentialMatch = GetTypeHandle(ptr);
if (corElementType == CorElementType.GenericInst)
{
if (GenericInstantiationMatch(typeHandle, potentialMatch, typeArguments) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
return potentialMatch;
}
}
else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
return potentialMatch;
}
}
return new TypeHandle(TargetPointer.Null);
}
TypeHandle IRuntimeTypeSystem.GetPrimitiveType(CorElementType typeCode)
{
TargetPointer coreLib = _target.ReadGlobalPointer(Constants.Globals.CoreLib);
CoreLibBinder coreLibData = _target.ProcessedData.GetOrAdd<CoreLibBinder>(coreLib);
TargetPointer typeHandlePtr = _target.ReadPointer(coreLibData.Classes + (ulong)typeCode * (ulong)_target.PointerSize);
return GetTypeHandle(typeHandlePtr);
}
private static bool ModuleMatch(AssemblyReference assemblyRef, AssemblyDefinition assemblyDef)
{
AssemblyName assemblyRefName = assemblyRef.GetAssemblyName();
AssemblyName assemblyDefName = assemblyDef.GetAssemblyName();
if ((assemblyRefName.Name != assemblyDefName.Name) ||
(assemblyRefName.Version != assemblyDefName.Version) ||
(assemblyRefName.CultureName != assemblyDefName.CultureName))
{
return false;
}
ReadOnlySpan<byte> refToken = assemblyRefName.GetPublicKeyToken();
ReadOnlySpan<byte> defToken = assemblyDefName.GetPublicKeyToken();
return refToken.SequenceEqual(defToken);
}
private MetadataReader? LookForHandle(AssemblyReference exportedAssemblyRef)
{
TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
TargetPointer appDomain = _target.ReadPointer(appDomainPointer);
foreach (ModuleHandle mdhandle in _target.Contracts.Loader.GetModuleHandles(appDomain, AssemblyIterationFlags.IncludeLoaded))
{
MetadataReader? md2 = _target.Contracts.EcmaMetadata.GetMetadata(mdhandle);
if (md2 == null)
continue;
AssemblyDefinition assemblyDefinition = md2.GetAssemblyDefinition();
if (ModuleMatch(exportedAssemblyRef, assemblyDefinition))
{
return md2;
}
}
return null;
}
TypeHandle IRuntimeTypeSystem.GetTypeByNameAndModule(string name, string nameSpace, ModuleHandle moduleHandle)
{
ILoader loader = _target.Contracts.Loader;
TargetPointer modulePtr = loader.GetModule(moduleHandle);
if (_typeHandlesByName.TryGetValue(new TypeKeyByName(name, nameSpace, modulePtr), out TypeHandle existing))
return existing;
string[] parts = name.Split('+');
string outerName = parts[0];
MetadataReader? md = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle);
TypeDefinitionHandle currentHandle = default;
// create a hash set of MDs and if we come across the same one more than once in a loop then we return null typehandle
HashSet<MetadataReader> seenMDs = new();
// 1. find the outer type
while (md != null && seenMDs.Add(md))
{
foreach (TypeDefinitionHandle typeDefHandle in md.TypeDefinitions)
{
TypeDefinition typedef = md.GetTypeDefinition(typeDefHandle);
if (md.GetString(typedef.Name) == outerName && md.GetString(typedef.Namespace) == nameSpace)
{
// found our outermost type, remember it
currentHandle = typeDefHandle;
break;
}
}
if (currentHandle == default)
{
// look for forwarded types
foreach (ExportedTypeHandle exportedTypeHandle in md.ExportedTypes)
{
ExportedType exportedType = md.GetExportedType(exportedTypeHandle);
if (exportedType.Implementation.Kind != HandleKind.AssemblyReference || !exportedType.IsForwarder)
continue;
if (md.GetString(exportedType.Name) == outerName && md.GetString(exportedType.Namespace) == nameSpace)
{
// get the assembly ref for target
AssemblyReferenceHandle arefHandle = (AssemblyReferenceHandle)exportedType.Implementation;
AssemblyReference exportedAssemblyRef = md.GetAssemblyReference(arefHandle);
md = LookForHandle(exportedAssemblyRef);
break;
}
}
}
else break; // if we found our typedef without forwarding break out of the while loop
}
if (currentHandle == default)
return new TypeHandle(TargetPointer.Null);
// 2. Walk down the nested types
for (int i = 1; i < parts.Length; i++)
{
string nestedName = parts[i];
bool found = false;
foreach (TypeDefinitionHandle nestedHandle in md!.GetTypeDefinition(currentHandle).GetNestedTypes())
{
TypeDefinition nestedDef = md.GetTypeDefinition(nestedHandle);
if (md.GetString(nestedDef.Name) == nestedName)
{
currentHandle = nestedHandle;
found = true;
break;
}
}
if (!found)
return new TypeHandle(TargetPointer.Null);
}
// 3. We have the handle, look up the type handle
int token = MetadataTokens.GetToken((EntityHandle)currentHandle);
TargetPointer typeDefToMethodTable = loader.GetLookupTables(moduleHandle).TypeDefToMethodTable;
TargetPointer typeHandlePtr = loader.GetModuleLookupMapElement(typeDefToMethodTable, (uint)token, out _);
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
if (typeHandlePtr == TargetPointer.Null)
return new TypeHandle(TargetPointer.Null);
TypeHandle foundTypeHandle = rts.GetTypeHandle(typeHandlePtr);
_ = _typeHandlesByName.TryAdd(new TypeKeyByName(name, nameSpace, modulePtr), foundTypeHandle);
return foundTypeHandle;
}
public bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token)
{
module = TargetPointer.Null;
token = 0;
if (!typeHandle.IsTypeDesc())
return false;
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
switch (elemType)
{
case CorElementType.MVar:
case CorElementType.Var:
TypeVarTypeDesc typeVarTypeDesc = _target.ProcessedData.GetOrAdd<TypeVarTypeDesc>(typeHandle.TypeDescAddress());
module = typeVarTypeDesc.Module;
token = typeVarTypeDesc.Token;
return true;
}
return false;
}
public bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv)
{
retAndArgTypes = default;
callConv = default;
if (!typeHandle.IsTypeDesc())
return false;
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
if (elemType != CorElementType.FnPtr)
return false;
FnPtrTypeDesc fnPtrTypeDesc = _target.ProcessedData.GetOrAdd<FnPtrTypeDesc>(typeHandle.TypeDescAddress());
retAndArgTypes = _target.ProcessedData.GetOrAdd<FunctionPointerRetAndArgs>(typeHandle.TypeDescAddress()).TypeHandles;
callConv = (byte)fnPtrTypeDesc.CallConv;
return true;
}
public bool IsPointer(TypeHandle typeHandle)
{
if (!typeHandle.IsTypeDesc())
return false;
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
return elemType == CorElementType.Ptr;
}
public TargetPointer GetLoaderModule(TypeHandle typeHandle)
{
if (typeHandle.IsTypeDesc())
{
// TypeDesc::GetLoaderModule
if (HasTypeParam(typeHandle))
{
return GetLoaderModule(GetTypeParam(typeHandle));
}
else if (IsGenericVariable(typeHandle, out TargetPointer genericParamModule, out _))
{
return genericParamModule;
}
else
{
System.Diagnostics.Debug.Assert(IsFunctionPointer(typeHandle, out _, out _));
FnPtrTypeDesc fnPtrTypeDesc = _target.ProcessedData.GetOrAdd<FnPtrTypeDesc>(typeHandle.TypeDescAddress());
return fnPtrTypeDesc.LoaderModule;
}
}
MethodTable mt = _methodTables[typeHandle.Address];
Data.MethodTableAuxiliaryData mtAuxData = _target.ProcessedData.GetOrAdd<Data.MethodTableAuxiliaryData>(mt.AuxiliaryData);
return mtAuxData.LoaderModule;
}
private sealed class FunctionPointerRetAndArgs : IData<FunctionPointerRetAndArgs>
{
public static FunctionPointerRetAndArgs Create(Target target, TargetPointer address) => new FunctionPointerRetAndArgs(target, address);
public TypeHandle[] TypeHandles { get; }
private FunctionPointerRetAndArgs(Target target, TargetPointer typePointer)
{
RuntimeTypeSystem_1 rts = (RuntimeTypeSystem_1)target.Contracts.RuntimeTypeSystem;
FnPtrTypeDesc fnPtrTypeDesc = target.ProcessedData.GetOrAdd<FnPtrTypeDesc>(typePointer);
TargetPointer retAndArgs = fnPtrTypeDesc.RetAndArgTypes;
int numberOfRetAndArgTypes = checked((int)fnPtrTypeDesc.NumArgs + 1);
TypeHandles = new TypeHandle[numberOfRetAndArgTypes];
for (int i = 0; i < numberOfRetAndArgTypes; i++)
{
TypeHandles[i] = rts.GetTypeHandle(target.ReadPointer(retAndArgs + (ulong)target.PointerSize * (ulong)i));
}
}
}
public MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer)
=> GetMethodDescHandle(methodDescPointer, validate: true);
private MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer, bool validate)
{
// if we already have a method desc at this address, return a handle
if (_methodDescs.ContainsKey(methodDescPointer))
{
return new MethodDescHandle(methodDescPointer);
}
TargetPointer methodDescChunkPointer;
if (validate)
{
if (!_methodValidation.ValidateMethodDescPointer(methodDescPointer, out methodDescChunkPointer))
{
throw new ArgumentException("Invalid method desc pointer", nameof(methodDescPointer));
}
}
else
{
methodDescChunkPointer = _methodValidation.GetMethodDescChunkPointerThrowing(methodDescPointer, _target.ProcessedData.GetOrAdd<Data.MethodDesc>(methodDescPointer));
}
// ok, we validated it, cache the data and add the MethodDesc struct to the dictionary
Data.MethodDescChunk validatedMethodDescChunkData = _target.ProcessedData.GetOrAdd<Data.MethodDescChunk>(methodDescChunkPointer);
Data.MethodDesc validatedMethodDescData = _target.ProcessedData.GetOrAdd<Data.MethodDesc>(methodDescPointer);
MethodDesc trustedMethodDescF = new MethodDesc(_target, methodDescPointer, validatedMethodDescData, methodDescChunkPointer, validatedMethodDescChunkData);
_ = _methodDescs.TryAdd(methodDescPointer, trustedMethodDescF);
return new MethodDescHandle(methodDescPointer);
}
public TargetPointer GetMethodTable(MethodDescHandle methodDescHandle) => _methodDescs[methodDescHandle.Address].MethodTable;
private InstantiatedMethodDesc AsInstantiatedMethodDesc(MethodDesc methodDesc)
{
Debug.Assert(methodDesc.Classification == MethodClassification.Instantiated);
return _target.ProcessedData.GetOrAdd<InstantiatedMethodDesc>(methodDesc.Address);
}
private DynamicMethodDesc AsDynamicMethodDesc(MethodDesc methodDesc)
{
Debug.Assert(methodDesc.Classification == MethodClassification.Dynamic);
return _target.ProcessedData.GetOrAdd<DynamicMethodDesc>(methodDesc.Address);
}
private StoredSigMethodDesc AsStoredSigMethodDesc(MethodDesc methodDesc)
{
Debug.Assert(methodDesc.Classification == MethodClassification.Dynamic ||
methodDesc.Classification == MethodClassification.EEImpl ||
methodDesc.Classification == MethodClassification.Array);
return _target.ProcessedData.GetOrAdd<StoredSigMethodDesc>(methodDesc.Address);
}
public bool IsGenericMethodDefinition(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (methodDesc.Classification != MethodClassification.Instantiated)
return false;
return AsInstantiatedMethodDesc(methodDesc).IsGenericMethodDefinition;
}
public ReadOnlySpan<TypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (methodDesc.Classification != MethodClassification.Instantiated)
return default;
return AsInstantiatedMethodDesc(methodDesc).Instantiation;
}
private bool RequiresInstArg(MethodDesc methodDesc)
{
if (HasMethodInstantiation(methodDesc))
return true;
if (methodDesc.IsStatic)
return true;
MethodTable mt = _methodTables[methodDesc.MethodTable];
if (mt.Flags.IsValueType)
return true;
if (mt.Flags.IsInterface && !IsAbstract(methodDesc))
return true;
return false;
}
public GenericContextLoc GetGenericContextLoc(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (!IsSharedByGenericInstantiations(methodDesc))
return GenericContextLoc.None;
else if (RequiresInstArg(methodDesc))
return GenericContextLoc.InstArg;
else
return GenericContextLoc.ThisPtr;
}
private bool IsSharedByGenericInstantiations(MethodDesc methodDesc)
{
// Check method-level sharing: InstantiatedMethodDesc with SharedMethodInstantiation
if (methodDesc.Classification == MethodClassification.Instantiated)
{
InstantiatedMethodDesc imd = AsInstantiatedMethodDesc(methodDesc);
if (imd.IsWrapperStubWithInstantiations)
return false;
// Check SharedMethodInstantiation flag
Data.InstantiatedMethodDesc imdData = _target.ProcessedData.GetOrAdd<Data.InstantiatedMethodDesc>(methodDesc.Address);
if ((imdData.Flags2 & (ushort)InstantiatedMethodDescFlags2.KindMask)
== (ushort)InstantiatedMethodDescFlags2.SharedMethodInstantiation)
return true;
}
// Check class-level sharing: canonical MethodTable with generic instantiation
MethodTable mt = _methodTables[methodDesc.MethodTable];
return mt.IsCanonMT && mt.Flags.HasInstantiation;
}
private bool IsAbstract(MethodDesc methodDesc)
{
if (methodDesc.Classification == MethodClassification.Array || methodDesc.Classification == MethodClassification.Dynamic)
return false;
uint token = methodDesc.Token;
if (EcmaMetadataUtils.GetRowId(token) == 0)
return false;
TargetPointer modulePtr = _methodTables[methodDesc.MethodTable].Module;
ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr);
MetadataReader? mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle);
if (mdReader is null)
return false;
MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(token));
MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle);
return (methodDef.Attributes & MethodAttributes.Abstract) != 0;
}
public bool IsAsyncMethod(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (!methodDesc.HasAsyncMethodData)
return false;
// AsyncMethodData is the last optional slot, placed after NativeCodeSlot.
// Read the AsyncMethodFlags (first field) and check for AsyncCall.
TargetPointer asyncDataAddr = methodDesc.GetAddressOfAsyncMethodData();
uint asyncFlags = _target.Read<uint>(asyncDataAddr);
return (asyncFlags & (uint)AsyncMethodFlags.AsyncCall) != 0;
}
public uint GetMethodToken(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
return methodDesc.Token;
}
public bool IsArrayMethod(MethodDescHandle methodDescHandle, out ArrayFunctionType functionType)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (methodDesc.Classification != MethodClassification.Array)
{
functionType = default;
return false;
}
// To get the array function index, subtract the number of virtuals from the method's slot
// The array vtable looks like:
// System.Object vtable
// System.Array vtable
// type[] vtable
// Get
// Set
// Address
// .ctor // possibly more
// See ArrayMethodDesc for details in coreclr
MethodTable methodTable = GetOrCreateMethodTable(methodDesc);
int arrayMethodIndex = methodDesc.Slot - methodTable.NumVirtuals;
functionType = arrayMethodIndex switch
{
0 => ArrayFunctionType.Get,
1 => ArrayFunctionType.Set,
2 => ArrayFunctionType.Address,
>= 3 => ArrayFunctionType.Constructor,
_ => throw new InvalidOperationException()
};
return true;
}
public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out string methodName)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (methodDesc.Classification != MethodClassification.Dynamic)
{
methodName = string.Empty;
return false;
}
methodName = AsDynamicMethodDesc(methodDesc).MethodName;
return true;
}
public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan<byte> signature)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
switch (methodDesc.Classification)
{
case MethodClassification.Dynamic:
case MethodClassification.EEImpl:
case MethodClassification.Array:
break; // These have stored sigs
default:
signature = default;
return false;
}
signature = AsStoredSigMethodDesc(methodDesc).Signature;
return true;
}
public bool IsDynamicMethod(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (methodDesc.Classification != MethodClassification.Dynamic)
{
return false;
}
return AsDynamicMethodDesc(methodDesc).IsDynamicMethod;
}
public bool IsIL(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
return methodDesc.IsIL;
}
public bool IsILStub(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (methodDesc.Classification != MethodClassification.Dynamic)
{
return false;
}
return AsDynamicMethodDesc(methodDesc).IsILStub;
}
public bool HasMDContextArg(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
if (methodDesc.Classification != MethodClassification.Dynamic)
{
return false;
}
return AsDynamicMethodDesc(methodDesc).HasMDContextArg;
}
private MethodTable GetOrCreateMethodTable(MethodDesc methodDesc)
{
// Ensures that the method table is valid, created, and cached
_ = GetTypeHandle(methodDesc.MethodTable);
return _methodTables[methodDesc.MethodTable];
}
private struct VtableIndirections
{
// See methodtable.inl VTABLE_SLOTS_PER_CHUNK and the comment on it
private const int NumPointersPerIndirection = 8;
private const int NumPointersPerIndirectionLog2 = 3;
private readonly Target _target;
public readonly TargetPointer Address;
public VtableIndirections(Target target, TargetPointer address)
{
_target = target;
Address = address;
}
public TargetPointer GetAddressOfSlot(uint slotNum)
{
TargetPointer indirectionPointer = Address + (ulong)(slotNum >> NumPointersPerIndirectionLog2) * (ulong)_target.PointerSize;
TargetPointer slotsStart = _target.ReadPointer(indirectionPointer);
return slotsStart + (ulong)(slotNum & (NumPointersPerIndirection - 1)) * (ulong)_target.PointerSize;
}
}
private VtableIndirections GetVTableIndirections(TargetPointer methodTableAddress)
{
var typeInfo = _target.GetTypeInfo(DataType.MethodTable);
return new VtableIndirections(_target, methodTableAddress + typeInfo.Size!.Value);
}
private TargetPointer GetAddressOfSlot(TypeHandle typeHandle, uint slotNum)
{
if (!typeHandle.IsMethodTable())
throw new InvalidOperationException($"nameof{typeHandle} is not a MethodTable");
Debug.Assert(slotNum < GetNumVtableSlots(typeHandle), "Slot number is greater than the number of slots");
// MethodTable::GetSlotPtrRaw
MethodTable mt = _methodTables[typeHandle.Address];
if (slotNum < mt.NumVirtuals)
{
// Virtual slots live in chunks pointed to by vtable indirections
return GetVTableIndirections(typeHandle.Address).GetAddressOfSlot(slotNum);
}
else
{
Debug.Assert(mt.NumVirtuals < GetNumVtableSlots(typeHandle), "Method table does not have non-virtual slots");
// Non-virtual slots < GetNumVtableSlots live before the MethodTableAuxiliaryData. The array grows backwards
TargetPointer auxDataPtr = mt.AuxiliaryData;
Data.MethodTableAuxiliaryData auxData = _target.ProcessedData.GetOrAdd<Data.MethodTableAuxiliaryData>(auxDataPtr);
TargetPointer nonVirtualSlotsArray = auxDataPtr + (ulong)auxData.OffsetToNonVirtualSlots;
return nonVirtualSlotsArray - ((1 + (slotNum - mt.NumVirtuals)) * (ulong)_target.PointerSize);
}
}
private bool IsWrapperStub(MethodDesc md)
{
return md.IsUnboxingStub || IsInstantiatingStub(md);
}
private bool IsInstantiatingStub(MethodDesc md)
{
return md.Classification == MethodClassification.Instantiated && !md.IsUnboxingStub && AsInstantiatedMethodDesc(md).IsWrapperStubWithInstantiations;
}
private bool HasMethodInstantiation(MethodDesc md)
{
return md.Classification == MethodClassification.Instantiated && AsInstantiatedMethodDesc(md).HasMethodInstantiation;
}
private bool IsGenericMethodDefinition(MethodDesc md)
{
return md.Classification == MethodClassification.Instantiated && AsInstantiatedMethodDesc(md).IsGenericMethodDefinition;
}
private TargetPointer GetLoaderModule(MethodDesc md)
{
// MethodDesc::GetLoaderModule:
// return GetMethodDescChunk()->GetLoaderModule();
// MethodDescChunk::GetLoaderModule:
if (md.IsLoaderModuleAttachedToChunk)
{
TargetPointer methodDescChunkPointer = md.ChunkAddress;
TargetPointer endOfChunk = methodDescChunkPointer + md.SizeOfChunk;
TargetPointer ppLoaderModule = endOfChunk - (ulong)_target.PointerSize;
return _target.ReadPointer(ppLoaderModule);
}
else
{
TargetPointer mtAddr = GetMethodTable(new MethodDescHandle(md.Address));
TypeHandle mt = GetTypeHandle(mtAddr);
return GetLoaderModule(mt);
}
}
bool IRuntimeTypeSystem.IsCollectibleMethod(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
TargetPointer loaderModuleAddr = GetLoaderModule(md);
ModuleHandle mod = _target.Contracts.Loader.GetModuleHandleFromModulePtr(loaderModuleAddr);
return _target.Contracts.Loader.IsCollectible(mod);
}
bool IRuntimeTypeSystem.IsVersionable(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
if (md.IsEligibleForTieredCompilation)
return true;
// MethodDesc::IsEligibleForReJIT
if (_target.Contracts.ReJIT.IsEnabled())
{
if (!md.IsIL)
return false;
if (IsWrapperStub(md))
return false;
return _target.Contracts.CodeVersions.CodeVersionManagerSupportsMethod(methodDesc.Address);
}
return false;
}
TargetPointer IRuntimeTypeSystem.GetMethodDescVersioningState(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
TargetPointer codeDataAddress = md.CodeData;
if (codeDataAddress == TargetPointer.Null)
return TargetPointer.Null;
Data.MethodDescCodeData codeData = _target.ProcessedData.GetOrAdd<Data.MethodDescCodeData>(codeDataAddress);
return codeData.VersioningState;
}
uint IRuntimeTypeSystem.GetMethodToken(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
return methodDesc.Token;
}
ushort IRuntimeTypeSystem.GetSlotNumber(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
return md.Slot;
}
bool IRuntimeTypeSystem.HasNativeCodeSlot(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
return md.HasNativeCodeSlot;
}
// Based on MethodTable::IntroducedMethodIterator
private IEnumerable<MethodDescHandle> GetIntroducedMethods(TypeHandle typeHandle)
{
Debug.Assert(typeHandle.IsMethodTable());
EEClass eeClass = GetClassData(typeHandle);
TargetPointer chunkAddr = eeClass.MethodDescChunk;
while (chunkAddr != TargetPointer.Null)
{
MethodDescChunk chunk = _target.ProcessedData.GetOrAdd<MethodDescChunk>(chunkAddr);
TargetPointer methodDescPtr = chunk.FirstMethodDesc;
// chunk.Count is the number of MethodDescs in the chunk - 1
for (int i = 0; i < chunk.Count + 1; i++)
{
// Validation of some MethodDescs fails in heap dumps due to missing memory.
// Skipping validation should be okay as the pointers come from the target.
MethodDescHandle methodDescHandle = GetMethodDescHandle(methodDescPtr, validate: false);
MethodDesc md = _methodDescs[methodDescHandle.Address];
methodDescPtr += md.Size;
yield return methodDescHandle;
}
chunkAddr = chunk.Next;
}
}
IEnumerable<TargetPointer> IRuntimeTypeSystem.GetIntroducedMethodDescs(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
yield break;
TypeHandle canonMT = GetTypeHandle(GetCanonicalMethodTable(typeHandle));
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
{
yield return mdh.Address;
}
}
// Uses GetMethodDescForVtableSlot if slot is less than the number of vtable slots
// otherwise looks for the slot in the introduced methods
TargetPointer IRuntimeTypeSystem.GetMethodDescForSlot(TypeHandle typeHandle, ushort slot)
{
if (!typeHandle.IsMethodTable())
// TypeDesc do not contain any slots.
return TargetPointer.Null;
TypeHandle canonMT = GetTypeHandle(GetCanonicalMethodTable(typeHandle));
if (slot < GetNumVtableSlots(canonMT))
{
return GetMethodDescForVtableSlot(canonMT, slot);
}
else
{
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
{
MethodDesc md = _methodDescs[mdh.Address];
if (md.Slot == slot)
{
return mdh.Address;
}
}
return TargetPointer.Null;
}
}
private TargetPointer GetMethodDescForVtableSlot(TypeHandle typeHandle, ushort slot)
{
// based on MethodTable::GetMethodDescForSlot_NoThrow
if (!typeHandle.IsMethodTable())
// TypeDesc do not contain any slots.
throw new ArgumentException(nameof(slot), "Slot number is greater than the number of slots");
TargetPointer cannonMTPTr = GetCanonicalMethodTable(typeHandle);
TypeHandle canonMT = GetTypeHandle(cannonMTPTr);
if (slot >= GetNumVtableSlots(canonMT))
throw new ArgumentException(nameof(slot), "Slot number is greater than the number of slots");
TargetPointer slotPtr = GetAddressOfSlot(canonMT, slot);
TargetCodePointer pCode = _target.ReadCodePointer(slotPtr);
if (pCode == TargetCodePointer.Null)
{
TargetPointer lookupMTPtr = cannonMTPTr;
while (lookupMTPtr != TargetPointer.Null)
{
// if pCode is null, we iterate through the method descs in the MT.
TypeHandle lookupMT = GetTypeHandle(lookupMTPtr);
foreach (MethodDescHandle mdh in GetIntroducedMethods(lookupMT))
{
MethodDesc md = _methodDescs[mdh.Address];
if (md.Slot == slot)
{
return mdh.Address;
}
}
lookupMTPtr = GetParentMethodTable(lookupMT);
if (lookupMTPtr != TargetPointer.Null)
lookupMTPtr = GetCanonicalMethodTable(GetTypeHandle(lookupMTPtr));
}
return TargetPointer.Null;
}
return GetMethodDescForEntrypoint(pCode);
}
private readonly TargetPointer GetMethodDescForEntrypoint(TargetCodePointer pCode)
{
// standard path, ask ExecutionManager for the MethodDesc
IExecutionManager executionManager = _target.Contracts.ExecutionManager;
if (executionManager.GetCodeBlockHandle(pCode) is CodeBlockHandle cbh)
{
TargetPointer methodDescPtr = executionManager.GetMethodDesc(cbh);
return methodDescPtr;
}
// stub path, read address as a Precode and read MethodDesc from it
{
TargetPointer methodDescPtr = _target.Contracts.PrecodeStubs.GetMethodDescFromStubAddress(pCode);
return methodDescPtr;
}
}
TargetCodePointer IRuntimeTypeSystem.GetSlot(TypeHandle typeHandle, uint slot)
{
// based on MethodTable::GetSlot(uint slotNumber)
if (slot < GetNumVtableSlots(typeHandle))
{
TargetPointer slotPtr = GetAddressOfSlot(typeHandle, slot);
return _target.ReadCodePointer(slotPtr);
}
return TargetCodePointer.Null;
}
TargetPointer IRuntimeTypeSystem.GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
return md.GetAddressOfNativeCodeSlot();
}
TargetCodePointer IRuntimeTypeSystem.GetNativeCode(MethodDescHandle methodDescHandle)
{
MethodDesc md = _methodDescs[methodDescHandle.Address];
// TODO(cdac): _ASSERTE(!IsDefaultInterfaceMethod() || HasNativeCodeSlot());
if (md.HasNativeCodeSlot)
{
// When profiler is enabled, profiler may ask to rejit a code even though we
// we have ngen code for this MethodDesc. (See MethodDesc::DoPrestub).
// This means that *ppCode is not stable. It can turn from non-zero to zero.
TargetPointer ppCode = md.GetAddressOfNativeCodeSlot();
TargetCodePointer pCode = _target.ReadCodePointer(ppCode);
return CodePointerUtils.CodePointerFromAddress(pCode.AsTargetPointer, _target);
}
if (!md.HasStableEntryPoint || md.HasPrecode)
return TargetCodePointer.Null;
return GetStableEntryPoint(md);
}
TargetCodePointer IRuntimeTypeSystem.GetMethodEntryPointIfExists(MethodDescHandle methodDescHandle)
{
MethodDesc md = _methodDescs[methodDescHandle.Address];
return GetMethodEntryPointIfExists(md);
}
private TargetCodePointer GetStableEntryPoint(MethodDesc md)
{
// TODO(cdac): _ASSERTE(!IsVersionableWithVtableSlotBackpatch());
Debug.Assert(md.HasStableEntryPoint);
return GetMethodEntryPointIfExists(md);
}
private TargetCodePointer GetMethodEntryPointIfExists(MethodDesc md)
{
if (md.HasNonVtableSlot)
{
TargetPointer pSlot = md.GetAddressOfNonVtableSlot();
return _target.ReadCodePointer(pSlot);
}
TargetPointer methodTablePointer = md.MethodTable;
TypeHandle typeHandle = GetTypeHandle(methodTablePointer);
Debug.Assert(_methodTables[typeHandle.Address].IsCanonMT);
TargetPointer addrOfSlot = GetAddressOfSlot(typeHandle, md.Slot);
return _target.ReadCodePointer(addrOfSlot);
}
TargetPointer IRuntimeTypeSystem.GetGCStressCodeCopy(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
if (md.GCCoverageInfo is TargetPointer gcCoverageInfoAddr && gcCoverageInfoAddr != TargetPointer.Null)
{
Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo);
return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset;
}
return TargetPointer.Null;
}
internal static OptimizationTier GetOptimizationTier(uint? optimizationTier)
{
return (OptimizationTier_1?)optimizationTier switch
{
OptimizationTier_1.OptimizationTier0 => OptimizationTier.OptimizationTier0,
OptimizationTier_1.OptimizationTier1 => OptimizationTier.OptimizationTier1,
OptimizationTier_1.OptimizationTier1OSR => OptimizationTier.OptimizationTier1OSR,
OptimizationTier_1.OptimizationTierOptimized => OptimizationTier.OptimizationTierOptimized,
OptimizationTier_1.OptimizationTier0Instrumented => OptimizationTier.OptimizationTier0Instrumented,
OptimizationTier_1.OptimizationTier1Instrumented => OptimizationTier.OptimizationTier1Instrumented,
_ => OptimizationTier.OptimizationTierUnknown,
};
}
OptimizationTier IRuntimeTypeSystem.GetMethodDescOptimizationTier(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
TargetPointer codeDataAddress = methodDesc.CodeData;
if (codeDataAddress == TargetPointer.Null)
return OptimizationTier.OptimizationTierUnknown;
Data.MethodDescCodeData codeData = _target.ProcessedData.GetOrAdd<Data.MethodDescCodeData>(codeDataAddress);
return GetOptimizationTier(codeData.OptimizationTier);
}
bool IRuntimeTypeSystem.IsEligibleForTieredCompilation(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
return methodDesc.IsEligibleForTieredCompilation;
}
public bool IsAsyncThunkMethod(MethodDescHandle methodDescHandle)
{
MethodDesc md = _methodDescs[methodDescHandle.Address];
if (!md.HasAsyncMethodData)
{
return false;
}
Data.AsyncMethodData asyncData = _target.ProcessedData.GetOrAdd<Data.AsyncMethodData>(md.GetAddressOfAsyncMethodData());
return ((AsyncMethodFlags)asyncData.Flags).HasFlag(AsyncMethodFlags.Thunk);
}
public bool IsWrapperStub(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
return IsWrapperStub(methodDesc);
}
private sealed class NonValidatedMethodTableQueries : MethodValidation.IMethodTableQueries
{
private readonly RuntimeTypeSystem_1 _rts;
public NonValidatedMethodTableQueries(RuntimeTypeSystem_1 rts)
{
_rts = rts;
}
public bool SlotIsVtableSlot(TargetPointer methodTablePointer, uint slot)
{
return _rts.SlotIsVtableSlot(methodTablePointer, slot);
}
public TargetPointer GetAddressOfMethodTableSlot(TargetPointer methodTablePointer, uint slot)
{
return _rts.GetAddressOfMethodTableSlot(methodTablePointer, slot);
}
}
// for the benefit of MethodValidation
private TargetPointer GetAddressOfMethodTableSlot(TargetPointer methodTablePointer, uint slot)
{
TypeHandle typeHandle = GetTypeHandle(methodTablePointer);
Debug.Assert(_methodTables[typeHandle.Address].IsCanonMT);
TargetPointer addrOfSlot = GetAddressOfSlot(typeHandle, slot);
return addrOfSlot;
}
private bool SlotIsVtableSlot(TargetPointer methodTablePointer, uint slot)
{
TypeHandle typeHandle = GetTypeHandle(methodTablePointer);
return slot < GetNumVtableSlots(typeHandle);
}
TargetPointer IRuntimeTypeSystem.GetMTOfEnclosingClass(TargetPointer fieldDescPointer)
{
Data.FieldDesc fieldDesc = _target.ProcessedData.GetOrAdd<Data.FieldDesc>(fieldDescPointer);
return fieldDesc.MTOfEnclosingClass;
}
uint IRuntimeTypeSystem.GetFieldDescMemberDef(TargetPointer fieldDescPointer)
{
Data.FieldDesc fieldDesc = _target.ProcessedData.GetOrAdd<Data.FieldDesc>(fieldDescPointer);
return EcmaMetadataUtils.CreateFieldDef(fieldDesc.DWord1 & (uint)FieldDescFlags1.TokenMask);
}
bool IRuntimeTypeSystem.IsFieldDescThreadStatic(TargetPointer fieldDescPointer)
{
Data.FieldDesc fieldDesc = _target.ProcessedData.GetOrAdd<Data.FieldDesc>(fieldDescPointer);
return (fieldDesc.DWord1 & (uint)FieldDescFlags1.IsThreadStatic) != 0;
}
private bool IsFieldDescRVA(TargetPointer fieldDescPointer)
{
Data.FieldDesc fieldDesc = _target.ProcessedData.GetOrAdd<Data.FieldDesc>(fieldDescPointer);
return (fieldDesc.DWord1 & (uint)FieldDescFlags1.IsRVA) != 0;
}
bool IRuntimeTypeSystem.IsFieldDescStatic(TargetPointer fieldDescPointer)
{
Data.FieldDesc fieldDesc = _target.ProcessedData.GetOrAdd<Data.FieldDesc>(fieldDescPointer);
return (fieldDesc.DWord1 & (uint)FieldDescFlags1.IsStatic) != 0;
}
CorElementType IRuntimeTypeSystem.GetFieldDescType(TargetPointer fieldDescPointer)
{
Data.FieldDesc fieldDesc = _target.ProcessedData.GetOrAdd<Data.FieldDesc>(fieldDescPointer);
// 27 is the number of bits that the type is shifted left. if you change the enum, please change this too.
return (CorElementType)((fieldDesc.DWord2 & (uint)FieldDescFlags2.TypeMask) >> TYPE_MASK_OFFSET);
}
uint IRuntimeTypeSystem.GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef)
{
Data.FieldDesc fieldDesc = _target.ProcessedData.GetOrAdd<Data.FieldDesc>(fieldDescPointer);
if (fieldDesc.DWord2 == _target.ReadGlobal<uint>(Constants.Globals.FieldOffsetBigRVA))
{
return (uint)fieldDef.GetRelativeVirtualAddress();
}
return fieldDesc.DWord2 & (uint)FieldDescFlags2.OffsetMask;
}
TargetPointer IRuntimeTypeSystem.GetFieldDescByName(TypeHandle typeHandle, string fieldName)
{
if (!typeHandle.IsMethodTable())
return TargetPointer.Null;
TargetPointer modulePtr = GetModule(typeHandle);
if (modulePtr == TargetPointer.Null)
return TargetPointer.Null;
uint typeDefToken = GetTypeDefToken(typeHandle);
if (typeDefToken == 0)
return TargetPointer.Null;
EntityHandle entityHandle = MetadataTokens.EntityHandle((int)typeDefToken);
if (entityHandle.Kind != HandleKind.TypeDefinition)
return TargetPointer.Null;
TypeDefinitionHandle typeDefHandle = (TypeDefinitionHandle)entityHandle;
ILoader loader = _target.Contracts.Loader;
ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr);
MetadataReader? md = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle);
if (md is null)
return TargetPointer.Null;
TargetPointer fieldDefToDescMap = loader.GetLookupTables(moduleHandle).FieldDefToDesc;
foreach (FieldDefinitionHandle fieldDefHandle in md.GetTypeDefinition(typeDefHandle).GetFields())
{
FieldDefinition fieldDef = md.GetFieldDefinition(fieldDefHandle);
if (md.GetString(fieldDef.Name) == fieldName)
{
uint fieldDefToken = (uint)MetadataTokens.GetToken(fieldDefHandle);
TargetPointer fieldDescPtr = loader.GetModuleLookupMapElement(fieldDefToDescMap, fieldDefToken, out _);
return fieldDescPtr;
}
}
return TargetPointer.Null;
}
private TargetPointer GetStaticAddressHandle(TargetPointer @base, uint offset, bool isRVA, TargetPointer fieldDescPointer, ModuleHandle moduleHandle)
{
if (isRVA)
{
ILoader loader = _target.Contracts.Loader;
if (offset == _target.ReadGlobal<uint>(Constants.Globals.FieldOffsetDynamicRVA))
{
return loader.GetDynamicIL(moduleHandle, ((IRuntimeTypeSystem)this).GetFieldDescMemberDef(fieldDescPointer));
}
TargetPointer peAssembly = loader.GetPEAssembly(moduleHandle);
return loader.GetFieldAddressFromRva(peAssembly, (int)offset);
}
return new TargetPointer(@base + offset);
}
private TargetPointer GetFieldDescStaticOrThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer? thread = null)
{
TargetPointer enclosingMT = ((IRuntimeTypeSystem)this).GetMTOfEnclosingClass(fieldDescPointer);
TypeHandle ctx = GetTypeHandle(enclosingMT);
TargetPointer modulePtr = GetModule(ctx);
ILoader loader = _target.Contracts.Loader;
ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr);
CorElementType type = ((IRuntimeTypeSystem)this).GetFieldDescType(fieldDescPointer);
TargetPointer @base;
if (type == CorElementType.Class || type == CorElementType.ValueType)
{
if (thread.HasValue)
{
@base = GetGCThreadStaticsBasePointer(ctx, thread.Value);
}
else
{
@base = GetGCStaticsBasePointer(ctx);
}
}
else
{
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);
FieldDefinition fieldDef = mdReader.GetFieldDefinition(fieldHandle);
uint offset = ((IRuntimeTypeSystem)this).GetFieldDescOffset(fieldDescPointer, fieldDef);
bool isRVA = IsFieldDescRVA(fieldDescPointer);
TargetPointer handleAddr = GetStaticAddressHandle(@base, offset, isRVA, fieldDescPointer, moduleHandle);
if (type == CorElementType.ValueType && !isRVA)
{
TargetPointer objRef = _target.ReadPointer(handleAddr);
Data.Object obj = _target.ProcessedData.GetOrAdd<Data.Object>(objRef);
return obj.Data;
}
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;
TargetPointer systemAssembly = loader.GetSystemAssembly();
ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(systemAssembly);
IRuntimeTypeSystem rts = (IRuntimeTypeSystem)this;
TypeHandle th = rts.GetTypeByNameAndModule(typeName, @namespace, moduleHandle);
fieldDescAddr = rts.GetFieldDescByName(th, fieldName);
uint token = rts.GetFieldDescMemberDef(fieldDescAddr);
FieldDefinitionHandle fieldHandle = (FieldDefinitionHandle)MetadataTokens.Handle((int)token);
MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!;
fieldDef = mdReader.GetFieldDefinition(fieldHandle);
}
}
|