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
99 changes: 92 additions & 7 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ partial interface IRuntimeTypeSystem : IContract
public virtual TargetPointer GetParentMethodTable(TypeHandle typeHandle);

public virtual TargetPointer GetMethodDescForSlot(TypeHandle typeHandle, ushort slot);
public virtual IEnumerable<TargetPointer> GetIntroducedMethodDescs(TypeHandle methodTable);
public virtual TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot);

public virtual uint GetBaseSize(TypeHandle typeHandle);
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
Expand All @@ -57,6 +59,7 @@ partial interface IRuntimeTypeSystem : IContract

// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
public virtual uint GetTypeDefToken(TypeHandle typeHandle);
public virtual ushort GetNumVtableSlots(TypeHandle typeHandle);
public virtual ushort GetNumMethods(TypeHandle typeHandle);
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
// or for its generic type definition if it is a generic instantiation
Expand Down Expand Up @@ -169,8 +172,12 @@ partial interface IRuntimeTypeSystem : IContract
public virtual TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc);

// Get an instruction pointer that can be called to cause the MethodDesc to be executed
// Requires the entry point to be stable
public virtual TargetCodePointer GetNativeCode(MethodDescHandle methodDesc);

// Get an instruction pointer that can be called to cause the MethodDesc to be executed
public virtual TargetCodePointer GetMethodEntryPointIfExists(MethodDescHandle methodDesc);

// Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null
public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc);
}
Expand Down Expand Up @@ -470,6 +477,15 @@ Contracts used:
return (uint)(typeHandle.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() ? 0 : GetClassData(TypeHandle).NumMethods;

public uint GetTypeDefTypeAttributes(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).CorTypeAttr;
Expand Down Expand Up @@ -1302,6 +1318,30 @@ Getting the native code pointer for methods with a NativeCodeSlot or a stable en

return GetStableEntryPoint(methodDescHandle.Address, md);
}

