# Contract RuntimeTypeSystem

This contract is for exploring the properties of the runtime types of values on the managed heap or on the stack in a .NET process.

## APIs of contract

### ITypeHandle

An `ITypeHandle` is the runtime representation of the type information about a value which is represented as an `ITypeHandle`.
Given a `TargetPointer` address, the `RuntimeTypeSystem` contract provides an `ITypeHandle` for querying the details of the type.

``` csharp
// An opaque canonical identity for a runtime type. Handles are produced and
// interned by RuntimeTypeSystem; consumers must not fabricate implementations.
// A null reference represents the absence of a type.
// Canonical identity is scoped to a target cache epoch: after Target.Flush,
// resolving the same target address may return a different object reference.
interface ITypeHandle
{
    TargetPointer Address { get; }
}

// An internal real target-backed handle (a MethodTable* or TypeDesc* address).
class TargetTypeHandle : ITypeHandle { /* ... */ }

readonly record struct TypedByRefInfo(TargetPointer Data, TargetPointer TypeHandle);

internal enum CorElementType
{
    // Values defined in ECMA-335 - II.23.1.16 Element types used in signatures
    // +
    Internal = 0x21, // Indicates that the next pointer sized number of bytes is the address of a TypeHandle. Signatures that contain the Internal CorElementType cannot exist in metadata that is saved into a serialized format.
    CModInternal = 0x22, // Indicates that the next byte specifies if the modifier is required and the next pointer sized number of bytes after that is the address of a TypeHandle. Signatures that contain the CModInternal CorElementType cannot exist in metadata that is saved into a seralized format.
}
```

An `ITypeHandle` is the representation of runtime type information. Consumers obtain
canonical handles from `RuntimeTypeSystem.GetTypeHandle` using the target address of a
runtime `TypeHandle` or `MethodTable`.

``` csharp
partial interface IRuntimeTypeSystem : IContract
{
    #region TypeHandle inspection APIs
    public virtual ITypeHandle GetTypeHandle(TargetPointer targetPointer);

    public virtual TargetPointer GetModule(ITypeHandle typeHandle);
    // A canonical method table is either the MethodTable itself, or in the case of a generic instantiation, it is the
    // MethodTable of the prototypical instance.
    public virtual TargetPointer GetCanonicalMethodTable(ITypeHandle typeHandle);
    // True if this MethodTable is the canonical MethodTable (i.e., EEClassOrCanonMT points directly to the EEClass)
    public virtual bool IsCanonicalMethodTable(ITypeHandle typeHandle);
    public virtual TargetPointer GetParentMethodTable(ITypeHandle typeHandle);

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

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

    // True if the MethodTable is the sentinel value associated with unallocated space in the managed heap
    public virtual bool IsFreeObjectMethodTable(ITypeHandle typeHandle);
    // True if the MethodTable is the System.Object MethodTable (g_pObjectClass)
    public virtual bool IsObject(ITypeHandle typeHandle);
    public virtual bool IsString(ITypeHandle typeHandle);
    // True if the CorElementType represents a GC-collectable object reference.
    public virtual bool IsCorElementTypeObjRef(CorElementType elementType);
    // Returns the address of one of the runtime's well-known singleton MethodTables, or
    // TargetPointer.Null if the runtime has not yet initialized that global.
    public virtual TargetPointer GetWellKnownMethodTable(WellKnownMethodTable kind);
    // True if the MethodTable represents a type that contains managed references
    public virtual bool ContainsGCPointers(ITypeHandle typeHandle);
    // True if the MethodTable represents a byref-like value type (Span<T>, ReadOnlySpan<T>, any ref struct).
    public virtual bool IsByRefLike(ITypeHandle typeHandle);
    // If the type is an HFA (or HVA on ARM64), returns true and sets elementSize
    // to 4, 8, or 16. Returns false otherwise (including on targets that don't
    // define FEATURE_HFA). Mirrors MethodTable::GetHFAType in
    // src/coreclr/vm/class.cpp.
    public virtual bool TryGetHFAElementSize(ITypeHandle typeHandle, out int elementSize);
    // True if the type requires 8-byte alignment on platforms that don't 8-byte align by default (FEATURE_64BIT_ALIGNMENT)
    public virtual bool RequiresAlign8(ITypeHandle typeHandle);
    // Returns the cached SystemV AMD64 eightbyte register-passing classification for a value type
    // (used to decide how a struct is passed in registers), or false if the type has no such
    // classification (not applicable, or the runtime was not built with UNIX_AMD64_ABI).
    public virtual bool TryGetSystemVAmd64EightByteClassification(ITypeHandle typeHandle, out SystemVAmd64EightByteClassification classification);
    // True if the MethodTable represents a continuation type used by the async continuation feature
    public virtual bool IsContinuationWithoutMetadata(ITypeHandle typeHandle);
    // Returns the GC pointer runs for the method table as (offset, size) pairs. Each
    // run starts Offset bytes from the object pointer (`this`), where offset 0
    // is the method table pointer, and includes Size bytes of contiguous pointers
    // For handles representing value types the object is assumed to be stored in the boxed layout.
    public virtual IEnumerable<(uint Offset, uint Size)> GetGCDescSeries(ITypeHandle typeHandle, uint numComponents = 0);
    public virtual bool IsDynamicStatics(ITypeHandle typeHandle);
    public virtual ushort GetNumInterfaces(ITypeHandle typeHandle);

    // 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(ITypeHandle typeHandle);
    public virtual ushort GetNumVtableSlots(ITypeHandle typeHandle);
    public virtual ushort GetNumMethods(ITypeHandle 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
    public virtual uint GetTypeDefTypeAttributes(ITypeHandle typeHandle);
    public ushort GetNumInstanceFields(ITypeHandle typeHandle);
    public ushort GetNumStaticFields(ITypeHandle typeHandle);
    public ushort GetNumThreadStaticFields(ITypeHandle typeHandle);
    public TargetPointer GetGCThreadStaticsBasePointer(ITypeHandle typeHandle, TargetPointer threadPtr);
    public TargetPointer GetNonGCThreadStaticsBasePointer(ITypeHandle typeHandle, TargetPointer threadPtr);
    public IEnumerable<TargetPointer> GetFieldDescList(ITypeHandle typeHandle);
    // True if the MethodTable represents a type tracked as an Objective-C reference type with a finalizer
    public bool IsTrackedReferenceWithFinalizer(ITypeHandle typeHandle);
    public TargetPointer GetGCStaticsBasePointer(ITypeHandle typeHandle);
    public TargetPointer GetNonGCStaticsBasePointer(ITypeHandle typeHandle);
    public virtual ReadOnlySpan<ITypeHandle> GetInstantiation(ITypeHandle typeHandle);
    public bool IsClassInited(ITypeHandle typeHandle);
    public bool IsInitError(ITypeHandle typeHandle);
    public virtual bool IsGenericTypeDefinition(ITypeHandle typeHandle);

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

    // Element type of the type. NOTE: this drops the CorElementType.GenericInst, and CorElementType.String is returned as CorElementType.Class.
    // NOTE: If this returns CorElementType.ValueType it may be a normal valuetype or a "NATIVE" valuetype used to represent an interop view of a structure
    // HasTypeParam will return true for cases where this is the interop view, and false for normal valuetypes.
    public virtual CorElementType GetSignatureCorElementType(ITypeHandle typeHandle);

    // Internal element type of the type. Unlike GetSignatureCorElementType, this returns the underlying
    // primitive type for enums (e.g. I4 for an enum with int underlying type).
    // For arrays, reference types, and TypeDescs, behaves identically to GetSignatureCorElementType.
    public virtual CorElementType GetInternalCorElementType(ITypeHandle typeHandle);

    bool IsValueType(ITypeHandle typeHandle);
    // return true if the TypeHandle represents an enum type.
    bool IsEnum(ITypeHandle typeHandle);
    // return true if the TypeHandle represents a delegate type (i.e., its parent is System.MulticastDelegate)
    bool IsDelegate(ITypeHandle typeHandle);
    // 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.
    bool IsArray(ITypeHandle typeHandle, out uint rank);
    ITypeHandle GetTypeParam(ITypeHandle typeHandle);
    ITypeHandle? GetConstructedType(ITypeHandle? typeHandle, CorElementType corElementType, int rank, ImmutableArray<ITypeHandle?> typeArguments, SignatureCallingConvention callConv = SignatureCallingConvention.Default);
    ITypeHandle GetPrimitiveType(CorElementType typeCode);
    bool IsGenericVariable(ITypeHandle typeHandle, out TargetPointer module, out uint token, out uint index);
    bool IsFunctionPointer(ITypeHandle typeHandle, out ReadOnlySpan<ITypeHandle> retAndArgTypes, out SignatureCallingConvention callConv);
    bool IsPointer(ITypeHandle typeHandle);
    bool IsTypeDesc(ITypeHandle typeHandle);
    TargetPointer GetLoaderModule(ITypeHandle typeHandle);
    TypedByRefInfo GetTypedByRefInfo(TargetPointer typedByRef);

    #endregion TypeHandle inspection APIs
}
```

### MethodDesc

A `MethodDesc` is the runtime representation of a managed method (either from IL, from reflection emit, or generated by the runtime).

```csharp
struct MethodDescHandle
{
    // no public properties or constructors

    internal TargetPointer Address { get; }
}
```

```csharp
public enum ArrayFunctionType
{
    Get = 0,
    Set = 1,
    Address = 2,
    Constructor = 3
}
```

```csharp
public enum OptimizationTier
{
    OptimizationTierUnknown,
    OptimizationTier0,
    OptimizationTier1,
    OptimizationTier1OSR,
    OptimizationTierOptimized,
    OptimizationTier0Instrumented,
    OptimizationTier1Instrumented,
}
```

```csharp
public enum GenericContextLoc
{
    None,
    InstArgMethodDesc,
    InstArgMethodTable,
    ThisPtr,
}

[Flags]
public enum AsyncMethodFlags : uint
{
    None = 0,
    AsyncCall = 0x1,
    IsAsyncVariant = 0x2,
    Thunk = 0x4,
    ReturnDroppingThunk = 0x8,
}

// Identifies one of the runtime's well-known singleton MethodTables, each addressable
// via a dedicated global pointer (e.g. g_pObjectClass, g_pStringClass, g_pFreeObjectMethodTable).
public enum WellKnownMethodTable
{
    Object,
    String,
    Array,
    Exception,
    Free,
    Canon,
}
```

