|
// 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;
}
}
""";
}
|