File: DependencyNodeCore.cs
Web Access
Project: ILCompiler.DependencyAnalysisFramework.csproj (ILCompiler.DependencyAnalysisFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Collections.Generic;

namespace ILCompiler.DependencyAnalysisFramework
{
    public abstract partial class DependencyNodeCore<DependencyContextType> : DependencyNode, IDependencyNode<DependencyContextType>
    {
        public readonly struct DependencyListEntry(
            DependencyNodeCore<DependencyContextType> node,
            string reason)
        {
            public DependencyListEntry(object node, string reason)
                : this((DependencyNodeCore<DependencyContextType>)node, reason)
            {
            }

            public readonly DependencyNodeCore<DependencyContextType> Node = node;
            public readonly string Reason = reason;
        }

        public class DependencyList : List<DependencyListEntry>, IDependencySink
        {
            public DependencyList() { }

            public DependencyList(IEnumerable<DependencyListEntry> collection)
                : base(collection)
            {
            }

            public virtual void Add(DependencyNodeCore<DependencyContextType> node,
                                    string reason)
            {
                Add(new DependencyListEntry(node, reason));
            }

            public virtual void Add(object node, string reason)
            {
                Add(new DependencyListEntry(node, reason));
            }

            public new virtual void Add(DependencyListEntry dependency)
            {
                base.Add(dependency);
            }

            public virtual void AddRange(params ReadOnlySpan<DependencyListEntry> dependencies)
            {
                foreach (DependencyListEntry dependency in dependencies)
                {
                    Add(dependency);
                }
            }
        }

        public class CombinedDependencyList : List<CombinedDependencyListEntry>, IConditionalDependencySink
        {
            public new virtual void Add(CombinedDependencyListEntry dependency)
            {
                base.Add(dependency);
            }
        }

        public readonly struct CombinedDependencyListEntry(
            DependencyNodeCore<DependencyContextType> node,
            DependencyNodeCore<DependencyContextType> otherReasonNode,
            string reason) : IEquatable<CombinedDependencyListEntry>
        {
            public CombinedDependencyListEntry(object node,
                                               object otherReasonNode,
                                               string reason)
                : this(
                    (DependencyNodeCore<DependencyContextType>)node,
                    (DependencyNodeCore<DependencyContextType>)otherReasonNode,
                    reason)
            {
            }

            internal readonly DependencyListEntry Dependency = new(node, reason);
            public readonly DependencyNodeCore<DependencyContextType> OtherReasonNode = otherReasonNode;

            public readonly DependencyNodeCore<DependencyContextType> Node => Dependency.Node;
            public readonly string Reason => Dependency.Reason;

            // Used by HashSet, so must have good Equals/GetHashCode
            public override bool Equals(object? obj)
            {
                return obj is CombinedDependencyListEntry && Equals((CombinedDependencyListEntry)obj);
            }

            public override int GetHashCode()
            {
                int hash = 23;
                hash = hash * 31 + Node.GetHashCode();
                hash = hash * 31 + OtherReasonNode.GetHashCode();

                if (Reason != null)
                    hash = hash * 31 + Reason.GetHashCode();

                return hash;
            }

            public bool Equals(CombinedDependencyListEntry other)
            {
                return ReferenceEquals(Node, other.Node)
                    && ReferenceEquals(OtherReasonNode, other.OtherReasonNode)
                    && Equals(Reason, other.Reason);
            }
        }

        public abstract bool InterestingForDynamicDependencyAnalysis
        {
            get;
        }

        public abstract bool HasDynamicDependencies
        {
            get;
        }

        public abstract bool HasConditionalStaticDependencies
        {
            get;
        }

        public abstract bool StaticDependenciesAreComputed
        {
            get;
        }

        public virtual int DependencyPhaseForDeferredStaticComputation { get; }

        public abstract void AddStaticDependencies(DependencySink sink, DependencyContextType context);

        public virtual void AddConditionalDependencies(DependencySink sink, DependencyContextType context)
        {
        }

        public virtual void SearchDynamicDependencies(List<DependencyNodeCore<DependencyContextType>> markedNodes, int firstNode, DependencySink sink, DependencyContextType context)
        {
        }

        internal void CallOnMarked(DependencyContextType context)
        {
            OnMarked(context);
        }

        /// <summary>
        /// Overrides of this method allow a node to perform actions when said node becomes
        /// marked.
        /// </summary>
        /// <param name="context"></param>
        protected virtual void OnMarked(DependencyContextType context)
        {
            // Do nothing by default
        }

        // Force all non-abstract nodes to provide a name
        protected abstract string GetName(DependencyContextType context);

        string IDependencyNode<DependencyContextType>.GetName(DependencyContextType context) => GetName(context);

        // We would prefer GetName to be "protected internal", but that will break people who want to source
        // include the dependency analysis framework. When nobody does that, maybe we can get rid of this method.
        internal string GetNameInternal(DependencyContextType context)
        {
            return GetName(context);
        }

        public static string GetNodeName(DependencyNodeCore<DependencyContextType> node, DependencyContextType context)
            => node.GetName(context);
    }
}