```csharp
partial interface IRuntimeTypeSystem : IContract
{
    public virtual MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer);

    public virtual TargetPointer GetMethodTable(MethodDescHandle methodDesc);

    // Return true for an uninstantiated generic method
    public virtual bool IsGenericMethodDefinition(MethodDescHandle methodDesc);

    public virtual ReadOnlySpan<ITypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDesc);

    // Return mdTokenNil (0x06000000) if the method doesn't have a token, otherwise return the token of the method
    public virtual uint GetMethodToken(MethodDescHandle methodDesc);

    // Return true if a MethodDesc represents an array method
    // An array method is also a StoredSigMethodDesc
    public virtual bool IsArrayMethod(MethodDescHandle methodDesc, out ArrayFunctionType functionType);

    // Return true if a MethodDesc represents a method without metadata method, either an IL Stub dynamically
    // generated by the runtime, or a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
    // Or something else similar.
    // A no metadata method is also a StoredSigMethodDesc
    public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out string methodName);

    // Return true for a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
    // A DynamicMethod is also a StoredSigMethodDesc, and a NoMetadataMethod
    public virtual bool IsDynamicMethod(MethodDescHandle methodDesc);

    // return true if a MethodDesc represents an IL-backed method
    public virtual bool IsIL(MethodDescHandle methodDescHandle);

    // Return true if a MethodDesc represents an IL Stub dynamically generated by the runtime
    // A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod
    public virtual bool IsILStub(MethodDescHandle methodDesc);

    // Return true if a MethodDesc represents an IL stub with a special MethodDesc context arg
    public virtual bool HasMDContextArg(MethodDescHandle);

    // Determine where a shared generic method obtains its generic context.
    // Returns None if not shared, InstArgMethodDesc if an MD via a hidden parameter, InstArgMethodTable if an MT via a hidden parameter or ThisPtr if via the 'this' pointer's MethodTable.
    public virtual GenericContextLoc GetGenericContextLoc(MethodDescHandle methodDesc);

    // Return true if the method uses the async calling convention.
    // Corresponds to native MethodDesc::IsAsyncMethod().
    public virtual bool IsAsyncMethod(MethodDescHandle methodDesc);

    // Return true and the raw signature bytes for a MethodDesc, or false if no signature could be resolved.
    public virtual bool TryGetMethodSignature(MethodDescHandle methodDesc, out ReadOnlySpan<byte> signature);

    // Return true if a MethodDesc is in a collectible module
    public virtual bool IsCollectibleMethod(MethodDescHandle methodDesc);

    // Return true if a MethodDesc supports mulitiple code versions
    public virtual bool IsVersionable(MethodDescHandle methodDesc);

    // Return a pointer to the IL versioning state of the MethodDesc
    public virtual TargetPointer GetMethodDescVersioningState(MethodDescHandle methodDesc);

    // Return the MethodTable slot number of the MethodDesc
    public virtual ushort GetSlotNumber(MethodDescHandle methodDesc);

    // Return true if the MethodDesc has space associated with it for storing a pointer to a code block
    public virtual bool HasNativeCodeSlot(MethodDescHandle methodDesc);

    // Return the address of the space that stores a pointer to a code block associated with the MethodDesc
    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);

    // Gets the optimization tier stored on the MethodDesc's code data
    public virtual OptimizationTier GetMethodDescOptimizationTier(MethodDescHandle methodDesc);

    // Returns true if the method is eligible for tiered compilation
    public virtual bool IsEligibleForTieredCompilation(MethodDescHandle methodDesc);

    // Returns the normalized cDAC async flags for the method,
    // or AsyncMethodFlags.None if the method has no async method data.
    public virtual AsyncMethodFlags GetAsyncMethodFlags(MethodDescHandle methodDesc);

    // Returns the loaded non-return-dropping async variant with the same slot,
    // or TargetPointer.Null if the variant is not loaded.
    public virtual TargetPointer GetAsyncVariant(MethodDescHandle methodDesc);

    // Return true if the method is a wrapper stub (unboxing or instantiating).
    public virtual bool IsWrapperStub(MethodDescHandle methodDesc);

    // Return true if the method is an unboxing stub (a wrapper around a
    // value-type instance method that unboxes `this` before forwarding).
    public virtual bool IsUnboxingStub(MethodDescHandle methodDesc);

    // Returns true if the method signature uses the vararg calling convention.
    public virtual bool IsVarArg(MethodDescHandle methodDesc);

}
```

### FieldDesc
```csharp
TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer);
uint GetFieldDescMemberDef(TargetPointer fieldDescPointer);
bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer);
bool IsFieldDescStatic(TargetPointer fieldDescPointer);
bool IsFieldDescRVA(TargetPointer fieldDescPointer);
CorElementType GetFieldDescType(TargetPointer fieldDescPointer);
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition? fieldDef);
ITypeHandle? GetFieldDescApproxTypeHandle(TargetPointer fieldDescPointer);
bool TryGetFieldDescNext(TargetPointer fieldDescPointer, out TargetPointer nextFieldDesc);
TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer, bool unboxValueTypes = true);
TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread, bool unboxValueTypes = true);
```

### Other APIs
```csharp
void GetCoreLibFieldDescAndDef(string @namespace, string typeName, string fieldName, out TargetPointer fieldDescAddr, out FieldDefinition fieldDef);
```

## Version 1

### ITypeHandle

The `MethodTable` inspection APIs are implemented in terms of the following flags on the runtime `MethodTable` structure:

``` csharp
internal partial struct RuntimeTypeSystem_1
{
    // The lower 16-bits of the MTFlags field are used for these flags,
    // if WFLAGS_HIGH.HasComponentSize is unset
    [Flags]
    internal enum WFLAGS_LOW : uint
    {
        GenericsMask = 0x00000030,
        GenericsMask_NonGeneric = 0x00000000,            // no instantiation
        GenericsMask_SharedInst = 0x00000020,            // shared instantiation, e.g. List<__Canon> or List<MyValueType<__Canon>>
        GenericsMask_TypicalInstantiation = 0x00000030,  // the type instantiated at its formal parameters, e.g. List<T>

        IsByRefLike = 0x00001000,                        // value type that may contain managed pointers (e.g. Span<T>, ReadOnlySpan<T>)

        IsHFA = 0x00000800,                              // ARM/ARM64 only (FEATURE_HFA): Homogeneous Floating-point Aggregate.
                                                         // On UNIX_AMD64_ABI this bit is reused as IsRegStructPassed.

        StringArrayValues = GenericsMask_NonGeneric,
    }

    // Upper bits of MTFlags
    [Flags]
    internal enum WFLAGS_HIGH : uint
    {
        Category_Mask = 0x000F0000,
        Category_ElementType_Mask = 0x000E0000,
        Category_Array_Mask = 0x000C0000,
        Category_ValueType_Mask = 0x000C0000,

        Category_IfArrayThenSzArray = 0x00020000,
        Category_Array = 0x00080000,
        Category_ValueType = 0x00040000,
        Category_Nullable = 0x00050000,
        Category_Primitive = 0x00060000,
        Category_TruePrimitive = 0x00070000,
        Category_Interface = 0x000C0000,
        Collectible = 0x00200000,
        RequiresAlign8 = 0x00800000,
        ContainsGCPointers = 0x01000000,
        IsTrackedReferenceWithFinalizer = 0x04000000,
        ContainsGenericVariables = 0x20000000,
        HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size,
                                       // otherwise the lower bits are used for WFLAGS_LOW
    }

    [Flags]
    internal enum WFLAGS2_ENUM : uint
    {
        DynamicStatics = 0x0002,
        IsIntrinsicType = 0x0020,
    }

    // Encapsulates the MethodTable flags v1 uses
    internal struct MethodTableFlags
    {
        public uint MTFlags { get; }
        public uint MTFlags2 { get; }
        public uint BaseSize { get; }

        public WFLAGS_LOW GetFlag(WFLAGS_LOW mask) { ... /* mask & lower 16 bits of MTFlags */ }
        public WFLAGS_HIGH GetFlag(WFLAGS_HIGH mask) { ... /* mask & upper 16 bits of MTFlags */ }

        public WFLAGS2_ENUM GetFlag(WFLAGS2_ENUM mask) { ... /* mask & MTFlags2*/ }

        private bool TestFlagWithMask(WFLAGS_LOW mask, WFLAGS_LOW flag)
        {
            if (IsStringOrArray)
            {
                return (WFLAGS_LOW.StringArrayValues & mask) == flag;
            }
            else
            {
                return (FlagsLow & mask) == flag;
            }
        }

        public ushort ComponentSizeBits => (ushort)(MTFlags & 0x0000ffff); // only meaningful if HasComponentSize is set

        public bool HasComponentSize => GetFlag(WFLAGS_HIGH.HasComponentSize) != 0;
        public bool IsInterface => GetFlag(WFLAGS_HIGH.Category_Mask) == WFLAGS_HIGH.Category_Interface;
        public bool IsString => HasComponentSize && !IsArray && ComponentSizeBits == 2;
        public bool IsArray => GetFlag(WFLAGS_HIGH.Category_Array_Mask) == WFLAGS_HIGH.Category_Array;
        public bool IsStringOrArray => HasComponentSize;
        public ushort ComponentSize => HasComponentSize ? ComponentSizeBits : (ushort)0;
        public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric);
        public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0;
        public bool RequiresAlign8 => GetFlag(WFLAGS_HIGH.RequiresAlign8) != 0;
        public bool IsCollectible => GetFlag(WFLAGS_HIGH.Collectible) != 0;
        public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0;
        public bool IsIntrinsicType => GetFlag(WFLAGS2_ENUM.IsIntrinsicType) != 0;
        public bool IsTrackedReferenceWithFinalizer => GetFlag(WFLAGS_HIGH.IsTrackedReferenceWithFinalizer) != 0;
        public bool IsGenericTypeDefinition => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_TypicalInstantiation);
        public bool IsSharedByGenericInstantiations => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_SharedInst);
        public bool IsByRefLike => TestFlagWithMask(WFLAGS_LOW.IsByRefLike, WFLAGS_LOW.IsByRefLike);
        public bool IsHFA => TestFlagWithMask(WFLAGS_LOW.IsHFA, WFLAGS_LOW.IsHFA);
    }

    [Flags]
    internal enum EEClassOrCanonMTBits
    {
        EEClass = 0,
        CanonMT = 1,
        Mask = 1,
    }

    // 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,
    }
}
```

Internally the contract has a `MethodTable_1` struct that depends on the `MethodTable` data descriptor

```csharp
internal struct MethodTable_1
{
    internal RuntimeTypeSystem_1.MethodTableFlags 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 MethodTable_1(Data.MethodTable data)
    {
        Flags = new RuntimeTypeSystem_1.MethodTableFlags
        {
            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;
    }
}
```

Internally the contract uses extension methods on the `ITypeHandle` api so that it can distinguish between `MethodTable` and `TypeDesc`
```csharp
static class RuntimeTypeSystem_1_Helpers
{
    public static bool IsTypeDesc(this TypeHandle type)
    {
        return type.Address != 0 && (type.Address & TypeHandleBits.ValidMask) == TypeHandleBits.TypeDesc;
    }

    public static bool IsMethodTable(this TypeHandle type)
    {
        return type.Address != 0 && (type.Address & TypeHandleBits.ValidMask) == TypeHandleBits.MethodTable;
    }

    public static TargetPointer TypeDescAddress(this TypeHandle type)
    {
        if (!type.IsTypeDesc())
            return 0;

        return (ulong)type.Address & ~(ulong)TypeHandleBits.ValidMask;
    }
}
```

<!-- BEGIN GENERATED: usage contract=RuntimeTypeSystem version=c1 -->
### Data descriptors used

