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

namespace Microsoft.Diagnostics.DataContractReader.DataGenerator;

/// <summary>
/// Source for the <c>LayoutSet</c> struct emitted into each consuming assembly via
/// <c>RegisterPostInitializationOutput</c>. Resolves fields across an arbitrary
/// number of type layout sources (native cdac descriptors, managed type metadata,
/// symbols, etc.) discovered via <see cref="ContractRegistry.GetContracts{T}"/>.
/// </summary>
internal static class LayoutSetSource
{
    public const string HintName = "LayoutSet.g.cs";

    public const string Namespace = "Microsoft.Diagnostics.DataContractReader.Generated";

    public const string FullyQualifiedName = Namespace + ".LayoutSet";

    public const string Source = """
// <auto-generated/>
#nullable enable

using System;
using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Generated;

/// <summary>
/// An ordered set of type layouts resolved from multiple type info sources.
/// Field lookups iterate sources in priority order (native cdac descriptor
/// first, then managed type metadata), trying each candidate field name
/// per source. Sources are resolved lazily.
/// </summary>
internal readonly struct LayoutSet
{
    private readonly LazyLayout[] _layouts;

    public LayoutSet(LazyLayout[] layouts)
    {
        _layouts = layouts;
    }

    public ulong InstanceSize
    {
        get
        {
            foreach (LazyLayout layout in _layouts)
            {
                if (layout.Get()?.Size is uint s)
                    return s;
            }
            throw new InvalidOperationException("No layout source has a known instance size.");
        }
    }

    public bool TrySelect(TargetPointer address, out Target.TypeInfo type, out TargetPointer baseAddr, out string name, params ReadOnlySpan<string> names)
    {
        foreach (LazyLayout layout in _layouts)
        {
            if (layout.Get() is not Target.TypeInfo ti)
                continue;
            foreach (string candidate in names)
            {
                if (ti.Fields.ContainsKey(candidate))
                {
                    type = ti; baseAddr = address; name = candidate; return true;
                }
            }
        }
        type = default!; baseAddr = default; name = default!;
        return false;
    }

    public void Select(TargetPointer address, out Target.TypeInfo type, out TargetPointer baseAddr, out string name, params ReadOnlySpan<string> names)
    {
        if (!TrySelect(address, out type, out baseAddr, out name, names))
        {
            throw new InvalidOperationException(FormatMissing(names));
        }
    }

    private static string FormatMissing(ReadOnlySpan<string> names)
        => $"Field not found in any layout (names=[{string.Join(",", names.ToArray())}]).";

    public static LayoutSet Resolve(Target target, string[] names)
    {
        List<LazyLayout> layouts = new(2);

        foreach (string name in names)
        {
            if (target.TryGetTypeInfo(name, out Target.TypeInfo n))
            {
                layouts.Add(new LazyLayout(n));
                break;
            }
        }

        if (target.Contracts.TryGetContract(out IManagedTypeSource mts))
        {
            layouts.Add(new LazyLayout(() =>
            {
                foreach (string name in names)
                {
                    if (mts.TryGetTypeInfo(name, out Target.TypeInfo m))
                        return m;
                }
                return null;
            }));
        }

        return new LayoutSet(layouts.ToArray());
    }
}

/// <summary>
/// Memoized lazy holder for a single <see cref="Target.TypeInfo"/> source.
/// Constructed either with a pre-resolved value (for cheap sources) or with
/// a factory invoked on first access (for expensive sources).
/// </summary>
internal sealed class LazyLayout
{
    private Func<Target.TypeInfo?>? _factory;
    private Target.TypeInfo? _value;

    public LazyLayout(Target.TypeInfo value)
    {
        _value = value;
    }

    public LazyLayout(Func<Target.TypeInfo?> factory)
    {
        _factory = factory;
    }

    public Target.TypeInfo? Get()
    {
        if (_factory is not null)
        {
            _value = _factory();
            _factory = null;
        }
        return _value;
    }
}
""";
}