File: Internal\Runtime\TypeLoader\TypeLoaderEnvironment.StaticsLookup.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.Threading;

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

namespace Internal.Runtime.TypeLoader
{
    public sealed partial class TypeLoaderEnvironment
    {
        // To keep the synchronization simple, we execute all TLS registration/lookups under a global lock
        private Lock _threadStaticsLock = new Lock(useTrivialWaits: true);

        // Counter to keep track of generated offsets for TLS cells of dynamic types;
        private LowLevelDictionary<IntPtr, uint> _maxThreadLocalIndex = new LowLevelDictionary<IntPtr, uint>();
        private LowLevelDictionary<IntPtr, LowLevelDictionary<uint, IntPtr>> _dynamicGenericsThreadStaticDescs = new LowLevelDictionary<IntPtr, LowLevelDictionary<uint, IntPtr>>();

        // Various functions in static access need to create permanent pointers for use by thread static lookup.
        #region GC/Non-GC Statics
        /// <summary>
        /// Get a pointer to the nongc static field data of a type. This function works for dynamic
        /// types, reflectable types, and for all generic types
        /// </summary>
        public IntPtr TryGetNonGcStaticFieldData(RuntimeTypeHandle runtimeTypeHandle)
        {
            unsafe
            {
                // Non-generic, non-dynamic static data is found via the FieldAccessMap
                MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr();
                // Non-generic, non-dynamic types need special handling.
                Debug.Assert(typeAsEEType->IsDynamicType || typeAsEEType->IsGeneric);
            }

            // Search hashtable for static entry
            ExternalReferencesTable staticInfoLookup;
            var parser = GetStaticInfo(runtimeTypeHandle, out staticInfoLookup);
            if (!parser.IsNull)
            {
                var index = parser.GetUnsignedForBagElementKind(BagElementKind.NonGcStaticData);

                return index.HasValue ? staticInfoLookup.GetIntPtrFromIndex(index.Value) : IntPtr.Zero;
            }

            // Not found in hashtable... must be a dynamically created type
            Debug.Assert(runtimeTypeHandle.IsDynamicType());
            unsafe
            {
                MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr();
                if ((typeAsEEType->DynamicTypeFlags & DynamicTypeFlags.HasNonGCStatics) != 0)
                {
                    return typeAsEEType->DynamicNonGcStaticsData;
                }
            }

            // Type has no non-GC statics
            return IntPtr.Zero;
        }

        /// <summary>
        /// Get a pointer to the gc static field data of a type. This function works for dynamic
        /// types, reflectable types, and for all generic types
        /// </summary>
        public IntPtr TryGetGcStaticFieldData(RuntimeTypeHandle runtimeTypeHandle)
        {
            unsafe
            {
                // Non-generic, non-dynamic static data is found via the FieldAccessMap
                MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr();
                // Non-generic, non-dynamic types need special handling.
                Debug.Assert(typeAsEEType->IsDynamicType || typeAsEEType->IsGeneric);
            }

            // Search hashtable for static entry
            ExternalReferencesTable staticInfoLookup;
            var parser = GetStaticInfo(runtimeTypeHandle, out staticInfoLookup);
            if (!parser.IsNull)
            {
                var index = parser.GetUnsignedForBagElementKind(BagElementKind.GcStaticData);

                return index.HasValue ? staticInfoLookup.GetIntPtrFromIndex(index.Value) : IntPtr.Zero;
            }

            // Not found in hashtable... must be a dynamically created type
            Debug.Assert(runtimeTypeHandle.IsDynamicType());
            unsafe
            {
                MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr();
                if ((typeAsEEType->DynamicTypeFlags & DynamicTypeFlags.HasGCStatics) != 0)
                {
                    return typeAsEEType->DynamicGcStaticsData;
                }
            }

            // Type has no GC statics
            return IntPtr.Zero;
        }
        #endregion


        #region Thread Statics
        /// <summary>
        /// Get a pointer to a pointer to the thread static field data of a type. This function works for all generic types
        /// </summary>
        public IntPtr TryGetThreadStaticFieldData(RuntimeTypeHandle runtimeTypeHandle)
        {
            unsafe
            {
                // Non-generic, non-dynamic static data is found via the FieldAccessMap
                MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr();
                // Non-generic, non-dynamic types need special handling.
                Debug.Assert(typeAsEEType->IsDynamicType || typeAsEEType->IsGeneric);
            }

            // Search hashtable for static entry
            ExternalReferencesTable staticInfoLookup;
            var parser = GetStaticInfo(runtimeTypeHandle, out staticInfoLookup);
            if (!parser.IsNull)
            {
                var index = parser.GetUnsignedForBagElementKind(BagElementKind.ThreadStaticIndex);

                return index.HasValue ? staticInfoLookup.GetIntPtrFromIndex(index.Value) : IntPtr.Zero;
            }

            // Not found in hashtable... might be a dynamically created type
            if (runtimeTypeHandle.IsDynamicType())
            {
                unsafe
                {
                    MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr();
                    if (typeAsEEType->DynamicThreadStaticsIndex != IntPtr.Zero)
                        return typeAsEEType->DynamicThreadStaticsIndex;
                }
            }

            // Type has no GC statics
            return IntPtr.Zero;
        }