| Data Descriptor | Field | Type | Meaning |
| --- | --- | --- | --- |
| `ArrayMethodDesc` | *(type size)* | `uint32` | Base size for mcArray classification |
| `AsyncMethodData` | *(type size)* | `uint32` | Size of the async method data, added when HasAsyncMethodData flag is set |
| `AsyncMethodData` | `Flags` | `uint32` | Flags classifying the async method, including its calling convention, return shape, and whether it is a thunk |
| `AsyncMethodData` | `Signature` | `Signature` | The async variant's signature (see `Signature`) |
| `CLRToCOMCallMethodDesc` | *(type size)* | `uint32` | Base size for mcComInterOp classification |
| `CoreLibBinder` | `Classes` | `pointer` | Pointer to the array of core library type handles indexed by CorElementType |
| `DynamicMethodDesc` | *(type size)* | `uint32` | Base size for mcDynamic classification |
| `DynamicMethodDesc` | `MethodName` | `pointer` | Pointer to Null-terminated UTF8 string describing the Method desc |
| `DynamicStaticsInfo` | *(type size)* | `uint32` | Size of the data |
| `DynamicStaticsInfo` | `GCStatics` | `pointer` | Pointer to the GC statics |
| `DynamicStaticsInfo` | `NonGCStatics` | `pointer` | Pointer to non-GC statics |
| `EEClass` | `BaseSizePadding` | `uint8` | Trailing padding included in MethodTable.BaseSize but not occupied by instance fields |
| `EEClass` | `CorTypeAttr` | `uint32` | Various flags |
| `EEClass` | `FieldDescList` | `pointer` | A list of fields in the type |
| `EEClass` | `InternalCorElementType` | `uint8` | An InternalCorElementType uses the enum values of a CorElementType to indicate some of the information about the type of the type which uses the EEClass In particular, all reference types are CorElementType.Class, Enums are the element type of their underlying type and ValueTypes which can exactly be represented as an element type are represented as such, all other values types are represented as CorElementType.ValueType. |
| `EEClass` | `MethodDescChunk` | `pointer` | Pointer to the first MethodDescChunk of the EEClass |
| `EEClass` | `MethodTable` | `pointer` | Pointer to the canonical MethodTable of this type |
| `EEClass` | `NumInstanceFields` | `uint16` | Count of instance fields of the EEClass |
| `EEClass` | `NumMethods` | `uint16` | Count of methods attached to the EEClass |
| `EEClass` | `NumNonVirtualSlots` | `uint16` | Count of non-virtual slots for the EEClass |
| `EEClass` | `NumStaticFields` | `uint16` | Count of static fields of the EEClass |
| `EEClass` | `NumThreadStaticFields` | `uint16` | Count of threadstatic fields of the EEClass |
| `EEClass` | `OptionalFields` | `pointer` | Pointer to the `EEClassOptionalFields` for this type, or null if it has none |
| `EEClassOptionalFields` | `EightByteRegistersInfo` | `SystemVEightByteRegistersInfo` | Inline `SystemVEightByteRegistersInfo` describing the SystemV AMD64 register-passing classification (only populated on UNIX_AMD64_ABI builds) |
| `EEImplMethodDesc` | *(type size)* | `uint32` | Base size for mcEEImpl classification |
| `FCallMethodDesc` | *(type size)* | `uint32` | Base size for mcFCall classification |
| `FieldDesc` | *(type size)* | `uint32` | Size in bytes of each FieldDesc entry in a type's field descriptor list |
| `FieldDesc` | `DWord1` | `uint32` | The FD's flags and token |
| `FieldDesc` | `DWord2` | `uint32` | Packed flags and offset word containing the field's offset; the FieldOffsetNewEnc sentinel identifies an EnC-added field without assigned storage |
| `FieldDesc` | `MTOfEnclosingClass` | `pointer` | Pointer to method table of enclosing class |
| `FnPtrTypeDesc` | `CallConv` | `uint32` | Lower 8 bits is the calling convention bit as extracted by the signature that defines this TypeDesc |
| `FnPtrTypeDesc` | `LoaderModule` | `pointer` | Loader module associated with the function-pointer type |
| `FnPtrTypeDesc` | `NumArgs` | `uint32` | Number of arguments to the function described by the TypeDesc |
| `FnPtrTypeDesc` | `RetAndArgTypes` | `pointer` | Pointer to an array of TypeHandle addresses. This length of this is 1 more than NumArgs |
| `GCCoverageInfo` | `SavedCode` | `pointer` | Pointer to the GCCover saved code copy, if supported |
| `GenericsDictInfo` | `NumDicts` | `uint16` | Number of instantiation dictionaries, including inherited ones, in this GenericsDictInfo |
| `GenericsDictInfo` | `NumTypeArgs` | `uint16` | Number of type arguments in the type or method instantiation described by this GenericsDictInfo |
| `InstantiatedMethodDesc` | *(type size)* | `uint32` | Base size for mcInstantiated classification |
| `InstantiatedMethodDesc` | `Flags2` | `uint16` | Flags for the InstantiatedMethodDesc |
| `InstantiatedMethodDesc` | `NumGenericArgs` | `uint16` | How many generic args the method has |
| `InstantiatedMethodDesc` | `PerInstInfo` | `pointer` | The pointer to the method's type arguments |
| `LoaderAllocator` | `CreationNumber` | `uint64` | Monotonically-increasing creation number assigned to each collectible LoaderAllocator. |
| `LoaderAllocator` | `DynamicHelpersStubHeap` | `pointer` | Dynamic-helper stub heap (optional, present when ReadyToRun dynamic-helper stubs are enabled) |
| `LoaderAllocator` | `ExecutableHeap` | `pointer` | Executable-code heap |
| `LoaderAllocator` | `FixupPrecodeHeap` | `pointer` | Fixup-precode heap (optional, present when fixup precodes are supported) |
| `LoaderAllocator` | `HighFrequencyHeap` | `pointer` | High-frequency allocation heap |
| `LoaderAllocator` | `IsCollectible` | `uint8` | Non-zero if the LoaderAllocator is collectible. |
| `LoaderAllocator` | `LowFrequencyHeap` | `pointer` | Low-frequency allocation heap |
| `LoaderAllocator` | `NewStubPrecodeHeap` | `pointer` | New-stub-precode heap (optional, absent with portable entry points) |
| `LoaderAllocator` | `ObjectHandle` | `ObjectHandle` | Handle to the managed loader allocator object |
| `LoaderAllocator` | `ReferenceCount` | `uint32` | Reference count of the loader allocator |
| `LoaderAllocator` | `StaticsHeap` | `pointer` | Heap containing statics-related allocations |
| `LoaderAllocator` | `VirtualCallStubManager` | `pointer` | Pointer to the virtual-call stub manager |
| `MethodDesc` | *(type size)* | `uint32` | Base size for mcIL classification |
| `MethodDesc` | `ChunkIndex` | `uint8` | Offset of this MethodDesc relative to the end of its containing MethodDescChunk - in multiples of MethodDescAlignment |
| `MethodDesc` | `CodeData` | `pointer` | Pointer to per-method code data containing entry-point and code-versioning state |
| `MethodDesc` | `EntryPointFlags` | `uint8` | Flags describing entry-point state, including whether a temporary entry point has been assigned |
| `MethodDesc` | `Flags` | `uint16` | The method's flags |
| `MethodDesc` | `Flags3AndTokenRemainder` | `uint16` | More flags for the method, and the low bits of the method's token's RID |
| `MethodDesc` | `GCCoverageInfo` | `pointer` | The method's GCCover debug info, if supported |
| `MethodDesc` | `Slot` | `uint16` | The method's slot |
| `MethodDescChunk` | *(type size)* | `uint32` | Size of the data descriptor layout |
| `MethodDescChunk` | `Count` | `uint8` | The number of MethodDesc entries in this chunk, minus 1. |
| `MethodDescChunk` | `FlagsAndTokenRange` | `uint16` | MethodDescChunk flags, and the upper bits of the method token's RID |
| `MethodDescChunk` | `MethodTable` | `pointer` | The method table set of methods belongs to |
| `MethodDescChunk` | `Next` | `pointer` | The next chunk of methods |
| `MethodDescChunk` | `Size` | `uint8` | The size of this MethodDescChunk  following this MethodDescChunk header, minus 1. In multiples of MethodDescAlignment |
| `MethodDescCodeData` | `OptimizationTier` | `uint32` | Optimization tier of the method's current native code |
| `MethodDescCodeData` | `TemporaryEntryPoint` | `CodePointer` | Temporary code entry point used before the method has a stable entry point |
| `MethodDescCodeData` | `VersioningState` | `pointer` | The IL versioning state associated with a method descriptor |
| `MethodImpl` | *(type size)* | `uint32` | Size of the MethodImpl data, added when HasMethodImpl flag is set |
| `MethodTable` | *(type size)* | `uint32` | Size in bytes of the fixed MethodTable structure before its vtable indirections |
| `MethodTable` | `AuxiliaryData` | `pointer` | Pointer to the AuxiliaryData of a method table |
| `MethodTable` | `BaseSize` | `uint32` | BaseSize of a MethodTable |
| `MethodTable` | `EEClassOrCanonMT` | `pointer` | Path to both EEClass and canonical MethodTable of a MethodTable |
| `MethodTable` | `Module` | `pointer` | Module for MethodTable |
| `MethodTable` | `MTFlags` | `uint32` | One of the flags fields on MethodTable |
| `MethodTable` | `MTFlags2` | `uint32` | One of the flags fields on MethodTable |
| `MethodTable` | `NumInterfaces` | `uint16` | Number of interfaces of MethodTable |
| `MethodTable` | `NumVirtuals` | `uint16` | Number of virtual methods in MethodTable |
| `MethodTable` | `ParentMethodTable` | `pointer` | Parent type pointer of MethodTable |
| `MethodTable` | `PerInstInfo` | `pointer` | Either the array element type, or pointer to generic information for MethodTable |
| `MethodTableAuxiliaryData` | `Flags` | `uint32` | Flags of MethodTableAuxiliaryData |
| `MethodTableAuxiliaryData` | `LoaderModule` | `pointer` | The loader module associated with a method table |
| `MethodTableAuxiliaryData` | `OffsetToNonVirtualSlots` | `int16` | Offset from the auxiliary data address to the array of non-virtual slots |
| `NativeCodeSlot` | *(type size)* | `uint32` | Size of the native code slot, added when HasNativeCodeSlot flag is set |
| `NonVtableSlot` | *(type size)* | `uint32` | Size of the non-vtable slot, added when HasNonVtableSlot flag is set |
| `Object` | *(type size)* | `uint32` | Size in bytes of the fixed Object portion through its MethodTable pointer |
| `Object` | `m_pMethTab` | `pointer` | Method table for the object |
| `ParamTypeDesc` | `TypeArg` | `pointer` | Associated type argument |
| `PInvokeMethodDesc` | *(type size)* | `uint32` | Base size for mcPInvoke classification |
| `Signature` | `SignatureLength` | `uint32` | Length in bytes of the raw signature blob. |
| `Signature` | `SignaturePointer` | `pointer` | Target address of the raw signature blob. |
| `StoredSigMethodDesc` | `cSig` | `uint32` | Count of bytes in the metadata signature |
| `StoredSigMethodDesc` | `ExtendedFlags` | `uint32` | Flags field for the StoredSigMethodDesc |
| `StoredSigMethodDesc` | `Sig` | `pointer` | Pointer to a metadata signature |
| `SystemVEightByteRegistersInfo` | `EightByteClassification0` | `uint8` | Register classification of the first eightbyte |
| `SystemVEightByteRegistersInfo` | `EightByteClassification1` | `uint8` | Register classification of the second eightbyte |
| `SystemVEightByteRegistersInfo` | `EightByteSize0` | `uint8` | Byte size of the first eightbyte |
| `SystemVEightByteRegistersInfo` | `EightByteSize1` | `uint8` | Byte size of the second eightbyte |
| `SystemVEightByteRegistersInfo` | `NumEightBytes` | `uint8` | Number of eightbyte slots used to pass the value type in registers (0 if not passed in registers) |
| `ThreadStaticsInfo` | *(type size)* | `uint32` | Size in bytes of the thread-statics information stored immediately before MethodTable auxiliary data |
| `ThreadStaticsInfo` | `GCTlsIndex` | `pointer` | Pointer to GC thread local storage index |
| `ThreadStaticsInfo` | `NonGCTlsIndex` | `pointer` | Pointer to non-GC thread local storage index |
| `TypedByRef` | `Data` | `pointer` | Managed pointer (the byref) stored in a System.TypedReference value |
| `TypedByRef` | `Type` | `pointer` | Raw TypeHandle pointer of the referent type |
| `TypeDesc` | `TypeAndFlags` | `uint32` | The lower 8 bits are the CorElementType of the TypeDesc, the upper 24 bits are reserved for flags |
| `TypeVarTypeDesc` | `Index` | `uint32` | Zero-based index within the declaring type or method |
| `TypeVarTypeDesc` | `Module` | `pointer` | Pointer to module which defines the type variable |
| `TypeVarTypeDesc` | `Token` | `uint32` | Token of the type variable |

### Global variables used

| Global | Type | Meaning |
| --- | --- | --- |
| `ArrayBaseSize` | `uint32` | The base size of an array object; used to compute multidimensional array rank from MethodTable::BaseSize |
| `CanonMethodTable` | `pointer` | A pointer to the address of the canonical MethodTable used for shared generics (System.__Canon) |
| `ContinuationMethodTable` | `pointer` | A pointer to the address of the base Continuation MethodTable, or null if no continuations have been created |
| `ContinuationSingletonEEClass` | `pointer` | A pointer to the address of the singleton EEClass shared by continuation subtypes that have no metadata of their own |
| `CoreLib` | `pointer` | Pointer to the CoreLibBinder data containing well-known core library type handles |
| `ExceptionMethodTable` | `pointer` | A pointer to the address of the System.Exception MethodTable (g_pExceptionClass) |
| `FieldOffsetBigRVA` | `uint32` | Sentinel value of FieldDesc::DWord2 indicating the field is an RVA static whose offset is too large to encode in the bitfield; the real offset must be read from the field's metadata (FieldDefinition.GetRelativeVirtualAddress). |
| `FieldOffsetDynamicRVA` | `uint32` | Sentinel FieldDesc offset for an EnC-added RVA field whose enclosing type is not yet loaded |
| `FreeObjectMethodTable` | `pointer` | A pointer to the address of a MethodTable used by the GC to indicate reclaimed memory |
| `MethodDescAlignment` | `uint64` | MethodDescChunk trailing data is allocated in multiples of this constant.  The size (in bytes) of each MethodDesc (or subclass) instance is a multiple of this constant. |
| `MethodDescTokenRemainderBitCount` | `uint8` | Number of bits in the token remainder in MethodDesc |
| `MulticastDelegateMethodTable` | `pointer` | A pointer to the address of the System.MulticastDelegate MethodTable (g_pMulticastDelegateClass) |
| `ObjectArrayMethodTable` | `pointer` | A pointer to the address of the object[] MethodTable (g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]) |
| `ObjectMethodTable` | `pointer` | Address of the global variable holding the System.Object MethodTable pointer |
| `StaticsPointerMask` | `pointer` | For masking out a bit of DynamicStaticsInfo pointer fields |
| `StringMethodTable` | `pointer` | Pointer to the method table for `System.String` |

### Contracts used

