File: TargetFieldExtensions.cs
Web Access
Project: src\src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Abstractions\Microsoft.Diagnostics.DataContractReader.Abstractions.csproj (Microsoft.Diagnostics.DataContractReader.Abstractions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Numerics;
using Microsoft.Diagnostics.DataContractReader.Data;

namespace Microsoft.Diagnostics.DataContractReader;

/// <summary>
/// Extension methods for <see cref="Target"/> that provide typed field reading with optional
/// type validation. When the data descriptor includes type information (debug/checked builds),
/// these methods assert that the declared field type is compatible with the C# read type.
/// </summary>
public static class TargetFieldExtensions
{
    /// <summary>
    /// Read a primitive integer field from the target with type validation.
    /// </summary>
    public static T ReadField<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
        where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        AssertPrimitiveType<T>(field, fieldName);

        return target.Read<T>(address + (ulong)field.Offset);
    }

    /// <summary>
    /// Read an optional primitive integer field from the target with type validation.
    /// Returns <paramref name="defaultValue"/> if the field is not present in the descriptor.
    /// </summary>
    public static T ReadFieldOrDefault<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName, T defaultValue = default)
        where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
    {
        if (!typeInfo.Fields.TryGetValue(fieldName, out Target.FieldInfo field))
            return defaultValue;

        AssertPrimitiveType<T>(field, fieldName);

        return target.Read<T>(address + (ulong)field.Offset);
    }

    /// <summary>
    /// Read a pointer field from the target with type validation.
    /// </summary>
    public static TargetPointer ReadPointerField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        AssertPointerType(field, fieldName);

        return target.ReadPointer(address + (ulong)field.Offset);
    }

    /// <summary>
    /// Read an optional pointer field from the target with type validation.
    /// Returns <see cref="TargetPointer.Null"/> if the field is not present in the descriptor.
    /// </summary>
    public static TargetPointer ReadPointerFieldOrNull(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
    {
        if (!typeInfo.Fields.TryGetValue(fieldName, out Target.FieldInfo field))
            return TargetPointer.Null;

        AssertPointerType(field, fieldName);

        return target.ReadPointer(address + (ulong)field.Offset);
    }

    /// <summary>
    /// Read a native unsigned integer field from the target with type validation.
    /// </summary>
    public static TargetNUInt ReadNUIntField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        Debug.Assert(
            field.TypeName is null or "" or "nuint",
            $"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', expected nuint");

        return target.ReadNUInt(address + (ulong)field.Offset);
    }

    /// <summary>
    /// Read a code pointer field from the target with type validation.
    /// </summary>
    public static TargetCodePointer ReadCodePointerField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        Debug.Assert(
            field.TypeName is null or "" or "CodePointer",
            $"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', expected CodePointer");

        return target.ReadCodePointer(address + (ulong)field.Offset);
    }

    /// <summary>
    /// Read a field that contains an inline Data struct type, with type validation.
    /// Returns the data object created by <see cref="Target.IDataCache.GetOrAdd{T}"/>.
    /// </summary>
    public static T ReadDataField<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
        where T : IData<T>
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        Debug.Assert(
            field.TypeName is null or "" || field.TypeName == typeof(T).Name,
            $"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', reading as {typeof(T).Name}");

        return target.ProcessedData.GetOrAdd<T>(address + (ulong)field.Offset);
    }

    /// <summary>
    /// Read a field that contains a pointer to a Data struct type, with type validation.
    /// Reads the pointer, then creates the data object via <see cref="Target.IDataCache.GetOrAdd{T}"/>.
    /// Returns null if the pointer is null.
    /// </summary>
    public static T? ReadDataFieldPointer<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
        where T : IData<T>
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        AssertPointerType(field, fieldName);

        TargetPointer pointer = target.ReadPointer(address + (ulong)field.Offset);
        if (pointer == TargetPointer.Null)
            return default;

        return target.ProcessedData.GetOrAdd<T>(pointer);
    }

    /// <summary>
    /// Read an optional field that contains a pointer to a Data struct type, with type validation.
    /// Returns null if the field is not present in the descriptor or the pointer is null.
    /// </summary>
    public static T? ReadDataFieldPointerOrNull<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
        where T : IData<T>
    {
        if (!typeInfo.Fields.TryGetValue(fieldName, out Target.FieldInfo field))
            return default;

        AssertPointerType(field, fieldName);

        TargetPointer pointer = target.ReadPointer(address + (ulong)field.Offset);
        if (pointer == TargetPointer.Null)
            return default;

        return target.ProcessedData.GetOrAdd<T>(pointer);
    }

    /// <summary>
    /// Write a primitive integer field to the target with type validation.
    /// </summary>
    public static T WriteField<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName, T value)
        where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        AssertPrimitiveType<T>(field, fieldName);

        target.Write<T>(address + (ulong)field.Offset, value);
        return value;
    }

    /// <summary>
    /// Write a native unsigned integer field to the target with type validation.
    /// Returns the value written for convenient single-line backing-field updates.
    /// </summary>
    public static TargetNUInt WriteNUIntField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName, TargetNUInt value)
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        Debug.Assert(
            field.TypeName is null or "" or "nuint",
            $"Type mismatch writing field '{fieldName}': declared as '{field.TypeName}', expected nuint");

        ulong addr = address + (ulong)field.Offset;
        target.WriteNUInt(addr, value);
        return value;
    }

    /// <summary>
    /// Write a pointer field to the target with type validation.
    /// Returns the value written for convenient single-line backing-field updates.
    /// </summary>
    public static TargetPointer WritePointerField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName, TargetPointer value)
    {
        Target.FieldInfo field = typeInfo.Fields[fieldName];
        AssertPointerType(field, fieldName);

        ulong addr = address + (ulong)field.Offset;
        target.WritePointer(addr, value);
        return value;
    }

    [Conditional("DEBUG")]
    private static void AssertPrimitiveType<T>(Target.FieldInfo field, string fieldName)
        where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
    {
        Debug.Assert(
            field.TypeName is null or "" || IsCompatiblePrimitiveType<T>(field.TypeName),
            $"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', reading as {typeof(T).Name}");
    }

    [Conditional("DEBUG")]
    private static void AssertPointerType(Target.FieldInfo field, string fieldName)
    {
        Debug.Assert(
            field.TypeName is null or "" or "pointer",
            $"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', expected pointer");
    }

    private static bool IsCompatiblePrimitiveType<T>(string typeName)
        where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
    {
        return typeName switch
        {
            "uint8" => typeof(T) == typeof(byte),
            "int8" => typeof(T) == typeof(sbyte),
            "uint16" => typeof(T) == typeof(ushort),
            "int16" => typeof(T) == typeof(short),
            "uint32" => typeof(T) == typeof(uint),
            "int32" => typeof(T) == typeof(int),
            "uint64" => typeof(T) == typeof(ulong),
            "int64" => typeof(T) == typeof(long),
            "bool" => typeof(T) == typeof(byte),
            _ => false,
        };
    }
}