File: Contracts\ByRefPointerOffsetsReporter.cs
Web Access
Project: Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal sealed class ByRefPointerOffsetsReporter
{
    private const int MaxByRefLikeRecursionDepth = 16;

    private readonly IRuntimeTypeSystem _rts;
    private readonly uint _pointerSize;

    public ByRefPointerOffsetsReporter(Target target)
    {
        _rts = target.Contracts.RuntimeTypeSystem;
        _pointerSize = (uint)target.PointerSize;
    }

    public IEnumerable<ulong> Find(ITypeHandle typeHandle)
    {
        List<ulong> offsets = [];
        try
        {
            foreach (ulong offset in FindCore(typeHandle, baseOffset: 0, depth: 0))
                offsets.Add(offset);
        }
        catch (InvalidOperationException)
        {
            return [];
        }
        catch (VirtualReadException)
        {
            return [];
        }

        return offsets;
    }

    private IEnumerable<ulong> FindCore(
        TargetPointer fieldDesc,
        ulong baseOffset,
        int depth,
        bool isInlineArrayElement = false)
    {
        CorElementType fieldType = _rts.GetFieldDescType(fieldDesc);
        uint fieldOffset = isInlineArrayElement
            ? 0
            : _rts.GetFieldDescOffset(fieldDesc, fieldDef: null);

        if (fieldType == CorElementType.ValueType)
        {
            ITypeHandle? fieldTypeHandle = _rts.GetFieldDescApproxTypeHandle(fieldDesc);
            if (fieldTypeHandle is not null && _rts.IsByRefLike(fieldTypeHandle))
            {
                foreach (ulong slot in FindCore(fieldTypeHandle, baseOffset + fieldOffset, depth + 1))
                    yield return slot;
            }
        }
        else if (fieldType == CorElementType.Byref)
        {
            yield return baseOffset + fieldOffset;
        }
    }

    private IEnumerable<ulong> FindCore(ITypeHandle typeHandle, ulong baseOffset, int depth)
    {
        if (depth > MaxByRefLikeRecursionDepth)
            yield break;

        bool isInlineArray = _rts.IsInlineArray(typeHandle);
        foreach (TargetPointer fieldDesc in _rts.GetFieldDescList(typeHandle))
        {
            if (_rts.IsFieldDescStatic(fieldDesc))
                continue;

            if (isInlineArray)
            {
                CorElementType fieldType = _rts.GetFieldDescType(fieldDesc);
                ITypeHandle? fieldTypeHandle = fieldType == CorElementType.ValueType
                    ? _rts.GetFieldDescApproxTypeHandle(fieldDesc)
                    : null;
                uint elementSize = fieldType == CorElementType.Byref
                    ? _pointerSize
                    : fieldTypeHandle is not null ? _rts.GetNumInstanceFieldBytes(fieldTypeHandle) : 0;
                if (elementSize == 0)
                    continue;

                uint totalSize = _rts.GetNumInstanceFieldBytes(typeHandle);
                for (uint offset = 0; offset < totalSize; offset += elementSize)
                {
                    foreach (ulong slot in FindCore(fieldDesc, baseOffset + offset, depth, isInlineArrayElement: true))
                        yield return slot;
                }
            }
            else
            {
                foreach (ulong slot in FindCore(fieldDesc, baseOffset, depth, isInlineArrayElement: false))
                    yield return slot;
            }
        }
    }
}