| Contract Name |
| --- |
| `CodeVersions` |
| `EcmaMetadata` |
| `ExecutionManager` |
| `Loader` |
| `PlatformMetadata` |
| `PrecodeStubs` |
| `ReJIT` |
| `RuntimeInfo` |
| `Signature` |
| `Thread` |
<!-- END GENERATED: usage contract=RuntimeTypeSystem version=c1 -->

### Contract Constants:
| Name | Type | Purpose | Value |
| --- | --- | --- | --- |
| `TYPE_MASK_OFFSET` | int | The number of bits the type is shifted left in the field desc flags2 | `27` |

```csharp
    private readonly Dictionary<TargetPointer, MethodTable_1> _methodTables;

    internal TargetPointer FreeObjectMethodTablePointer {get; }
    internal TargetPointer ObjectMethodTablePointer {get; }
    internal TargetPointer ContinuationMethodTablePointer {get; }
    private TargetPointer _continuationSingletonEEClassPointer;

    public ITypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
    {
        ... // validate that typeHandlePointer points to something that looks like a MethodTable or a TypeDesc.
        ... // If this is a MethodTable
        ... //     read Data.MethodTable from typeHandlePointer.
        ... //     create a MethodTable_1 and add it to _methodTables.
        return GetOrCreateTargetTypeHandle(typeHandlePointer);
    }

    public TargetPointer GetModule(ITypeHandle 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;
            }
        }
        return TargetPointer.Null;
    }

    internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr)
    {
        return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask);
    }

    public TargetPointer GetCanonicalMethodTable(ITypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(TypeHandle).MethodTable;

    public TargetPointer GetParentMethodTable(ITypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : _methodTables[TypeHandle.Address].ParentMethodTable;

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

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

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

    private TargetPointer GetClassPointer(ITypeHandle TypeHandle)
    {
        // Returns TargetPointer.Null if not a MethodTable.
        // If EEClassOrCanonMT points directly to an EEClass, returns that pointer.
        // If EEClassOrCanonMT is a tagged pointer to a canonical MethodTable, follows
        // the canonical MT and returns its EEClass pointer.
    }

    private Data.EEClass GetClassData(ITypeHandle TypeHandle)
    {
        TargetPointer eeClassPtr = GetClassPointer(TypeHandle);
        ... // read Data.EEClass data from eeClassPtr
    }

    public bool TryGetSystemVAmd64EightByteClassification(ITypeHandle typeHandle, out SystemVAmd64EightByteClassification classification)
    {
        classification = default;
        if (!typeHandle.IsMethodTable())
            return false;

        // The SystemV eightbyte register classification lives in the EEClass optional fields and is
        // only present on UNIX_AMD64_ABI builds; return false if the type has no optional fields.
        TargetPointer optionalFields = GetClassData(typeHandle).OptionalFields;
        if (optionalFields == TargetPointer.Null)
            return false;

        TargetPointer eightByteInfo = optionalFields + /* EEClassOptionalFields::EightByteRegistersInfo offset */;
        byte numEightBytes = target.Read<byte>(eightByteInfo + /* SystemVEightByteRegistersInfo::NumEightBytes offset */);

        // The underlying data only stores two eightbyte slots; treat a count of 0 or > 2 as no classification.
        if (numEightBytes == 0 || numEightBytes > 2)
            return false;

        // For each eightbyte read its classification and size at the corresponding offsets:
        //   EightByteClassification0/1 and EightByteSize0/1
        // and populate 'classification' with one SystemVAmd64EightByte per eightbyte (First is always
        // present; Second is present only when numEightBytes > 1). Return true.
    }

    public bool IsFreeObjectMethodTable(ITypeHandle TypeHandle) => FreeObjectMethodTablePointer == TypeHandle.Address;

    public bool IsObject(ITypeHandle TypeHandle) => ObjectMethodTablePointer != TargetPointer.Null && ObjectMethodTablePointer == TypeHandle.Address;

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

    public bool IsCorElementTypeObjRef(CorElementType elementType) =>
        elementType is CorElementType.Class
            or CorElementType.Object
            or CorElementType.String
            or CorElementType.Array
            or CorElementType.SzArray;

    public TargetPointer GetWellKnownMethodTable(WellKnownMethodTable kind)
    {
        // Map the well-known kind to the corresponding runtime global. Returns
        // TargetPointer.Null if the global is missing or the pointer it stores has
        // not yet been initialized (e.g. SOS load notifications can run before the
        // runtime has populated the MT globals).
        string globalName = kind switch
        {
            WellKnownMethodTable.Object => "ObjectMethodTable",
            WellKnownMethodTable.String => "StringMethodTable",
            WellKnownMethodTable.Array => "ObjectArrayMethodTable",
            WellKnownMethodTable.Exception => "ExceptionMethodTable",
            WellKnownMethodTable.Free => "FreeObjectMethodTable",
            WellKnownMethodTable.Canon => "CanonMethodTable",
        };
        if (!target.TryReadGlobalPointer(globalName, out TargetPointer? ptrPtr))
            return TargetPointer.Null;
        if (!target.TryReadPointer(ptrPtr.Value, out TargetPointer value))
            return TargetPointer.Null;
        return value;
    }

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

    public bool IsByRefLike(ITypeHandle typeHandle) => typeHandle.IsMethodTable() && _methodTables[typeHandle.Address].Flags.IsByRefLike;

    // Mirrors MethodTable::GetHFAType in src/coreclr/vm/class.cpp. Pseudocode:
    //
    //   TryGetHFAElementSize(th):
    //       if targetArch is not ARM/ARM64:               return false
    //       if !th.Flags.IsHFA:                           return false
    //       if targetArch is ARM:                         return (true, th.Flags.RequiresAlign8 ? 8 : 4)
    //       mt = th
    //       loop (bounded depth):
    //           if (elem = GetVectorHFAElementSize(mt)):  return (true, elem)
    //           field = first non-static field of mt
    //           if field is null:                         return false
    //           switch field.ElementType:
    //               R4:        return (true, 4)
    //               R8:        return (true, 8)
    //               ValueType: mt = GetFieldDescApproxTypeHandle(field); continue
    //               default:   return false
    //
    //   GetVectorHFAElementSize(mt):                      // detects HVA shapes
    //       if !mt.Flags.IsIntrinsicType:                 return 0
    //       (ns, name) = typedef name+namespace via EcmaMetadata
    //       elem = match on (ns, name):
    //           "System.Numerics", "Vector`1":            NumInstanceFieldBytes (8 or 16, else 0)
    //           "System.Runtime.Intrinsics", "Vector128`1": 16
    //           "System.Runtime.Intrinsics", "Vector64`1":  8
    //           _:                                          return 0
    //       if !CorIsNumericalType(GetInstantiation(mt)[0]): return 0
    //       return elem
    public bool TryGetHFAElementSize(ITypeHandle typeHandle, out int elementSize) { ... }

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

    public bool IsCanonicalMethodTable(ITypeHandle typeHandle)
        => typeHandle.IsMethodTable() && _methodTables[typeHandle.Address].IsCanonMT;

    public bool IsContinuationWithoutMetadata(ITypeHandle typeHandle) => typeHandle.IsMethodTable()
        && ContinuationMethodTablePointer != TargetPointer.Null
        && _methodTables[typeHandle.Address].ParentMethodTable == ContinuationMethodTablePointer
        && _continuationSingletonEEClassPointer != TargetPointer.Null
        && GetClassPointer(typeHandle) == _continuationSingletonEEClassPointer;

    IEnumerable<(uint Offset, uint Size)> GetGCDescSeries(ITypeHandle typeHandle, uint numComponents = 0)
    {
        // Returns empty if not a method table or has no GC pointers.
        // Compute objectSize: baseSize + numComponents * componentSize.
        // For non-array types, numComponents == 0 so objectSize == baseSize.

        // Read NumSeries from (mtAddress - pointerSize), sign-extended to native width.
        // NumSeries == 0 → empty.

        // NumSeries > 0: Regular series.
        // Memory layout (each slot is pointer-sized, growing away from MT):
        //
        //   MT - (2*N+1)*ptrSize : series[N-1].seriessize
        //   MT - (2*N)  *ptrSize : series[N-1].startoffset
        //   ...
        //   MT - 3*ptrSize       : series[0].seriessize
        //   MT - 2*ptrSize       : series[0].startoffset
        //   MT - 1*ptrSize       : NumSeries (positive)
        //   MT                   : MethodTable
        //
        // The raw seriessize is stored with baseSize subtracted.
        // Add objectSize back to get the true run length:
        //   trueRunLength = rawSeriesSize + objectSize
        // For non-arrays objectSize == baseSize, recovering the actual run.
        // For arrays objectSize > baseSize, extending the single series across all elements.

        // NumSeries < 0: Value-class (repeating) series.
        // |NumSeries| val_serie_items describe pointer runs within one array element.
        // Memory layout:
        //
        //   MT - (N+2)*ptrSize : val_serie[N-1]
        //   ...
        //   MT - 3*ptrSize     : val_serie[0]
        //   MT - 2*ptrSize     : startoffset
        //   MT - 1*ptrSize     : NumSeries (-N)
        //   MT                 : MethodTable
        //
        // Each val_serie_item is { HALF_SIZE_T nptrs; HALF_SIZE_T skip; } packed into one pointer-width.
        // HALF_SIZE_T is uint16 on 32-bit, uint32 on 64-bit.
        // Read nptrs and skip as separate typed reads for endianness safety.
        // runBytes = nptrs * pointerSize; advance currentOffset by runBytes + skip after each item.
        //
        // The val_serie_items describe the GC pointer layout of a single array element.
        // An outer loop repeats the pattern across all elements in the array:
        //   currentOffset = startoffset
        //   while currentOffset <= objectSize - pointerSize:
        //       for each val_serie_item:
        //           yield (currentOffset, nptrs * pointerSize)
        //           currentOffset += nptrs * pointerSize + skip
    }

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

    public ushort GetNumInterfaces(ITypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : _methodTables[TypeHandle.Address].NumInterfaces;

    public uint GetTypeDefToken(ITypeHandle TypeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return 0;

        MethodTable_1 typeHandle = _methodTables[TypeHandle.Address];
        return (uint)(typeHandle.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
    }

    public ushort GetNumVtableSlots(ITypeHandle 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(ITypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).NumMethods;

    public uint GetTypeDefTypeAttributes(ITypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).CorTypeAttr;

    public ushort GetNumInstanceFields(ITypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumInstanceFields;

    public ushort GetNumStaticFields(ITypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumStaticFields;

    public ushort GetNumThreadStaticFields(ITypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumThreadStaticFields;

    public IEnumerable<TargetPointer> GetFieldDescList(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            yield break;

        TargetPointer fieldDescListPtr = GetClassData(typeHandle).FieldDescList;
        uint fieldDescSize = _target.GetTypeInfo(DataType.FieldDesc).Size!.Value;

        ushort numInstanceFields = GetNumInstanceFields(typeHandle);
        TargetPointer parentMT = GetParentMethodTable(typeHandle);
        if (parentMT != TargetPointer.Null)
        {
            ITypeHandle parentHandle = GetTypeHandle(parentMT);
            numInstanceFields -= GetNumInstanceFields(parentHandle);
        }
        int totalFields = numInstanceFields + GetNumStaticFields(typeHandle);
        for (int i = 0; i < totalFields; i++)
        {
            yield return fieldDescListPtr + (ulong)i * fieldDescSize;
        }
    }

    public bool IsTrackedReferenceWithFinalizer(ITypeHandle typeHandle) => typeHandle.IsMethodTable() && _methodTables[typeHandle.Address].Flags.IsTrackedReferenceWithFinalizer;

    public TargetPointer GetGCStaticsBasePointer(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return TargetPointer.Null;

        MethodTable methodTable = _methodTables[typeHandle.Address];
        if (!methodTable.Flags.IsDynamicStatics)
            return TargetPointer.Null;
        TargetPointer dynamicStaticsInfoSize = target.GetTypeInfo(DataType.DynamicStaticsInfo).Size!.Value;
        TargetPointer mask = target.ReadGlobalPointer("StaticsPointerMask");

        TargetPointer dynamicStaticsInfo = methodTable.AuxiliaryData - dynamicStaticsInfoSize;
        return (target.ReadPointer(dynamicStaticsInfo + /* DynamicStaticsInfo::GCStatics offset */) & (ulong)mask);
    }

    public TargetPointer GetNonGCStaticsBasePointer(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return TargetPointer.Null;

        MethodTable methodTable = _methodTables[typeHandle.Address];
        if (!methodTable.Flags.IsDynamicStatics)
            return TargetPointer.Null;
        TargetPointer dynamicStaticsInfoSize = target.GetTypeInfo(DataType.DynamicStaticsInfo).Size!.Value;
        TargetPointer mask = target.ReadGlobalPointer("StaticsPointerMask");

        TargetPointer dynamicStaticsInfo = methodTable.AuxiliaryData - dynamicStaticsInfoSize;
        return (target.ReadPointer(dynamicStaticsInfo + /* DynamicStaticsInfo::NonGCStatics offset */) & (ulong)mask);
    }

    public TargetPointer GetGCThreadStaticsBasePointer(ITypeHandle typeHandle, TargetPointer threadPtr)
    {
        if (!typeHandle.IsMethodTable())
            return TargetPointer.Null;
        MethodTable_1 methodTable = _methodTables[typeHandle.Address];
        TargetPointer threadStaticsInfoSize = target.GetTypeInfo(DataType.ThreadStaticsInfo).Size;
        TargetPointer threadStaticsInfoAddr = methodTable.AuxiliaryData - threadStaticsInfoSize;

        TargetPointer tlsIndexAddr = threadStaticsInfoAddr + /* ThreadStaticsInfo::GCTlsIndex offset */;
        Contracts.IThread threadContract = target.Contracts.Thread;
        return threadContract.GetThreadLocalStaticBase(threadPtr, tlsIndexAddr);
    }

    public TargetPointer GetNonGCThreadStaticsBasePointer(ITypeHandle typeHandle, TargetPointer threadPtr)
    {
        if (!typeHandle.IsMethodTable())
            return TargetPointer.Null;
        MethodTable_1 methodTable = _methodTables[typeHandle.Address];
        TargetPointer threadStaticsInfoSize = target.GetTypeInfo(DataType.ThreadStaticsInfo).Size;
        TargetPointer threadStaticsInfoAddr = methodTable.AuxiliaryData - threadStaticsInfoSize;

        TargetPointer tlsIndexAddr = threadStaticsInfoAddr + /* ThreadStaticsInfo::NonGCTlsIndex offset */;
        Contracts.IThread threadContract = target.Contracts.Thread;
        return threadContract.GetThreadLocalStaticBase(threadPtr, tlsIndexAddr);
    }

    public ReadOnlySpan<ITypeHandle> GetInstantiation(ITypeHandle TypeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return default;

        MethodTable_1 typeHandle = _methodTables[TypeHandle.Address];
        if (!typeHandle.Flags.HasInstantiation)
            return default;

        TargetPointer perInstInfo = typeHandle.PerInstInfo;
        TargetPointer genericsDictInfo = perInstInfo - (ulong)_target.PointerSize;
        TargetPointer dictionaryPointer = _target.ReadPointer(perInstInfo);

        int NumTypeArgs = // Read NumTypeArgs from genericsDictInfo using GenericsDictInfo contract
        ITypeHandle[] instantiation = new ITypeHandle[NumTypeArgs];
        for (int i = 0; i < NumTypeArgs; i++)
            instantiation[i] = GetTypeHandle(_target.ReadPointer(dictionaryPointer + _target.PointerSize * i));

        return instantiation;
    }

    public bool IsClassInited(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return false;
        TargetPointer auxiliaryDataPtr = target.ReadPointer(typeHandle.Address + /* MethodTable.AuxiliaryData offset */);
        TargetPointer flagsPtr = target.ReadPointer(auxiliaryDataPtr + /* MethodTableAuxiliaryData::Flags offset */);
        uint flags = target.Read<uint>(flagsPtr);
        return (flags & (uint)MethodTableAuxiliaryFlags.Initialized) != 0;
    }

    public bool IsInitError(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return false;
        TargetPointer auxiliaryDataPtr = target.ReadPointer(typeHandle.Address + /* MethodTable.AuxiliaryData offset */);
        TargetPointer flagsPtr = target.ReadPointer(auxiliaryDataPtr + /* MethodTableAuxiliaryData::Flags offset */);
        uint flags = target.Read<uint>(flagsPtr);
        return (flags & (uint)MethodTableAuxiliaryFlags.IsInitError) != 0;
    }

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

    public bool IsCollectible(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return false;
        MethodTable typeHandle = _methodTables[typeHandle.Address];
        return typeHandle.Flags.IsCollectible;
    }

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

    public bool HasTypeParam(ITypeHandle typeHandle)
    {
        if (typeHandle.IsMethodTable())
        {
            MethodTable typeHandle = _methodTables[typeHandle.Address];
            return typeHandle.Flags.IsArray;
        }
        else if (typeHandle.IsTypeDesc())
        {
            int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
            CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);
            switch (elemType)
            {
                case CorElementType.ValueType:
                case CorElementType.Byref:
                case CorElementType.Ptr:
                    return true;
            }
        }
        return false;
    }

    public CorElementType GetSignatureCorElementType(ITypeHandle typeHandle)
    {
        if (typeHandle.IsMethodTable())
        {
            MethodTable typeHandle = _methodTables[typeHandle.Address];

            switch (typeHandle.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
            {
                case WFLAGS_HIGH.Category_Array:
                    return CorElementType.Array;
                case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
                    return CorElementType.SzArray;
                case WFLAGS_HIGH.Category_ValueType:
                case WFLAGS_HIGH.Category_Nullable:
                case WFLAGS_HIGH.Category_Primitive:
                    return CorElementType.ValueType;
                case WFLAGS_HIGH.Category_TruePrimitive:
                    return (CorElementType)GetClassData(typeHandle).InternalCorElementType;
                default:
                    return CorElementType.Class;
            }
        }
        else if (typeHandle.IsTypeDesc())
        {
            int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
            return (CorElementType)(TypeAndFlags & 0xFF);
        }
        return default(CorElementType);
    }

    // Internal element type: returns the underlying primitive type for enums. For all other types, identical to GetSignatureCorElementType.
    public CorElementType GetInternalCorElementType(ITypeHandle 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(ITypeHandle typeHandle)
    {
        // if methodtable: check WFLAGS_HIGH for Category_ValueType
        // if typedesc: check for CorElementType.ValueType
    }

    // Enums have Category_Primitive 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 (Int32, etc.) have Category_TruePrimitive.
    public bool IsEnum(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return false;

        MethodTable methodTable = _methodTables[typeHandle.Address];
        return methodTable.Flags.GetFlag(WFLAGS_HIGH.Category_Mask) == WFLAGS_HIGH.Category_Primitive;
    }

    public bool IsDelegate(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            return false;

        TargetPointer parentMT = GetParentMethodTable(typeHandle);
        return parentMT == MulticastDelegateMethodTablePointer;
    }

    // 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(ITypeHandle typeHandle, out uint rank)
    {
        if (typeHandle.IsMethodTable())
        {
            MethodTable typeHandle = _methodTables[typeHandle.Address];

            switch (typeHandle.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
            {
                case WFLAGS_HIGH.Category_Array:
                    // Multidim array: BaseSize = ArrayBaseSize + Rank * sizeof(uint) * 2
                    uint arrayBaseSize = target.ReadGlobal<uint>("ArrayBaseSize");
                    rank = (typeHandle.Flags.BaseSize - arrayBaseSize) / (sizeof(uint) * 2);
                    return true;

                case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
                    rank = 1;
                    return true;
            }
        }

        rank = 0;
        return false;
    }

    public ITypeHandle GetTypeParam(ITypeHandle typeHandle)
    {
        if (typeHandle.IsMethodTable())
        {
            MethodTable typeHandle = _methodTables[typeHandle.Address];

            // Validate that this is an array
            if (!typeHandle.Flags.IsArray)
                throw new ArgumentException(nameof(typeHandle));

            return GetTypeHandle(typeHandle.PerInstInfo);
        }
        else if (typeHandle.IsTypeDesc())
        {
            int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
            CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);

            switch (elemType)
            {
                case CorElementType.ValueType:
                case CorElementType.Byref:
                case CorElementType.Ptr:
                    TargetPointer typeArgPointer = // Read TypeArg field from ParamTypeDesc contract using address typeHandle.TypeDescAddress()
                    return GetTypeHandle(typeArgPointer);
            }
        }
        throw new ArgumentException(nameof(typeHandle));
    }

    // helper functions

    private bool GenericInstantiationMatch(ITypeHandle genericType, ITypeHandle potentialMatch, ImmutableArray<ITypeHandle> typeArguments)
    {
        ReadOnlySpan<ITypeHandle> 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(ITypeHandle elementType, CorElementType corElementType, int rank, ITypeHandle 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 FnPtrMatch(ITypeHandle candidate, ImmutableArray<ITypeHandle> retAndArgTypes, SignatureCallingConvention callConv)
    {
        if (!IsFunctionPointer(candidate, out ReadOnlySpan<ITypeHandle> candidateRetAndArgs, out SignatureCallingConvention candidateCallConv))
            return false;
        if (candidateCallConv != callConv)
            return false;
        if (candidateRetAndArgs.Length != retAndArgTypes.Length)
            return false;
        for (int i = 0; i < candidateRetAndArgs.Length; i++)
        {
            if (candidateRetAndArgs[i].Address != retAndArgTypes[i].Address)
                return false;
        }
        return true;
    }

    private bool IsLoaded(ITypeHandle typeHandle)
    {
        if (typeHandle.Address == TargetPointer.Null)
            return false;
        if (typeHandle.IsTypeDesc())
        {
            uint typeAndFlags = _target.Read<uint>(typeHandle.TypeDescAddress() + /* TypeDesc::TypeAndFlags offset */);
            return (typeAndFlags & (uint)TypeDescFlags.IsNotFullyLoaded) == 0;
        }

        MethodTable methodTable = _methodTables[typeHandle.Address];
        uint flags = _target.Read<uint>(methodTable.AuxiliaryData + /* AuxiliaryData::Flags offset */);
        return (flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0;
    }

    ITypeHandle? GetConstructedType(ITypeHandle? typeHandle, CorElementType corElementType, int rank, ImmutableArray<ITypeHandle?> typeArguments, SignatureCallingConvention callConv)
    {
        // For function pointers the type handle arg is unused - type information is provided in the type arguments.
        if (corElementType != CorElementType.FnPtr && typeHandle is null)
            return null;
        ILoader loaderContract = _target.Contracts.Loader;
        TargetPointer loaderModule = // see [link](https://github.com/dotnet/runtime/blob/e1979b72ccb5f916649f1d9949ef663254790c25/src/coreclr/vm/clsload.cpp#L78)
        ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule);
        ITypeHandle? potentialMatch = null;
        foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle))
        {
            potentialMatch = GetTypeHandle(ptr);
            if (corElementType == CorElementType.GenericInst)
            {
                if (GenericInstantiationMatch(typeHandle, potentialMatch, typeArguments) && IsLoaded(potentialMatch))
                {
                    return potentialMatch;
                }
            }
            else if (corElementType == CorElementType.FnPtr)
            {
                if (FnPtrMatch(potentialMatch, typeArguments, callConv) && IsLoaded(potentialMatch))
                {
                    return potentialMatch;
                }
            }
            else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch))
            {
                return potentialMatch;
            }
        }
        return null;
    }

    public ITypeHandle GetPrimitiveType(CorElementType typeCode)
    {
        TargetPointer coreLib = _target.ReadGlobalPointer("CoreLib");
        TargetPointer classes = _target.ReadPointer(coreLib + /* CoreLibBinder::Classes offset */);
        TargetPointer typeHandlePtr = _target.ReadPointer(classes + (ulong)typeCode * (ulong)_target.PointerSize);
        return GetTypeHandle(typeHandlePtr);
    }

    public bool IsGenericVariable(ITypeHandle typeHandle, out TargetPointer module, out uint token, out uint index)
    {
        module = TargetPointer.Null;
        token = 0;
        index = 0;

        if (!typeHandle.IsTypeDesc())
            return false;

        int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
        CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);

        switch (elemType)
        {
            case CorElementType.MVar:
            case CorElementType.Var:
                module = // Read Module field from TypeVarTypeDesc contract using address typeHandle.TypeDescAddress()
                token = // Read Token field from TypeVarTypeDesc contract using address typeHandle.TypeDescAddress()
                index = // Read Index field from TypeVarTypeDesc contract using address typeHandle.TypeDescAddress()
                return true;
        }
        return false;
    }

    public bool IsFunctionPointer(ITypeHandle typeHandle, out ReadOnlySpan<ITypeHandle> retAndArgTypes, out SignatureCallingConvention callConv)
    {
        retAndArgTypes = default;
        callConv = default;

        if (!typeHandle.IsTypeDesc())
            return false;

        int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
        CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);

        if (elemType != CorElementType.FnPtr)
            return false;

        int NumArgs = // Read NumArgs field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress()
        TargetPointer RetAndArgTypes = // Read NumArgs field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress()

        ITypeHandle[] retAndArgTypesArray = new ITypeHandle[NumTypeArgs + 1];
        for (int i = 0; i <= NumTypeArgs; i++)
            retAndArgTypesArray[i] = GetTypeHandle(_target.ReadPointer(RetAndArgTypes + _target.PointerSize * i));

        retAndArgTypes = retAndArgTypesArray;
        callConv = (byte) // Read CallConv field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress(), and then ignore all but the low 8 bits.
        return true;
    }

    public bool IsPointer(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsTypeDesc())
            return false;

        int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
        CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);
        return elemType == CorElementType.Ptr;
    }

    public bool IsTypeDesc(ITypeHandle typeHandle) => typeHandle.IsTypeDesc();

    public TargetPointer GetLoaderModule(ITypeHandle typeHandle)
    {
        if (typeHandle.IsTypeDesc())
        {
            // TypeDesc::GetLoaderModule
            if (HasTypeParam(typeHandle))
            {
                return GetLoaderModule(GetTypeParam(typeHandle));
            }
            else if (IsGenericVariable(typeHandle, out TargetPointer genericParamModule, out _))
            {
                return genericParamModule;
            }
            else
            {
                Debug.Assert(IsFunctionPointer(typeHandle, out _, out _));
                return target.ReadPointer(typeHandle.TypeDescAddress() + /* FnPtrTypeDesc::LoaderModule offset */);
            }
        }
        return target.ReadPointer(mt.AuxiliaryData + /* MethodTableAuxiliaryData::LoaderModule offset */);
    }

    public TypedByRefInfo GetTypedByRefInfo(TargetPointer typedByRef)
    {
        // Read both fields of a TypedByRef using the TypedByRef data descriptor.
        TargetPointer data = target.ReadPointer(typedByRef + /* TypedByRef::Data offset */);
        TargetPointer typeHandle = target.ReadPointer(typedByRef + /* TypedByRef::Type offset */);
        return new TypedByRefInfo(data, typeHandle);
    }
```

### MethodDesc

The `MethodDesc` APIs of RuntimeTypeSystem version 1 depend on the following globals:

| Global name | Meaning |
| --- | --- |
| `MethodDescAlignment` | `MethodDescChunk` trailing data is allocated in multiples of this constant.  The size (in bytes) of each `MethodDesc` (or subclass) instance is a multiple of this constant. |
| `MethodDescTokenRemainderBitCount` | Number of bits in the token remainder in `MethodDesc` |


In the runtime a `MethodDesc` implicitly belongs to a single `MethodDescChunk` and some common data is shared between method descriptors that belong to the same chunk.  A single method table
will typically have multiple chunks.  There are subkinds of MethodDescs at runtime of varying sizes (but the sizes must be mutliples of `MethodDescAlignment`) and each chunk contains method descriptors of the same size.

We depend on the following data descriptors:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `MethodDesc` | `ChunkIndex` | Offset of this `MethodDesc` relative to the end of its containing `MethodDescChunk` - in multiples of `MethodDescAlignment` |
| `MethodDesc` | `Slot` | The method's slot |
| `MethodDesc` | `Flags` | The method's flags |
| `MethodDesc` | `Flags3AndTokenRemainder` | More flags for the method, and the low bits of the method's token's RID |
| `MethodDesc` | `GCCoverageInfo` | The method's GCCover debug info, if supported |
| `MethodDescCodeData` | `VersioningState` | The IL versioning state associated with a method descriptor
| `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to |
| `MethodDescChunk` | `Next` | The next chunk of methods |
| `MethodDescChunk` | `Size` | The size of this `MethodDescChunk`  following this `MethodDescChunk` header, minus 1. In multiples of `MethodDescAlignment` |
| `MethodDescChunk` | `Count` | The number of `MethodDesc` entries in this chunk, minus 1. |
| `MethodDescChunk` | `FlagsAndTokenRange` | `MethodDescChunk` flags, and the upper bits of the method token's RID |
| `MethodTable` | `AuxiliaryData` | Auxiliary data associated with the method table. See `MethodTableAuxiliaryData` |
| `MethodTableAuxiliaryData` | `LoaderModule` | The loader module associated with a method table
| `MethodTableAuxiliaryData` | `OffsetToNonVirtualSlots` | Offset from the auxiliary data address to the array of non-virtual slots |
| `InstantiatedMethodDesc` | `PerInstInfo` | The pointer to the method's type arguments |
| `InstantiatedMethodDesc` | `Flags2` | Flags for the `InstantiatedMethodDesc` |
| `InstantiatedMethodDesc` | `NumGenericArgs` | How many generic args the method has |
| `StoredSigMethodDesc` | `Sig` | Pointer to a metadata signature |
| `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature |
| `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` |
| `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc |
| `AsyncMethodData` | `Flags` | Async method flags |
| `AsyncMethodData` | `Signature` | The async variant's signature (see `Signature`) |
| `Signature` | `SignaturePointer` | Pointer to the raw signature blob |
| `Signature` | `SignatureLength` | Count of bytes in the raw signature blob |
| `GCCoverageInfo` | `SavedCode` | Pointer to the GCCover saved code copy, if supported |

The following data descriptor types are used only for their sizes when computing the total size of a `MethodDesc` instance.
The size of a `MethodDesc` is the base size of its classification subtype plus the sizes of any optional trailing slots indicated by its flags:

| Data Descriptor Name | Meaning |
| --- | --- |
| `MethodDesc` | Base size for `mcIL` classification |
| `FCallMethodDesc` | Base size for `mcFCall` classification |
| `PInvokeMethodDesc` | Base size for `mcPInvoke` classification |
| `EEImplMethodDesc` | Base size for `mcEEImpl` classification |
| `ArrayMethodDesc` | Base size for `mcArray` classification |
| `InstantiatedMethodDesc` | Base size for `mcInstantiated` classification |
| `CLRToCOMCallMethodDesc` | Base size for `mcComInterOp` classification |
| `DynamicMethodDesc` | Base size for `mcDynamic` classification |
| `NonVtableSlot` | Size of the non-vtable slot, added when `HasNonVtableSlot` flag is set |
| `MethodImpl` | Size of the MethodImpl data, added when `HasMethodImpl` flag is set |
| `NativeCodeSlot` | Size of the native code slot, added when `HasNativeCodeSlot` flag is set |
| `AsyncMethodData` | Size of the async method data, added when `HasAsyncMethodData` flag is set |


The contract depends on the following other contracts

| Contract |
| --- |
| CodeVersions |
| Loader |
| PlatformMetadata |
| ReJIT |
| ExecutionManager |
| PrecodeStubs |

And the following enumeration definitions

```csharp
    internal enum MethodDescClassification
    {
        IL = 0, // IL
        FCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor)
        PInvoke = 2, // PInvoke method
        EEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke)
        Array = 4, // Array ECall
        Instantiated = 5, // Instantiated generic methods, including descriptors
                            // for both shared and unshared code (see InstantiatedMethodDesc)
        ComInterop = 6,
        Dynamic = 7, // for method desc with no metadata behind
    }

    [Flags]
    internal enum MethodDescFlags : ushort
    {
        ClassificationMask = 0x7,
        #region Additonal pointers
        // The below flags each imply that there's an extra pointer-sized piece of data after the MethodDesc in the MethodDescChunk
        HasNonVtableSlot = 0x0008,
        HasMethodImpl = 0x0010,
        HasNativeCodeSlot = 0x0020,
        HasAsyncMethodData = 0x0040,
        Static = 0x0080,
        // Mask for the above flags
        MethodDescAdditionalPointersMask = 0x0038,
        #endredion Additional pointers
    }

    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_1 : uint
    {
        None = 0,
        AsyncCall = 0x1,
        IsAsyncVariant = 0x4,
        Thunk = 0x10,
        ReturnDroppingThunk = 0x20,
    }

    [Flags]
    internal enum ILStubType : uint
    {
        StubPInvokeVarArg = 0x4,
        StubCLRToCOMInterop = 0x6,
    }

    [Flags]
    internal enum MethodDescFlags3 : ushort
    {
        // HasPrecode implies that HasStableEntryPoint is set.
        HasStableEntryPoint = 0x1000, // The method entrypoint is stable (either precode or actual code)
        HasPrecode = 0x2000, // Precode has been allocated for this method
        IsUnboxingStub = 0x4000,
        IsEligibleForTieredCompilation = 0x8000,
    }

    [Flags]
    internal enum MethodDescEntryPointFlags : byte
    {
        TemporaryEntryPointAssigned = 0x04,
    }

    internal enum MethodTableAuxiliaryFlags : uint
    {
        Initialized = 0x0001,
        IsInitError = 0x0100,
        IsNotFullyLoaded = 0x0040,
    }

    internal enum TypeDescFlags : uint
    {
        IsNotFullyLoaded = 0x00001000,
    }

```

Internal to the contract in order to answer queries about method descriptors,
we collect the information in a `MethodDesc` struct:

```csharp
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;
    }

    public MethodClassification Classification => (MethodClassification)(_desc.Flags & MethodDescFlags.ClassificationMask);
    public bool IsIL => Classification == MethodClassification.IL || Classification == MethodClassification.Instantiated;

    public TargetPointer MethodTable => _chunk.MethodTable;

    public ushort Slot => _desc.Slot;


    internal bool HasFlags(MethodDescChunkFlags flags) => (_chunk.FlagsAndTokenRange & (ushort)flags) != 0;
    internal bool HasFlags(MethodDescFlags flags) => (_desc.Flags & (ushort)flags) != 0;
    internal bool HasFlags(MethodDescFlags3 flags) => (_desc.Flags3AndTokenRemainder & (ushort)flags) != 0;

    public bool IsLoaderModuleAttachedToChunk => HasFlags(MethodDescChunkFlags.LoaderModuleAttachedToChunk);

    public ulong SizeOfChunk
    {
        get
        {
            ulong typeSize = _target.GetTypeInfo(DataType.MethodDescChunk).Size;
            ulong chunkSize = (ulong)(_chunk.Size + 1) * _target.ReadGlobal<ulong>("MethodDescAlignment");
            ulong extra = IsLoaderModuleAttachedToChunk ? (ulong)_target.PointerSize : 0;
            return typeSize + chunkSize + extra;
        }
    }

    public bool IsEligibleForTieredCompilation => HasFlags(MethodDescFlags3.IsEligibleForTieredCompilation);

    // non-vtable slot, native code slot and MethodImpl slots are stored after the MethodDesc itself, packed tightly
    // in the order: [non-vtable; methhod impl; native code].
    internal int NonVtableSlotIndex => HasNonVtableSlot ? 0 : throw new InvalidOperationException();
    internal int MethodImplIndex => HasMethodImpl ? /* 0 or 1 */ : throw new InvalidOperationException();
    internal int NativeCodeSlotIndex => HasNativeCodeSlot ? /* 0, 1 or 2 */ : throw new InvalidOperationException();

    internal bool HasNativeCodeSlot => HasFlags(MethodDescFlags.HasNativeCodeSlot);
    internal bool HasNonVtableSlot => HasFlags(MethodDescFlags.HasNonVtableSlot);
    internal bool HasMethodImpl => HasFlags(MethodDescFlags.HasMethodImpl);

    internal bool HasStableEntryPoint => HasFlags(MethodDescFlags3.HasStableEntryPoint);
    internal bool HasPrecode => HasFlags(MethodDescFlags3.HasPrecode);

}
```

Method descriptor handles are instantiated by caching the relevant data in a `_methodDescs` dictionary:

```csharp
    public MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer)
    {
        // Validate that methodDescPointer points at a MethodDesc
        // Get the corresponding MethodDescChunk pointer
        // Load the relevant Data.MethodDesc and Data.MethodDescChunk structures
        // and caching the results in _methodDescs
        return new MethodDescHandle() { Address = methodDescPointer };
    }
```

And the various apis are implemented with the following algorithms

```csharp
    public TargetPointer GetMethodTable(MethodDescHandle methodDescHandle)
    {
        return _methodDescs[methodDescHandle.Address].MethodTable;
    }

    public bool IsGenericMethodDefinition(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        if (methodDesc.Classification != MethodDescClassification.Instantiated)
            return false;

        ushort Flags2 = // Read Flags2 field from InstantiatedMethodDesc contract using address methodDescHandle.Address

        return ((int)Flags2 & (int)InstantiatedMethodDescFlags2.KindMask) == (int)InstantiatedMethodDescFlags2.GenericMethodDefinition;
    }

    public ReadOnlySpan<ITypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        if (methodDesc.Classification != MethodDescClassification.Instantiated)
            return default;

        TargetPointer dictionaryPointer = // Read PerInstInfo field from InstantiatedMethodDesc contract using address methodDescHandle.Address
        if (dictionaryPointer == 0)
            return default;

        int NumTypeArgs = // Read NumGenericArgs from methodDescHandle.Address using InstantiatedMethodDesc contract
        ITypeHandle[] instantiation = new ITypeHandle[NumTypeArgs];
        for (int i = 0; i < NumTypeArgs; i++)
            instantiation[i] = GetTypeHandle(_target.ReadPointer(dictionaryPointer + _target.PointerSize * i));

        return instantiation;
    }

    public uint GetMethodToken(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        TargetPointer methodDescChunk = // Using ChunkIndex from methodDesc, compute the wrapping MethodDescChunk

        ushort Flags3AndTokenRemainder = // Read Flags3AndTokenRemainder field from MethodDesc contract using address methodDescHandle.Address

        ushort FlagsAndTokenRange = // Read FlagsAndTokenRange field from MethodDescChunk contract using address methodDescChunk

        int tokenRemainderBitCount = _target.ReadGlobal<byte>("MethodDescTokenRemainderBitCount");
        int tokenRangeBitCount = 24 - tokenRemainderBitCount;
        uint allRidBitsSet = 0xFFFFFF;
        uint tokenRemainderMask = allRidBitsSet >> tokenRangeBitCount;
        uint tokenRangeMask = allRidBitsSet >> tokenRemainderBitCount;

        uint tokenRemainder = (uint)(_desc.Flags3AndTokenRemainder & tokenRemainderMask);
        uint tokenRange = ((uint)(_chunk.FlagsAndTokenRange & tokenRangeMask)) << tokenRemainderBitCount;

        return 0x06000000 | tokenRange | tokenRemainder;
    }

    public bool TryGetMethodSignature(MethodDescHandle methodDescHandle, out ReadOnlySpan<byte> signature)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        // Dynamic, EEImpl and Array methods store their signature directly on the MethodDesc.
        MethodClassification classification = (MethodClassification)(methodDesc.Flags & MethodDescFlags.ClassificationMask);
        if (classification is MethodClassification.Dynamic or MethodClassification.EEImpl or MethodClassification.Array)
        {
            signature = // StoredSigMethodDesc.Signature blob for methodDesc
            return true;
        }

        // Async variant methods carry their signature in the AsyncMethodData.
        if (HasFlag(methodDesc, MethodDescFlags.HasAsyncMethodData))
        {
            AsyncMethodData asyncData = // Read AsyncMethodData for methodDesc
            if (((AsyncMethodFlags)asyncData.Flags).HasFlag(AsyncMethodFlags.IsAsyncVariant))
            {
                signature = // Read asyncData.Signature.SignatureLength bytes from asyncData.Signature.SignaturePointer
                return true;
            }
        }

        // Otherwise resolve the signature from ECMA metadata using the MethodDef token.
        uint token = GetMethodToken(methodDescHandle);
        if (EcmaMetadataUtils.GetRowId(token) == 0)
        {
            signature = default;
            return false;
        }

        MetadataReader? mdReader = // Get metadata reader for the method's module, or null if unavailable
        if (mdReader is null)
        {
            signature = default;
            return false;
        }

        MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(token));
        MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle);
        signature = mdReader.GetBlobBytes(methodDef.Signature);
        return true;
    }

    public uint GetMethodDescSize(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        // Compute the size from data descriptor type sizes.
        // The base size comes from the classification subtype, and optional slot
        // sizes are added based on the MethodDesc flags.
        MethodClassification classification = (MethodClassification)(methodDesc.Flags & MethodDescFlags.ClassificationMask);
        uint size = classification switch
        {
            MethodClassification.IL => target.GetTypeInfo(DataType.MethodDesc).Size,
            MethodClassification.FCall => target.GetTypeInfo(DataType.FCallMethodDesc).Size,
            MethodClassification.PInvoke => target.GetTypeInfo(DataType.PInvokeMethodDesc).Size,
            MethodClassification.EEImpl => target.GetTypeInfo(DataType.EEImplMethodDesc).Size,
            MethodClassification.Array => target.GetTypeInfo(DataType.ArrayMethodDesc).Size,
            MethodClassification.Instantiated => target.GetTypeInfo(DataType.InstantiatedMethodDesc).Size,
            MethodClassification.ComInterop => target.GetTypeInfo(DataType.CLRToCOMCallMethodDesc).Size,
            MethodClassification.Dynamic => target.GetTypeInfo(DataType.DynamicMethodDesc).Size,
        };

        if (HasFlag(methodDesc, MethodDescFlags.HasNonVtableSlot))
            size += target.GetTypeInfo(DataType.NonVtableSlot).Size;
        if (HasFlag(methodDesc, MethodDescFlags.HasMethodImpl))
            size += target.GetTypeInfo(DataType.MethodImpl).Size;
        if (HasFlag(methodDesc, MethodDescFlags.HasNativeCodeSlot))
            size += target.GetTypeInfo(DataType.NativeCodeSlot).Size;
        if (HasFlag(methodDesc, MethodDescFlags.HasAsyncMethodData))
            size += target.GetTypeInfo(DataType.AsyncMethodData).Size;

        return size;
    }

    public bool IsArrayMethod(MethodDescHandle methodDescHandle, out ArrayFunctionType functionType)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        if (methodDesc.Classification != MethodDescClassification.Array)
        {
            functionType = default;
            return false;
        }

        int arrayMethodIndex = methodDesc.Slot - GetNumVtableSlots(GetTypeHandle(methodDesc.MethodTable));

        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 ReadOnlySpan<byte> methodName)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        if (methodDesc.Classification != MethodDescClassification.Dynamic)
        {
            methodName = default;
            return false;
        }

        TargetPointer methodNamePointer = // Read MethodName field from DynamicMethodDesc contract using address methodDescHandle.Address

        methodName = // ReadBuffer from target of a utf8 null terminated string, starting at address methodNamePointer
        return true;
    }

    public bool IsDynamicMethod(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        if (methodDesc.Classification != MethodDescClassification.Dynamic)
        {
            return false;
        }

        uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address

        return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod);
    }

    public bool IsILStub(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        if (methodDesc.Classification != MethodDescClassification.Dynamic)
        {
            return false;
        }

        uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address

        return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsILStub);
    }

    public bool IsIL(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
        return methodDesc.Classification == MethodClassification.IL || methodDesc.Classification == MethodClassification.Instantiated;
    }

    public bool HasMDContextArg(MethodDescHandle method)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

        if (methodDesc.Classification != MethodDescClassification.Dynamic)
        {
            return false;
        }

        uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address
        ILStubType ilStubType = (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask);
        bool isCLRToCOMStub = ilStubType == ILStubType.StubCLRToCOMInterop;
        bool isPInvokeVarArgStub = ilStubType == ILStubType.StubPInvokeVarArg;

        return isCLRToCOMStub || isPInvokeVarArgStub;
    }

    public ushort GetSlotNumber(MethodDescHandle methodDesc) => _methodDescs[methodDesc.Addres]._desc.Slot;
```

Determining if a method is in a collectible module:

```csharp
    private TargetPointer GetLoaderModule(MethodDesc md)
    {
        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));
            ITypeHandle mt = GetTypeHandle(mtAddr);
            return GetLoaderModule(mt);
        }
    }

    private TargetPointer GetLoaderModule(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
        {
            // FIXME[cdac]: TypeDesc::GetLoaderModule()
        }
        else
        {
            MethodTable mt = _methodTables[typeHandle.Address];
            Data.MethodTableAuxiliaryData mtAuxData = /* get the AuxiliaryData from the Method Table*/;
            return mtAuxData.LoaderModule;
        }
    }

    public bool 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);

    }
```

Determining if a method supports multiple code versions:

```csharp
    private bool IsWrapperStub(MethodDesc md)
    {
        return md.IsUnboxingStub || IsInstantiatingStub(md);
    }

    private bool IsInstantiatingStub(MethodDesc md)
    {
        return md.Classification == MethodClassification.Instantiated && !md.IsUnboxingStub && IsWrapperStubWithInstantiations(md);
    }

    private bool IsWrapperStubWithInstantiations(MethodDesc methodDesc)
    {
        return /*Flags2 of InstantiatedMethodDesc at methodDesc.Address has WrapperStubWithInstantiations set*/;
    }

    public bool 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;
    }
```

Reading a method's Runtime Async flags:

```csharp
    public AsyncMethodFlags GetAsyncMethodFlags(MethodDescHandle methodDescHandle)
    {
        MethodDesc md = _methodDescs[methodDescHandle.Address];
        if (!md.HasAsyncMethodData)
            return AsyncMethodFlags.None;

        AsyncMethodFlags_1 raw = (AsyncMethodFlags_1)/* AsyncMethodData.Flags */;
        AsyncMethodFlags result = AsyncMethodFlags.None;
        if ((raw & AsyncMethodFlags_1.AsyncCall) != 0)
            result |= AsyncMethodFlags.AsyncCall;
        if ((raw & AsyncMethodFlags_1.IsAsyncVariant) != 0)
            result |= AsyncMethodFlags.IsAsyncVariant;
        if ((raw & AsyncMethodFlags_1.Thunk) != 0)
            result |= AsyncMethodFlags.Thunk;
        if ((raw & AsyncMethodFlags_1.ReturnDroppingThunk) != 0)
            result |= AsyncMethodFlags.ReturnDroppingThunk;
        return result;
    }
```

Resolving the loaded async variant of an async thunk method:

```csharp
    public TargetPointer GetAsyncVariant(MethodDescHandle methodDescHandle)
    {
        MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
        ITypeHandle methodTable = GetTypeHandle(methodDesc.MethodTable);
        ITypeHandle canonicalMethodTable = GetTypeHandle(GetCanonicalMethodTable(methodTable));

        foreach (MethodDescHandle candidateHandle in GetIntroducedMethods(canonicalMethodTable))
        {
            MethodDesc candidate = _methodDescs[candidateHandle.Address];
            if (candidate.Slot != methodDesc.Slot)
                continue;

            AsyncMethodFlags flags = GetAsyncMethodFlags(candidateHandle);
            if (flags.HasFlag(AsyncMethodFlags.IsAsyncVariant) &&
                !flags.HasFlag(AsyncMethodFlags.ReturnDroppingThunk))
            {
                return candidateHandle.Address;
            }
        }

        return TargetPointer.Null;
    }
```

Determining where a shared generic method obtains its generic context:

```csharp
    public GenericContextLoc GetGenericContextLoc(MethodDescHandle methodDescHandle)
    {
        MethodDesc md = _methodDescs[methodDescHandle.Address];
        if (!IsSharedByGenericInstantiations(md))
            return GenericContextLoc.None;
        else if (HasMethodInstantiation(md))
            return GenericContextLoc.InstArgMethodDesc;
        else if (RequiresInstArgMethodTable(md))
            return GenericContextLoc.InstArgMethodTable;
        else
            return GenericContextLoc.ThisPtr;
    }

    private bool RequiresInstArgMethodTable(MethodDesc md)
    {
        if (md.IsStatic)
            return true;

        MethodTable mt = _methodTables[md.MethodTable];
        if (mt.Flags.IsValueType)
            return true;

        if (mt.Flags.IsInterface && !IsAbstract(md) /* checked from md classification and metadata attributes */)
            return true;

        return false;
    }

    private bool IsSharedByGenericInstantiations(MethodDesc md)
    {
        if (md.Classification == MethodClassification.Instantiated)
        {
            InstantiatedMethodDesc imd = AsInstantiatedMethodDesc(md);
            if (imd.IsWrapperStubWithInstantiations)
                return false;
            if (/* Flags2 of InstantiatedMethodDesc has SharedMethodInstantiation set */)
                return true;
        }
        MethodTable mt = _methodTables[md.MethodTable];
        return mt.Flags.IsSharedByGenericInstantiations;
    }
```

Determining if a method uses the async calling convention:

```csharp
    public bool IsAsyncMethod(MethodDescHandle methodDescHandle)
    {
        MethodDesc md = _methodDescs[methodDescHandle.Address];
        if (!md.HasAsyncMethodData)
            return false;

        Data.AsyncMethodData asyncData = // Read AsyncMethodData from the async method data optional slot
        return ((AsyncMethodFlags)asyncData.Flags).HasFlag(AsyncMethodFlags.AsyncCall);
    }
```

Determining if a method is a wrapper stub (unboxing or instantiating):

```csharp
    public bool IsWrapperStub(MethodDescHandle methodDescHandle)
    {
        MethodDesc md = _methodDescs[methodDescHandle.Address];
        return md.IsUnboxingStub || IsInstantiatingStub(md);
    }
```

Determining if a method is an unboxing stub. An unboxing stub is a wrapper
around a value-type instance method whose `this` is a boxed object: the
stub unboxes `this` and forwards to the real instance method. The bit is
stored in `MethodDescFlags3` and surfaces as the `IsUnboxingStub` flag on
`MethodDesc`:

```csharp
    public bool IsUnboxingStub(MethodDescHandle methodDescHandle)
        => _methodDescs[methodDescHandle.Address].IsUnboxingStub;
```

Determining if a method uses the vararg calling convention:

```csharp
    public bool IsVarArg(MethodDescHandle methodDescHandle)
    {
        ReadOnlySpan<byte> signature = IsStoredSigMethodDesc(methodDescHandle)
            ? GetStoredSignature(methodDescHandle)
            : GetMetadataSignature(methodDescHandle);

        return !signature.IsEmpty
            && (SignatureCallingConvention)(signature[0] & 0x0F) == SignatureCallingConvention.VarArgs;
    }
```

Extracting a pointer to the `MethodDescVersioningState` data for a given method

```csharp
    public TargetPointer GetMethodDescVersioningState(MethodDescHandle methodDesc)
    {
        MethodDesc md = _methodDescs[methodDesc.Address];
        TargetPointer codeDataAddress = md._desc.CodeData;
        return /* VersioningState field of MethodDescCodeData at codeDataAddress */;
    }
```

Checking if a method has a native code slot and getting its address

```csharp
    public bool HasNativeCodeSlot(MethodDescHandle methodDesc) =>_methodDescs[methodDesc.Address].HasNativeCodeSlot;

    uint GetMethodClassificationBaseSize (MethodClassification classification)
    => classification switch
    {
        MethodClassification.IL => /*size of MethodDesc*/,
        MethodClassification.FCall => /* size of FCallMethodDesc */
        MethodClassification.PInvoke => /* size of PInvokeMethodDesc */
        MethodClassification.EEImpl => /* size of EEImplMethodDesc */
        MethodClassification.Array => /* size of ArrayMethodDesc */
        MethodClassification.Instantiated => /* size of InstantiatedMethodDesc */
        MethodClassification.ComInterop => /* size of CLRToCOMCallMethodDesc */
        MethodClassification.Dynamic => /* size of DynamicMethodDesc */
    };

    private uint MethodDescAdditionalPointersOffset(MethodDesc md) => GetMethodClassificationBaseSize(md.Classification);

    public TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc)
    {
        MethodDesc md = _methodDescs[methodDesc.Address];
        uint offset = MethodDescAdditionalPointersOffset(md);
        offset += (uint)(_target.PointerSize * md.NativeCodeSlotIndex);
        return methodDesc.Address + offset;
    }
```

Getting the native code pointer for methods with a NativeCodeSlot or a stable entry point

```csharp
    private TargetCodePointer GetStableEntryPoint(TargetPointer methodDescAddress, MethodDesc md)
    {
        return GetMethodEntryPointIfExists(methodDescAddress, md);
    }

    private TargetCodePointer GetMethodEntryPointIfExists(TargetPointer methodDescAddress, MethodDesc md)
    {
        if (md.HasNonVtableSlot)
        {
            TargetPointer pSlot = GetAddressOfNonVtableSlot(methodDescAddress, md);

            return _target.ReadCodePointer(pSlot);
        }

        TargetPointer methodTablePointer = md.MethodTable;
        ITypeHandle typeHandle = GetTypeHandle(methodTablePointer);
        TargetPointer addrOfSlot = GetAddressOfSlot(typeHandle, md.Slot);
        return _target.ReadCodePointer(addrOfSlot);
    }

    private TargetPointer GetAddressOfNonVtableSlot(TargetPointer methodDescPointer, MethodDesc md)
    {
        uint offset = MethodDescAdditionalPointersOffset(md);
        offset += (uint)(_target.PointerSize * md.NonVtableSlotIndex);
        return methodDescPointer.Value + offset;
    }

    private TargetPointer GetAddressOfSlot(ITypeHandle typeHandle, uint slotNum)
    {
        if (!typeHandle.IsMethodTable())
            throw new InvalidOperationException("typeHandle is not a MethodTable");
        MethodTable mt = _methodTables[typeHandle.Address];

        if (slotNum < mt.NumVirtuals)
        {
            // Virtual slots live in chunks pointed to by vtable indirections
            return GetVTableIndirectionsAddressOfSlot(typeHandle.Address, slotNum);
        }
        else
        {
            // Non-virtual slots < GetNumVtableSlots live before the MethodTableAuxiliaryData. The array grows backwards
            TargetPointer auxDataPtr = _target.ReadPointer(typeHandle.Address + /* MethodTable::AuxiliaryData offset */);
            TargetPointer nonVirtualSlotsArray = auxDataPtr + _target.Read<short>(/* MethodTableAuxiliaryData::OffsetToNonVirtualSlots offset */);
            return nonVirtualSlotsArray - (1 + (slotNum - mt.NumVirtuals));
        }

    }

    private TargetPointer GetVTableIndirectionsAddressOfSlot (TargetPointer methodTablePointer, uint slot)
    {
        private const int NumPointersPerIndirection = 8;
        private const int NumPointersPerIndirectionLog2 = 3;
        TargetPointer indirectionPointer = methodTablePointer + /*size of MethodTable*/ + (ulong)(slotNum >> NumPointersPerIndirectionLog2) * (ulong)_target.PointerSize;
        TargetPointer slotsStart = _target.ReadPointer(indirectionPointer);
        return slotsStart + (ulong)(slotNum & (NumPointersPerIndirection - 1)) * (ulong)_target.PointerSize;
    }

    public TargetCodePointer GetNativeCode(MethodDescHandle methodDescHandle)
    {
        MethodDesc md = _methodDescs[methodDescHandle.Address];
        if (md.HasNativeCodeSlot)
        {
            TargetPointer ppCode = GetAddressOfNativeCodeSlot(methodDescHandle);
            return _target.ReadCodePointer(ppCode);
        }

        if (!md.HasStableEntryPoint || md.HasPrecode)
            return TargetCodePointer.Null;

        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(ITypeHandle 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
```csharp
    // Based on MethodTable::IntroducedMethodIterator
    private IEnumerable<MethodDescHandle> GetIntroducedMethods(ITypeHandle typeHandle)
    {
        // typeHandle must represent a MethodTable

        EEClass eeClass = GetClassData(typeHandle);

        // pointer to the first MethodDescChunk
        TargetPointer chunkAddr = eeClass.MethodDescChunk;
        while (chunkAddr != TargetPointer.Null)
        {
            MethodDescChunk chunk = // read Data.MethodDescChunk data from chunkAddr
            TargetPointer methodDescPtr = chunk.FirstMethodDesc;

            // chunk.Count is the number of MethodDescs in the chunk - 1
            // add 1 to get the actual number of MethodDescs within the chunk
            for (int i = 0; i < chunk.Count + 1; i++)
            {
                MethodDescHandle methodDescHandle = GetMethodDescHandle(methodDescPtr);

                // increment pointer to the beginning of the next MethodDesc
                methodDescPtr += GetMethodDescSize(methodDescHandle);
                yield return methodDescHandle;
            }

            // go to the next chunk
            chunkAddr = chunk.Next;
        }
    }

    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 get the MethodDesc from it
        {
            TargetPointer methodDescPtr = _target.Contracts.PrecodeStubs.GetMethodDescFromStubAddress(pCode);
            return methodDescPtr;
        }
    }

    public IEnumerable<TargetPointer> GetIntroducedMethodDescs(ITypeHandle typeHandle)
    {
        if (!typeHandle.IsMethodTable())
            throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");

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

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

        TargetPointer cannonMTPTr = GetCanonicalMethodTable(typeHandle);
        ITypeHandle 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.
                ITypeHandle 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);
    }
