Repository: dotnet/runtime
File: src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/TypeValidation.cs (C#)

Code before:

namespace Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers;

internal sealed class TypeValidation
{
    private readonly Target _target;

    internal TypeValidation(Target target)
    {
        _target = target;
    }

    // This doesn't need as many properties as MethodTable because we don't want to be operating on
    // a NonValidatedMethodTable for too long
    internal struct NonValidatedMethodTable
    {
        private readonly Data.MethodTable _methodTable;
        internal TargetPointer Address { get; init; }

        internal NonValidatedMethodTable(Target target, TargetPointer methodTablePointer)
        {
            Address = methodTablePointer;
            // Construct the full MethodTable to eagerly read all fields so that any
            // VirtualReadException from unreadable memory is raised during validation
            // (where it is caught) rather than later when Data.MethodTable is constructed.
            _methodTable = new Data.MethodTable(target, methodTablePointer);
        }

        internal MethodTableFlags_1 Flags => GetOrCreateFlags();

        internal TargetPointer EEClassOrCanonMT => _methodTable.EEClassOrCanonMT;
        internal TargetPointer EEClass => MethodTableFlags_1.GetEEClassOrCanonMTBits(EEClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.EEClass ? EEClassOrCanonMT : throw new InvalidOperationException("not an EEClass");
        internal TargetPointer CanonMT
        {
            get
            {
                if (MethodTableFlags_1.GetEEClassOrCanonMTBits(EEClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.CanonMT)
                {
                    return MethodTableFlags_1.UntagEEClassOrCanonMT(EEClassOrCanonMT);
                }
                else
                {
                    throw new InvalidOperationException("not a canonical method table");
                }
            }
        }
    }

    internal struct NonValidatedEEClass
    {
        private readonly Data.EEClass _eeClass;

Review comment (suggestion):
Doing the validation here seems to defeat the purpose of having a NonValidatedMethodTable type. If we want to validate the MethodTable eagerly I'd suggest lets change the caller to create a Data.MethodTable directly rather than creating a NonValidatedMethodTable.

Code after:

namespace Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers;

internal sealed class TypeValidation
{
    private readonly Target _target;

    internal TypeValidation(Target target)
    {
        _target = target;
    }

    // This doesn't need as many properties as MethodTable because we don't want to be operating on
    // a NonValidatedMethodTable for too long
    internal struct NonValidatedMethodTable
    {
        private readonly Target _target;
        private readonly Target.TypeInfo _type;
        internal TargetPointer Address { get; init; }

        private MethodTableFlags_1? _methodTableFlags;

        internal NonValidatedMethodTable(Target target, TargetPointer methodTablePointer)
        {
            _target = target;
            _type = target.GetTypeInfo(DataType.MethodTable);
            Address = methodTablePointer;
            _methodTableFlags = null;
        }

        private MethodTableFlags_1 GetOrCreateFlags()
        {
            if (_methodTableFlags == null)
            {
                // note: may throw if the method table Address is corrupted
                MethodTableFlags_1 flags = new MethodTableFlags_1
                {
                    MTFlags = _target.Read<uint>(Address + (ulong)_type.Fields[nameof(MethodTableFlags_1.MTFlags)].Offset),
                    MTFlags2 = _target.Read<uint>(Address + (ulong)_type.Fields[nameof(MethodTableFlags_1.MTFlags2)].Offset),
                    BaseSize = _target.Read<uint>(Address + (ulong)_type.Fields[nameof(MethodTableFlags_1.BaseSize)].Offset),
                };
                _methodTableFlags = flags;
            }
            return _methodTableFlags.Value;
        }

        internal MethodTableFlags_1 Flags => GetOrCreateFlags();

        internal TargetPointer EEClassOrCanonMT => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(EEClassOrCanonMT)].Offset);
        internal TargetPointer EEClass => MethodTableFlags_1.GetEEClassOrCanonMTBits(EEClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.EEClass ? EEClassOrCanonMT : throw new InvalidOperationException("not an EEClass");
        internal TargetPointer CanonMT