File: ILCompiler\Metadata\Transform.TypeForwarders.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.MetadataTransform\ILCompiler.MetadataTransform.csproj (ILCompiler.MetadataTransform)
// 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.Metadata.NativeFormat.Writer;

using Cts = Internal.TypeSystem;
using Ecma = System.Reflection.Metadata;

using Debug = System.Diagnostics.Debug;
using AssemblyNameInfo = System.Reflection.Metadata.AssemblyNameInfo;
using AssemblyContentType = System.Reflection.AssemblyContentType;
using AssemblyNameFlags = System.Reflection.AssemblyNameFlags;
using AssemblyFlags = System.Reflection.AssemblyFlags;

namespace ILCompiler.Metadata
{
    internal partial class Transform<TPolicy>
    {
        private EntityMap<ForwarderKey, TypeForwarder> _forwarders = new EntityMap<ForwarderKey, TypeForwarder>(EqualityComparer<ForwarderKey>.Default);

        private Action<ForwarderKey, TypeForwarder> _initForwarder;

        private void HandleTypeForwarders(Cts.Ecma.EcmaModule module)
        {
            foreach (var exportedTypeHandle in module.MetadataReader.ExportedTypes)
            {
                if (!_policy.GeneratesMetadata(module, exportedTypeHandle))
                {
                    continue;
                }

                Ecma.ExportedType exportedType = module.MetadataReader.GetExportedType(exportedTypeHandle);
                if (exportedType.IsForwarder || exportedType.Implementation.Kind == Ecma.HandleKind.ExportedType)
                {
                    HandleTypeForwarder(module, exportedTypeHandle);
                }
                else
                {
                    Debug.Fail("Multi-module assemblies");
                }
            }
        }

        private TypeForwarder HandleTypeForwarder(Cts.Ecma.EcmaModule module, Ecma.ExportedTypeHandle handle)
        {
            return _forwarders.GetOrCreate(new ForwarderKey(module, handle), _initForwarder ??= InitializeTypeForwarder);
        }

        private void InitializeTypeForwarder(ForwarderKey key, TypeForwarder record)
        {
            Cts.Ecma.EcmaModule module = key.Module;
            Ecma.MetadataReader reader = module.MetadataReader;
            Ecma.ExportedType exportedType = reader.GetExportedType(key.ExportedType);

            record.Name = HandleString(reader.GetString(exportedType.Name));

            switch (exportedType.Implementation.Kind)
            {
                case Ecma.HandleKind.AssemblyReference:
                    {
                        string ns = reader.GetString(exportedType.Namespace);
                        NamespaceDefinition namespaceDefinition = HandleNamespaceDefinition(module, ns);

                        Ecma.AssemblyReference assemblyRef = reader.GetAssemblyReference((Ecma.AssemblyReferenceHandle)exportedType.Implementation);
                        AssemblyNameInfo refName = new AssemblyNameInfo
                        (
                            name: reader.GetString(assemblyRef.Name),
                            version: assemblyRef.Version,
                            cultureName: reader.GetString(assemblyRef.Culture),
                            flags: (AssemblyNameFlags)assemblyRef.Flags,
                            publicKeyOrToken: reader.GetBlobContent(assemblyRef.PublicKeyOrToken)
                        );
                        record.Scope = HandleScopeReference(refName);

                        namespaceDefinition.TypeForwarders.Add(record);
                    }
                    break;

                case Ecma.HandleKind.ExportedType:
                    {
                        TypeForwarder scope = HandleTypeForwarder(module, (Ecma.ExportedTypeHandle)exportedType.Implementation);

                        record.Scope = scope.Scope;

                        scope.NestedTypes.Add(record);
                    }
                    break;

                default:
                    throw new BadImageFormatException();
            }
        }

#pragma warning disable CA1067 // Override Object.Equals(object) when implementing IEquatable<T>
        private readonly struct ForwarderKey : IEquatable<ForwarderKey>
#pragma warning restore CA1067 // Override Object.Equals(object) when implementing IEquatable<T>
        {
            public readonly Cts.Ecma.EcmaModule Module;
            public readonly Ecma.ExportedTypeHandle ExportedType;
            public ForwarderKey(Cts.Ecma.EcmaModule module, Ecma.ExportedTypeHandle exportedType)
                => (Module, ExportedType) = (module, exportedType);

            public bool Equals(ForwarderKey other) => Module == other.Module && ExportedType == other.ExportedType;
            public override int GetHashCode() => HashCode.Combine(Module.GetHashCode(), ExportedType.GetHashCode());
        }
    }
}