public TargetCodePointer IRuntimeTypeSystem.GetMethodEntryPointIfExists(MethodDescHandle methodDescHandle)
{
MethodDesc md = _methodDescs[methodDescHandle.Address];
return GetMethodEntryPointIfExists(methodDescHandle.Address, md);
}
```

Getting the value of a slot of a MethodTable
```csharp
public TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot)
{
// based on MethodTable::GetSlot(uint slotNumber)
if (!typeHandle.IsMethodTable())
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");

if (slot < GetNumVtableSlots(typeHandle))
{
TargetPointer slotPtr = GetAddressOfSlot(typeHandle, slot);
return _target.ReadCodePointer(slotPtr);
}

return TargetCodePointer.Null;
}
```

Getting a MethodDesc for a certain slot in a MethodTable
Expand Down Expand Up @@ -1353,33 +1393,78 @@ Getting a MethodDesc for a certain slot in a MethodTable
}
}

public TargetPointer GetMethodDescForSlot(TypeHandle methodTable, ushort slot)
public IEnumerable<TargetPointer> GetIntroducedMethodDescs(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");

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
public TargetPointer GetMethodDescForSlot(TypeHandle typeHandle, ushort slot)
{
if (!typeHandle.IsMethodTable())
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");

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 methodTable, ushort slot)
{
// based on MethodTable::GetMethodDescForSlot_NoThrow
if (!typeHandle.IsMethodTable())
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");

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)
{
// if pCode is null, we iterate through the method descs in the MT
while (true) // arbitrary limit to avoid infinite loop
TargetPointer lookupMTPtr = cannonMTPTr;
while (lookupMTPtr != TargetPointer.Null)
{
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
// 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 a MethodDesc matches the slot, return that MethodDesc
if (md.Slot == slot)
{
return mdh.Address;
}
}
canonMT = GetTypeHandle(GetCanonicalMethodTable(GetTypeHandle(GetParentMethodTable(canonMT))));
lookupMTPtr = GetParentMethodTable(lookupMT);
if (lookupMTPtr != TargetPointer.Null)
lookupMTPtr = GetCanonicalMethodTable(GetTypeHandle(lookupMTPtr));
}
return TargetPointer.Null;
}

return GetMethodDescForEntrypoint(pCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

Expand Down Expand Up @@ -89,6 +90,8 @@ public interface IRuntimeTypeSystem : IContract
TargetPointer GetParentMethodTable(TypeHandle typeHandle) => throw new NotImplementedException();

TargetPointer GetMethodDescForSlot(TypeHandle methodTable, ushort slot) => throw new NotImplementedException();
IEnumerable<TargetPointer> GetIntroducedMethodDescs(TypeHandle methodTable) => throw new NotImplementedException();
TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot) => throw new NotImplementedException();

uint GetBaseSize(TypeHandle typeHandle) => throw new NotImplementedException();
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
Expand All @@ -104,6 +107,7 @@ public interface IRuntimeTypeSystem : IContract

// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
uint GetTypeDefToken(TypeHandle typeHandle) => throw new NotImplementedException();
ushort GetNumVtableSlots(TypeHandle typeHandle) => throw new NotImplementedException();
ushort GetNumMethods(TypeHandle typeHandle) => throw new NotImplementedException();
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
// or for its generic type definition if it is a generic instantiation
Expand Down Expand Up @@ -175,6 +179,7 @@ public interface IRuntimeTypeSystem : IContract
TargetPointer GetMethodDescVersioningState(MethodDescHandle methodDesc) => throw new NotImplementedException();

TargetCodePointer GetNativeCode(MethodDescHandle methodDesc) => throw new NotImplementedException();
TargetCodePointer GetMethodEntryPointIfExists(MethodDescHandle methodDesc) => throw new NotImplementedException();

ushort GetSlotNumber(MethodDescHandle methodDesc) => throw new NotImplementedException();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,14 @@ public uint GetTypeDefToken(TypeHandle typeHandle)
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;
Expand Down Expand Up @@ -699,15 +707,6 @@ private FunctionPointerRetAndArgs(Target target, TargetPointer typePointer)
}
}

private 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 MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer)
=> GetMethodDescHandle(methodDescPointer, validate: true);

Expand Down Expand Up @@ -1086,33 +1085,80 @@ private IEnumerable<MethodDescHandle> GetIntroducedMethods(TypeHandle typeHandle
}
}

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())
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");
// 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)
{
while (canonMT.Address != TargetPointer.Null)
TargetPointer lookupMTPtr = cannonMTPTr;
while (lookupMTPtr != TargetPointer.Null)
{
// if pCode is null, we iterate through the method descs in the MT.
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
TypeHandle lookupMT = GetTypeHandle(lookupMTPtr);
foreach (MethodDescHandle mdh in GetIntroducedMethods(lookupMT))
{
MethodDesc md = _methodDescs[mdh.Address];
if (md.Slot == slot)
{
return mdh.Address;
}
}
canonMT = GetTypeHandle(GetCanonicalMethodTable(GetTypeHandle(GetParentMethodTable(canonMT))));
lookupMTPtr = GetParentMethodTable(lookupMT);
if (lookupMTPtr != TargetPointer.Null)
lookupMTPtr = GetCanonicalMethodTable(GetTypeHandle(lookupMTPtr));
}
Debug.Fail("We should never reach here, as there should always be a MethodDesc for a slot");
return TargetPointer.Null;
}

return GetMethodDescForEntrypoint(pCode);
Expand All @@ -1135,6 +1181,19 @@ private readonly TargetPointer GetMethodDescForEntrypoint(TargetCodePointer pCod
}
}

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];
Expand All @@ -1161,6 +1220,12 @@ TargetCodePointer IRuntimeTypeSystem.GetNativeCode(MethodDescHandle methodDescHa
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ public static ClrDataAddress ToClrDataAddress(this TargetPointer address, Target
}
}

/// <summary>
/// Converts a TargetCodePointer to a ClrDataAddress using sign extension if required.
/// </summary>
public static ClrDataAddress ToClrDataAddress(this TargetCodePointer address, Target target)
{
if (target.PointerSize == sizeof(ulong))
{
return address.Value;
}
else
{
return (ulong)(int)address.Value;
}
}

/// <summary>
/// Converts a ClrDataAddress to a TargetPointer, ensuring the address is within the valid range for the target platform.
/// When overrideCheck is true, this will not check the range and will allow any address. This is used on legacy endpoints which
Expand Down
Loading
Loading