File: Compiler\CompilerTypeSystemContext.GetFieldMethodOverrides.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler)
// 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 Internal.TypeSystem;
using Internal.IL.Stubs;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler
{
    public partial class CompilerTypeSystemContext
    {
        private MethodDesc _objectEqualsMethod;
        private MetadataType _iAsyncStateMachineType;

        private sealed class GetFieldMethodHashtable : LockFreeReaderHashtable<MetadataType, MethodDesc>
        {
            protected override int GetKeyHashCode(MetadataType key) => key.GetHashCode();
            protected override int GetValueHashCode(MethodDesc value) => value.OwningType.GetHashCode();
            protected override bool CompareKeyToValue(MetadataType key, MethodDesc value) => key == value.OwningType;
            protected override bool CompareValueToValue(MethodDesc v1, MethodDesc v2) => v1.OwningType == v2.OwningType;

            protected override MethodDesc CreateValueFromKey(MetadataType key)
            {
                return new GetFieldHelperMethodOverride(key);
            }
        }

        private GetFieldMethodHashtable _getFieldMethodHashtable = new GetFieldMethodHashtable();

        protected virtual IEnumerable<MethodDesc> GetAllMethodsForValueType(TypeDesc valueType, bool virtualOnly)
        {
            TypeDesc valueTypeDefinition = valueType.GetTypeDefinition();

            if (RequiresValueTypeGetFieldHelperMethod((MetadataType)valueTypeDefinition))
            {
                MethodDesc getFieldHelperMethod = _getFieldMethodHashtable.GetOrCreateValue((MetadataType)valueTypeDefinition);

                if (valueType != valueTypeDefinition)
                {
                    yield return GetMethodForInstantiatedType(getFieldHelperMethod, (InstantiatedType)valueType);
                }
                else
                {
                    yield return getFieldHelperMethod;
                }
            }

            IEnumerable<MethodDesc> metadataMethods = virtualOnly ? valueType.GetVirtualMethods() : valueType.GetMethods();
            foreach (MethodDesc method in metadataMethods)
                yield return method;
        }

        protected virtual IEnumerable<MethodDesc> GetAllMethodsForAttribute(TypeDesc attributeType, bool virtualOnly)
        {
            TypeDesc attributeTypeDefinition = attributeType.GetTypeDefinition();

            if (RequiresAttributeGetFieldHelperMethod(attributeTypeDefinition))
            {
                MethodDesc getFieldHelperMethod = _getFieldMethodHashtable.GetOrCreateValue((MetadataType)attributeTypeDefinition);

                if (attributeType != attributeTypeDefinition)
                {
                    yield return GetMethodForInstantiatedType(getFieldHelperMethod, (InstantiatedType)attributeType);
                }
                else
                {
                    yield return getFieldHelperMethod;
                }
            }

            IEnumerable<MethodDesc> metadataMethods = virtualOnly ? attributeType.GetVirtualMethods() : attributeType.GetMethods();
            foreach (MethodDesc method in metadataMethods)
                yield return method;
        }

        private bool RequiresValueTypeGetFieldHelperMethod(MetadataType valueType)
        {
            _objectEqualsMethod ??= GetWellKnownType(WellKnownType.Object).GetMethod("Equals"u8, null);

            // If the classlib doesn't have Object.Equals, we don't need this.
            if (_objectEqualsMethod == null)
                return false;

            // Byref-like valuetypes cannot be boxed.
            if (valueType.IsByRefLike)
                return false;

            // Enums get their overrides from System.Enum.
            if (valueType.IsEnum)
                return false;

            // These need to provide an implementation of Equals/GetHashCode because of NaN handling.
            // The helper would be useless.
            if (valueType.IsWellKnownType(WellKnownType.Double) || valueType.IsWellKnownType(WellKnownType.Single))
                return false;

            // Heuristic: async state machines don't need equality/hashcode.
            if (IsAsyncStateMachineType(valueType))
                return false;

            // ValueTuples override Equals/GetHashCode and don't have a `base.GetHashCode` call. So avoid the infrastructure.
            if (valueType.Module == SystemModule && valueType.Name.StartsWith("ValueTuple`"u8) && valueType.Namespace.SequenceEqual("System"u8))
                return false;

            return !_typeStateHashtable.GetOrCreateValue(valueType).CanCompareValueTypeBits;
        }

        public bool IsAsyncStateMachineType(MetadataType type)
        {
            Debug.Assert(type.IsValueType);
            _iAsyncStateMachineType ??= SystemModule.GetType("System.Runtime.CompilerServices"u8, "IAsyncStateMachine"u8, throwIfNotFound: false);
            return type.HasCustomAttribute("System.Runtime.CompilerServices", "CompilerGeneratedAttribute")
                && Array.IndexOf(type.RuntimeInterfaces, _iAsyncStateMachineType) >= 0;
        }

        private bool RequiresAttributeGetFieldHelperMethod(TypeDesc attributeTypeDef)
        {
            _objectEqualsMethod ??= GetWellKnownType(WellKnownType.Object).GetMethod("Equals"u8, null);

            // If the classlib doesn't have Object.Equals, we don't need this.
            if (_objectEqualsMethod == null)
                return false;

            foreach (FieldDesc field in attributeTypeDef.GetFields())
            {
                if (field.IsStatic)
                    continue;

                return true;
            }

            return false;
        }

        private sealed class TypeState
        {
            private enum Flags
            {
                CanCompareValueTypeBits         = 0x0000_0001,
                CanCompareValueTypeBitsComputed = 0x0000_0002,
            }

            private volatile Flags _flags;
            private readonly TypeStateHashtable _hashtable;

            public TypeDesc Type { get; }

            public bool CanCompareValueTypeBits
            {
                get
                {
                    Flags flags = _flags;
                    if ((flags & Flags.CanCompareValueTypeBitsComputed) == 0)
                    {
                        Debug.Assert(Type.IsValueType);
                        MetadataType mdType = (MetadataType)Type;
                        if (ComparerIntrinsics.CanCompareValueTypeBits(mdType, ((CompilerTypeSystemContext)mdType.Context)._objectEqualsMethod))
                            flags |= Flags.CanCompareValueTypeBits;
                        flags |= Flags.CanCompareValueTypeBitsComputed;

                        _flags = flags;
                    }
                    return (flags & Flags.CanCompareValueTypeBits) != 0;
                }
            }

            public TypeState(TypeDesc type, TypeStateHashtable hashtable)
            {
                Type = type;
                _hashtable = hashtable;
            }
        }

        private sealed class TypeStateHashtable : LockFreeReaderHashtable<TypeDesc, TypeState>
        {
            protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
            protected override int GetValueHashCode(TypeState value) => value.Type.GetHashCode();
            protected override bool CompareKeyToValue(TypeDesc key, TypeState value) => key == value.Type;
            protected override bool CompareValueToValue(TypeState v1, TypeState v2) => v1.Type == v2.Type;

            protected override TypeState CreateValueFromKey(TypeDesc key)
            {
                return new TypeState(key, this);
            }
        }
        private TypeStateHashtable _typeStateHashtable = new TypeStateHashtable();
    }
}