File: Internal\Runtime\TypeLoader\TypeLoaderEnvironment.ConstructedGenericTypesLookup.cs
Web Access
Project: src\src\runtime\src\coreclr\nativeaot\System.Private.TypeLoader\src\System.Private.TypeLoader.csproj (System.Private.TypeLoader)
// 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.Diagnostics;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Threading;

using Internal.NativeFormat;
using Internal.Runtime;
using Internal.Runtime.Augments;
using Internal.TypeSystem;

namespace Internal.Runtime.TypeLoader
{
    public sealed partial class TypeLoaderEnvironment
    {
        internal class GenericTypeEntry
        {
            private int? _hashCode;
            public bool _isRegisteredSuccessfully;
            public RuntimeTypeHandle _instantiatedTypeHandle;
            public RuntimeTypeHandle _genericTypeDefinitionHandle;
            public RuntimeTypeHandle[] _genericTypeArgumentHandles;

            public override int GetHashCode()
            {
                if (!_hashCode.HasValue)
                {
                    _hashCode = VersionResilientHashCode.GenericInstanceHashCode(_genericTypeDefinitionHandle.GetHashCode(), _genericTypeArgumentHandles);
                }
                return _hashCode.Value;
            }

            public override bool Equals(object obj)
            {
                // There are no scenarios where we call .Equals to check the componentized equality (we explicitly use IsEqualToEntryByComponentsComparison for that),
                // so making sure we revert to the reference equality in case someone calls here.
                return base.Equals(obj);
            }

            public virtual bool IsEqualToEntryByComponentsComparison(GenericTypeEntry other)
            {
                if (!other._genericTypeDefinitionHandle.Equals(_genericTypeDefinitionHandle))
                    return false;

                if (other._genericTypeArgumentHandles == null)
                    return false;

                if (other._genericTypeArgumentHandles.Length != _genericTypeArgumentHandles.Length)
                    return false;

                for (int i = 0; i < _genericTypeArgumentHandles.Length; i++)
                    if (!other._genericTypeArgumentHandles[i].Equals(_genericTypeArgumentHandles[i]))
                        return false;

                return true;
            }
        }

        internal class DynamicGenericTypesHashtable : LockFreeReaderHashtable<GenericTypeLookupData, GenericTypeEntry>
        {
            protected override int GetKeyHashCode(GenericTypeLookupData key)
            {
                return key.LookupHashCode();
            }
            protected override bool CompareKeyToValue(GenericTypeLookupData key, GenericTypeEntry value)
            {
                return key.MatchGenericTypeEntry(value);
            }

            protected override int GetValueHashCode(GenericTypeEntry value)
            {
                return value.GetHashCode();
            }

            protected override bool CompareValueToValue(GenericTypeEntry value1, GenericTypeEntry value2)
            {
                // Comparisons should *only* be done using the generic type components
                return value1.IsEqualToEntryByComponentsComparison(value2);
            }

            protected override GenericTypeEntry CreateValueFromKey(GenericTypeLookupData key)
            {
                // Feature not used by the TypeBuilder
                throw NotImplemented.ByDesign;
            }
        }

        internal struct GenericTypeLookupData
        {
            private DefType _typeToLookup;
            private RuntimeTypeHandle _genericTypeDefinitionHandle;
            private RuntimeTypeHandle[] _genericTypeArgumentHandles;

            internal GenericTypeLookupData(DefType typeToLookup)
            {
                Debug.Assert(typeToLookup != null);
                _typeToLookup = typeToLookup;
                _genericTypeDefinitionHandle = _typeToLookup.GetTypeDefinition().RuntimeTypeHandle;
                // _genericTypeArgumentHandles not initialized here to avoid allocation of new array (and it's not used if we initialize _typeToLookup).
            }

            internal GenericTypeLookupData(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles)
            {
                Debug.Assert(genericTypeArgumentHandles != null);
                _typeToLookup = null;
                _genericTypeDefinitionHandle = genericTypeDefinitionHandle;
                _genericTypeArgumentHandles = genericTypeArgumentHandles;
            }

            internal int LookupHashCode()
            {
                return _typeToLookup != null ? _typeToLookup.GetHashCode() : VersionResilientHashCode.GenericInstanceHashCode(_genericTypeDefinitionHandle.GetHashCode(), _genericTypeArgumentHandles);
            }

            internal bool MatchParsedEntry(RuntimeTypeHandle tentativeType)
            {
                RuntimeTypeHandle parsedTypeDefinitionHandle = RuntimeAugments.GetGenericDefinition(tentativeType);
                if (!parsedTypeDefinitionHandle.Equals(_genericTypeDefinitionHandle))
                    return false;

                int lookupArity = (_typeToLookup != null ? _typeToLookup.Instantiation.Length : _genericTypeArgumentHandles.Length);

                for (int i = 0; i < lookupArity; i++)
                {
                    RuntimeTypeHandle parsedArg = RuntimeAugments.GetGenericArgument(tentativeType, i);
                    RuntimeTypeHandle lookupArg = (_typeToLookup != null ? _typeToLookup.Instantiation[i].RuntimeTypeHandle : _genericTypeArgumentHandles[i]);
                    if (!parsedArg.Equals(lookupArg))
                        return false;
                }

                return true;
            }