        public IntPtr GetThreadStaticGCDescForDynamicType(TypeManagerHandle typeManagerHandle, uint index)
        {
            using (_threadStaticsLock.EnterScope())
            {
                return _dynamicGenericsThreadStaticDescs[typeManagerHandle.GetIntPtrUNSAFE()][index];
            }
        }

        public uint GetNextThreadStaticsOffsetValue(TypeManagerHandle typeManagerHandle)
        {
            if (!_maxThreadLocalIndex.TryGetValue(typeManagerHandle.GetIntPtrUNSAFE(), out uint result))
                result = (uint)RuntimeAugments.GetHighestStaticThreadStaticIndex(typeManagerHandle);

            _maxThreadLocalIndex[typeManagerHandle.GetIntPtrUNSAFE()] = checked(++result);

            return result;
        }

        public void RegisterDynamicThreadStaticsInfo(RuntimeTypeHandle runtimeTypeHandle, uint offsetValue, IntPtr gcDesc)
        {
            bool registered = false;
            Debug.Assert(offsetValue != 0 && runtimeTypeHandle.IsDynamicType());

            IntPtr typeManager = runtimeTypeHandle.GetTypeManager().GetIntPtrUNSAFE();

            _threadStaticsLock.Enter();
            try
            {
                if (!_dynamicGenericsThreadStaticDescs.TryGetValue(typeManager, out LowLevelDictionary<uint, IntPtr> gcDescs))
                {
                    _dynamicGenericsThreadStaticDescs.Add(typeManager, gcDescs = new LowLevelDictionary<uint, IntPtr>());
                }
                gcDescs.Add(offsetValue, gcDesc);
                registered = true;
            }
            finally
            {
                if (!registered)
                {
                    if (_dynamicGenericsThreadStaticDescs.TryGetValue(typeManager, out LowLevelDictionary<uint, IntPtr> gcDescs))
                    {
                        gcDescs.Remove(offsetValue);
                    }
                }

                _threadStaticsLock.Exit();
            }
        }
        #endregion


        #region Privates
        // get the statics hash table, external references, and static info table for a module
        // TODO multi-file: consider whether we want to cache this info
        private static unsafe bool GetStaticsInfoHashtable(NativeFormatModuleInfo module, out NativeHashtable staticsInfoHashtable, out ExternalReferencesTable externalReferencesLookup, out ExternalReferencesTable staticInfoLookup)
        {
            byte* pBlob;
            uint cbBlob;

            staticsInfoHashtable = default(NativeHashtable);
            externalReferencesLookup = default(ExternalReferencesTable);
            staticInfoLookup = default(ExternalReferencesTable);

            // Load statics info hashtable
            if (!module.TryFindBlob(ReflectionMapBlob.StaticsInfoHashtable, out pBlob, out cbBlob))
                return false;
            NativeReader reader = new NativeReader(pBlob, cbBlob);
            NativeParser parser = new NativeParser(reader, 0);

            if (!externalReferencesLookup.InitializeNativeReferences(module))
                return false;

            if (!staticInfoLookup.InitializeNativeStatics(module))
                return false;

            staticsInfoHashtable = new NativeHashtable(parser);

            return true;
        }

        private static NativeParser GetStaticInfo(RuntimeTypeHandle instantiatedType, out ExternalReferencesTable staticsInfoLookup)
        {
            TypeManagerHandle moduleHandle = RuntimeAugments.GetModuleFromTypeHandle(instantiatedType);
            NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(moduleHandle);
            NativeHashtable staticsInfoHashtable;
            ExternalReferencesTable externalReferencesLookup;
            if (!GetStaticsInfoHashtable(module, out staticsInfoHashtable, out externalReferencesLookup, out staticsInfoLookup))
                return default(NativeParser);

            int lookupHashcode = instantiatedType.GetHashCode();
            var enumerator = staticsInfoHashtable.Lookup(lookupHashcode);

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

                if (!parsedInstantiatedType.Equals(instantiatedType))
                    continue;

                return entryParser;
            }

            return default(NativeParser);
        }
        #endregion
    }
}