File: ComInterfaceContext.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\gen\ComInterfaceGenerator\ComInterfaceGenerator.csproj (Microsoft.Interop.ComInterfaceGenerator)
// 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;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis;
 
namespace Microsoft.Interop
{
    internal sealed record ComInterfaceContext
    {
        internal ComInterfaceInfo Info { get; init; }
        internal ComInterfaceContext? Base { get; init; }
        internal ComInterfaceOptions Options { get; init; }
        internal bool IsExternallyDefined { get; init; }
 
        private ComInterfaceContext(ComInterfaceInfo info, ComInterfaceContext? @base, ComInterfaceOptions options)
        {
            Info = info;
            Base = @base;
            Options = options;
        }
 
        /// <summary>
        /// Takes a list of ComInterfaceInfo, and creates a list of ComInterfaceContext.
        /// </summary>
        public static ImmutableArray<DiagnosticOr<ComInterfaceContext>> GetContexts(ImmutableArray<ComInterfaceInfo> data, CancellationToken _)
        {
            Dictionary<string, ComInterfaceInfo> nameToInterfaceInfoMap = new();
            var accumulator = ImmutableArray.CreateBuilder<DiagnosticOr<ComInterfaceContext>>(data.Length);
            foreach (var iface in data)
            {
                nameToInterfaceInfoMap.Add(iface.ThisInterfaceKey, iface);
            }
            Dictionary<string, DiagnosticOr<ComInterfaceContext>> nameToContextCache = new();
 
            foreach (var iface in data)
            {
                accumulator.Add(AddContext(iface));
            }
            return accumulator.MoveToImmutable();
 
            DiagnosticOr<ComInterfaceContext> AddContext(ComInterfaceInfo iface)
            {
                if (nameToContextCache.TryGetValue(iface.ThisInterfaceKey, out var cachedValue))
                {
                    return cachedValue;
                }
 
                if (iface.BaseInterfaceKey is null)
                {
                    var baselessCtx = DiagnosticOr<ComInterfaceContext>.From(new ComInterfaceContext(iface, null, iface.Options) { IsExternallyDefined = iface.IsExternallyDefined });
                    nameToContextCache[iface.ThisInterfaceKey] = baselessCtx;
                    return baselessCtx;
                }
 
                DiagnosticOr<ComInterfaceContext> baseReturnedValue;
                if (
                    // Cached base info is a diagnostic - failure
                    (nameToContextCache.TryGetValue(iface.BaseInterfaceKey, out var baseCachedValue) && baseCachedValue.HasDiagnostic)
                    // Cannot find base ComInterfaceInfo - failure (failed ComInterfaceInfo creation)
                    || !nameToInterfaceInfoMap.TryGetValue(iface.BaseInterfaceKey, out var baseInfo)
                    // Newly calculated base context pair is a diagnostic - failure
                    || (baseReturnedValue = AddContext(baseInfo)).HasDiagnostic)
                {
                    // The base has failed generation at some point, so this interface cannot be generated
                    var diagnostic = DiagnosticOr<ComInterfaceContext>.From(
                        DiagnosticInfo.Create(
                            GeneratorDiagnostics.BaseInterfaceIsNotGenerated,
                            iface.DiagnosticLocation, iface.ThisInterfaceKey, iface.BaseInterfaceKey));
                    nameToContextCache[iface.ThisInterfaceKey] = diagnostic;
                    return diagnostic;
                }
                DiagnosticOr<ComInterfaceContext> baseContext = baseCachedValue ?? baseReturnedValue;
                Debug.Assert(baseContext.HasValue);
                var ctx = DiagnosticOr<ComInterfaceContext>.From(new ComInterfaceContext(iface, baseContext.Value, iface.Options) { IsExternallyDefined = iface.IsExternallyDefined });
                nameToContextCache[iface.ThisInterfaceKey] = ctx;
                return ctx;
            }
        }
 
        internal ComInterfaceContext GetTopLevelBase()
        {
            var currBase = Base;
            while (currBase is not null)
                currBase = currBase.Base;
            return currBase;
        }
    }
}