            internal bool MatchGenericTypeEntry(GenericTypeEntry entry)
            {
                if (!entry._genericTypeDefinitionHandle.Equals(_genericTypeDefinitionHandle))
                    return false;

                if (entry._genericTypeArgumentHandles == null)
                    return false;

                if (_typeToLookup != null)
                {
                    int expectedArity = _typeToLookup.Instantiation.Length;

                    if (entry._genericTypeArgumentHandles.Length != expectedArity)
                        return false;

                    for (int i = 0; i < expectedArity; i++)
                        if (!entry._genericTypeArgumentHandles[i].Equals(_typeToLookup.Instantiation[i].RuntimeTypeHandle))
                            return false;
                }
                else
                {
                    if (entry._genericTypeArgumentHandles.Length != _genericTypeArgumentHandles.Length)
                        return false;

                    for (int i = 0; i < _genericTypeArgumentHandles.Length; i++)
                        if (!entry._genericTypeArgumentHandles[i].Equals(_genericTypeArgumentHandles[i]))
                            return false;
                }

                return true;
            }
        }

        internal struct LazyDictionaryContext : IEquatable<LazyDictionaryContext>
        {
            public IntPtr _context;
            public IntPtr _signature;

            public override bool Equals(object obj)
            {
                if (!(obj is LazyDictionaryContext))
                    return false;
                return Equals((LazyDictionaryContext)obj);
            }

            public bool Equals(LazyDictionaryContext other)
            {
                return _context == other._context && _signature == other._signature;
            }

            public override int GetHashCode()
            {
                return _context.GetHashCode() ^ _signature.GetHashCode();
            }
        }

        private DynamicGenericTypesHashtable _dynamicGenericTypes = new DynamicGenericTypesHashtable();
        private LowLevelDictionary<LazyDictionaryContext, IntPtr> _lazyGenericDictionaries = new LowLevelDictionary<LazyDictionaryContext, IntPtr>();


        //
        // Return a generic type instantiation using the runtime type system. If the underlying runtime type system does not support
        // this operation, return false.
        //
        internal bool TryLookupConstructedGenericTypeForComponents(GenericTypeLookupData lookupData, out RuntimeTypeHandle runtimeTypeHandle)
        {
            if (!TryGetStaticGenericTypeForComponents(lookupData, out runtimeTypeHandle))
                if (!TryGetDynamicGenericTypeForComponents(lookupData, out runtimeTypeHandle))
                    return false;

            return true;
        }

        public bool TryLookupConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle)
        {
            return TryLookupConstructedGenericTypeForComponents(new GenericTypeLookupData(genericTypeDefinitionHandle, genericTypeArgumentHandles), out runtimeTypeHandle);
        }

        public bool TryLookupConstructedLazyDictionaryForContext(IntPtr context, IntPtr signature, out IntPtr dictionary)
        {
            Debug.Assert(_typeLoaderLock.IsHeldByCurrentThread);
            return _lazyGenericDictionaries.TryGetValue(new LazyDictionaryContext { _context = context, _signature = signature }, out dictionary);
        }

        #region Privates
        private unsafe bool TryGetDynamicGenericTypeForComponents(GenericTypeLookupData lookupData, out RuntimeTypeHandle runtimeTypeHandle)
        {
            runtimeTypeHandle = default(RuntimeTypeHandle);

            using (_dynamicGenericsLock.EnterScope())
            {
                GenericTypeEntry entry;
                if (!_dynamicGenericTypes.TryGetValue(lookupData, out entry))
                    return false;

                if (!entry._isRegisteredSuccessfully)
                    return false;

                runtimeTypeHandle = entry._instantiatedTypeHandle;
                return true;
            }
        }

        internal static unsafe bool TryGetStaticGenericTypeForComponents(GenericTypeLookupData lookupData, out RuntimeTypeHandle runtimeTypeHandle)
        {
            // Search the hashtable for a generic instantiation match
            // TODO multi-file: consider whether we can limit the search somehow,
            // i.e. not look at all the modules

            runtimeTypeHandle = default(RuntimeTypeHandle);

            NativeHashtable genericsHashtable;
            ExternalReferencesTable externalReferencesLookup;

            foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules())
            {
                if (!GetHashtableFromBlob(module, ReflectionMapBlob.GenericsHashtable, out genericsHashtable, out externalReferencesLookup))
                    continue;

                int lookupHashcode = lookupData.LookupHashCode();
                var enumerator = genericsHashtable.Lookup(lookupHashcode);

                NativeParser entryParser;
                while (!(entryParser = enumerator.GetNext()).IsNull)
                {
                    RuntimeTypeHandle tentativeType = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());

                    if (!lookupData.MatchParsedEntry(tentativeType))
                        continue;

                    runtimeTypeHandle = tentativeType;
                    Debug.Assert(RuntimeAugments.IsGenericType(runtimeTypeHandle));

                    return true;
                }
            }

            return false;
        }
        #endregion
    }
}