```

### FieldDesc

The FieldDesc APIs of RuntimeTypeSystem version 1 depend on the following globals:

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

The FieldDesc APIs of RuntimeTypeSystem version 1 depend on the following data descriptors:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `FieldDesc` | `MTOfEnclosingClass` | Pointer to method table of enclosing class |
| `FieldDesc` | `DWord1` | The FD's flags and token |
| `FieldDesc` | `DWord2` | The FD's kind and offset |

```csharp
internal enum FieldDescFlags1 : uint
{
    TokenMask = 0xffffff,
    IsStatic = 0x1000000,
    IsThreadStatic = 0x2000000,
    IsRVA = 0x4000000,
}

internal enum FieldDescFlags2 : uint
{
    TypeMask = 0xf8000000,
    OffsetMask = 0x07ffffff,
}

TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer)
{
    return target.ReadPointer(fieldDescPointer + /* FieldDesc::MTOfEnclosingClass offset */);
}

uint GetFieldDescMemberDef(TargetPointer fieldDescPointer)
{
    uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
    return EcmaMetadataUtils.CreateFieldDef(DWord1 & (uint)FieldDescFlags1.TokenMask);
}

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

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

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

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

uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition? fieldDef)
{
    uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
    // DWord2 packs the 27-bit offset (low bits) with the 5-bit field type (high bits), so the offset
    // must be masked out before comparing against the big-RVA sentinel. The native runtime compares the
    // FieldDesc's m_dwOffset bitfield directly (see FieldDesc::GetOffset in src/coreclr/vm/field.h).
    uint offset = DWord2 & (uint)FieldDescFlags2.OffsetMask;
    if (offset == _target.ReadGlobal<uint>("FieldOffsetBigRVA"))
    {
        if (fieldDef is null)
            throw new ArgumentNullException(nameof(fieldDef), "Field definition is required for big RVA fields");
        return (uint)fieldDef.Value.GetRelativeVirtualAddress();
    }
    return offset;
}

TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer, bool unboxValueTypes = true)
{
    // Resolves the base pointer (GC or non-GC statics) for the enclosing type,
    // then applies the field's metadata-based offset within that region.
    // Uses GetGCStaticsBasePointer / GetNonGCStaticsBasePointer depending on the field's CorElementType.
    // When unboxValueTypes is true (default), value-type statics are dereferenced to the unboxed
    // payload inside the boxed object. Pass false to obtain the address of the boxed reference itself.
}

TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread, bool unboxValueTypes = true)
{
    // Like GetFieldDescStaticAddress, but resolves thread-local base pointers instead.
    // Uses GetGCThreadStaticsBasePointer / GetNonGCThreadStaticsBasePointer.
    // The unboxValueTypes parameter behaves the same as in GetFieldDescStaticAddress.
}

ITypeHandle? GetFieldDescApproxTypeHandle(TargetPointer fieldDescPointer)
{
    // Resolve enclosing MT -> Module -> MetadataReader, decode the field's
    // signature using the SignatureDecoder contract with a SignatureTypeProvider
    // bound to the enclosing class as generic context, and return the resulting
    // TypeHandle. Returns null if any link in the chain is unavailable
    // (e.g. uncached constructed instantiation).
}

bool TryGetFieldDescNext(TargetPointer fieldDescPointer, out TargetPointer nextFieldDesc)
{
    // The FieldDescs of a type form a contiguous array with no terminator. Advance one FieldDesc-size
    // along, but first bounds-check: locate the enclosing type (via the FieldDesc's enclosing
    // MethodTable) and, if `fieldDescPointer` is the last FieldDesc in that type's list, report that
    // there is no next FieldDesc by returning false.
    TargetPointer enclosingMT = GetMTOfEnclosingClass(fieldDescPointer);
    ITypeHandle typeHandle = GetTypeHandle(enclosingMT);
    // The field list holds the type's own instance fields (total instance fields minus the parent's)
    // followed by its static fields; see GetFieldDescList.
    TargetPointer lastFieldDesc = /* address of the final FieldDesc in typeHandle's list */;
    if (fieldDescPointer == lastFieldDesc)
    {
        nextFieldDesc = TargetPointer.Null;
        return false;
    }
    nextFieldDesc = fieldDescPointer + /* sizeof(FieldDesc) */;
    return true;
}
```

### Other APIs

```csharp
void 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;
    ITypeHandle 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);
}
```
