File: Linker.Dataflow\ArrayValue.cs
Web Access
Project: src\src\tools\illink\src\linker\Mono.Linker.csproj (illink)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
using System;
using System.Collections.Generic;
using System.Text;
using ILLink.Shared.DataFlow;
using Mono.Cecil;
using Mono.Linker.Dataflow;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
 
 
namespace ILLink.Shared.TrimAnalysis
{
    internal partial record ArrayValue
    {
        public static MultiValue Create(MultiValue size, TypeReference elementType)
        {
            MultiValue result = MultiValueLattice.Top;
            foreach (var sizeValue in size.AsEnumerable())
            {
                result = MultiValueLattice.Meet(result, new MultiValue(new ArrayValue(sizeValue, elementType)));
            }
 
            return result;
        }
 
        public static ArrayValue Create(int size, TypeReference elementType)
        {
            return new ArrayValue(new ConstIntValue(size), elementType);
        }
 
        /// <summary>
        /// Constructs an array value of the given size
        /// </summary>
        ArrayValue(SingleValue size, TypeReference elementType)
        {
            Size = size;
            ElementType = elementType;
            IndexValues = new Dictionary<int, ValueBasicBlockPair>();
        }
 
        public TypeReference ElementType { get; }
        public Dictionary<int, ValueBasicBlockPair> IndexValues { get; }
 
        public partial bool TryGetValueByIndex(int index, out MultiValue value)
        {
            if (IndexValues.TryGetValue(index, out var valuePair))
            {
                value = valuePair.Value;
                return true;
            }
 
            value = default;
            return false;
        }
 
        public override int GetHashCode()
        {
            return HashCode.Combine(GetType().GetHashCode(), Size);
        }
 
        public bool Equals(ArrayValue? otherArr)
        {
            if (otherArr == null)
                return false;
 
            bool equals = Size.Equals(otherArr.Size);
            equals &= IndexValues.Count == otherArr.IndexValues.Count;
            if (!equals)
                return false;
 
            // Here we rely on the assumption that we can't store mutable values in arrays. The only mutable value
            // which we currently support are array values, but those are not allowed in an array (to avoid complexity).
            // As such we can rely on the values to be immutable, and thus if the counts are equal
            // then the arrays are equal if items from one can be directly found in the other.
            foreach (var kvp in IndexValues)
                if (!otherArr.IndexValues.TryGetValue(kvp.Key, out ValueBasicBlockPair value) || !kvp.Value.Equals(value))
                    return false;
 
            return true;
        }
 
        public override SingleValue DeepCopy()
        {
            var newValue = new ArrayValue(Size.DeepCopy(), ElementType);
            foreach (var kvp in IndexValues)
            {
#if DEBUG
                // Since it's possible to store a reference to array as one of its own elements
                // simple deep copy could lead to endless recursion.
                // So instead we simply disallow arrays as element values completely - and treat that case as "too complex to analyze".
                foreach (SingleValue v in kvp.Value.Value.AsEnumerable())
                {
                    System.Diagnostics.Debug.Assert(v is not ArrayValue);
                }
#endif
 
                newValue.IndexValues.Add(kvp.Key, new ValueBasicBlockPair(kvp.Value.Value.DeepCopy(), kvp.Value.BasicBlockIndex));
            }
 
            return newValue;
        }
 
        public override string ToString()
        {
            StringBuilder result = new();
            result.Append("Array Size:");
            result.Append(this.ValueToString(Size));
 
            result.Append(", Values:(");
            bool first = true;
            foreach (var element in IndexValues)
            {
                if (!first)
                {
                    result.Append(',');
                    first = false;
                }
 
                result.Append('(');
                result.Append(element.Key);
                result.Append(",(");
                bool firstValue = true;
                foreach (var v in element.Value.Value.AsEnumerable())
                {
                    if (firstValue)
                    {
                        result.Append(',');
                        firstValue = false;
                    }
 
                    result.Append(v.ToString());
                }
                result.Append("))");
            }
            result.Append(')');
 
            return result.ToString();
        }
    }
}