File: ILCompiler\Metadata\Transform.Scope.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 System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Internal.Metadata.NativeFormat.Writer;

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

using Debug = System.Diagnostics.Debug;
using AssemblyFlags = Internal.Metadata.NativeFormat.AssemblyFlags;
using AssemblyNameInfo = System.Reflection.Metadata.AssemblyNameInfo;

namespace ILCompiler.Metadata
{
    internal partial class Transform<TPolicy>
    {
        internal EntityMap<Cts.ModuleDesc, ScopeDefinition> _scopeDefs
            = new EntityMap<Cts.ModuleDesc, ScopeDefinition>(EqualityComparer<Cts.ModuleDesc>.Default);
        private Action<Cts.ModuleDesc, ScopeDefinition> _initScopeDef;

        private ScopeDefinition HandleScopeDefinition(Cts.ModuleDesc module)
        {
            return _scopeDefs.GetOrCreate(module, _initScopeDef ??= InitializeScopeDefinition);
        }

        private void InitializeScopeDefinition(Cts.ModuleDesc module, ScopeDefinition scopeDefinition)
        {
            var assemblyDesc = module as Cts.IAssemblyDesc;
            if (assemblyDesc != null)
            {
                var assemblyName = assemblyDesc.GetName();

                scopeDefinition.Name = HandleString(assemblyName.Name);
                scopeDefinition.Culture = HandleString(assemblyName.CultureName);
                scopeDefinition.MajorVersion = checked((ushort)assemblyName.Version.Major);
                scopeDefinition.MinorVersion = checked((ushort)assemblyName.Version.Minor);
                scopeDefinition.BuildNumber = checked((ushort)assemblyName.Version.Build);
                scopeDefinition.RevisionNumber = checked((ushort)assemblyName.Version.Revision);
                scopeDefinition.Flags = (AssemblyFlags)assemblyName.Flags;
                scopeDefinition.PublicKey = assemblyName.PublicKeyOrToken.ToArray();

                Cts.MetadataType moduleType = module.GetGlobalModuleType();
                if (moduleType != null && _policy.GeneratesMetadata(moduleType))
                {
                    scopeDefinition.GlobalModuleType = (TypeDefinition)HandleType(moduleType);
                }

                Cts.Ecma.EcmaAssembly ecmaAssembly = module as Cts.Ecma.EcmaAssembly;
                if (ecmaAssembly != null)
                {
                    Ecma.CustomAttributeHandleCollection customAttributes = ecmaAssembly.AssemblyDefinition.GetCustomAttributes();
                    if (customAttributes.Count > 0)
                    {
                        scopeDefinition.CustomAttributes = HandleCustomAttributes(ecmaAssembly, customAttributes);
                    }

                    Cts.MethodDesc entryPoint = ecmaAssembly.EntryPoint;
                    if (entryPoint != null && _policy.GeneratesMetadata(entryPoint))
                    {
                        scopeDefinition.EntryPoint = (QualifiedMethod)HandleQualifiedMethod(entryPoint);
                    }

                    Ecma.MetadataReader reader = ecmaAssembly.MetadataReader;
                    Ecma.ModuleDefinition moduleDefinition = reader.GetModuleDefinition();
                    scopeDefinition.ModuleName = HandleString(reader.GetString(moduleDefinition.Name));
                    scopeDefinition.Mvid = reader.GetGuid(moduleDefinition.Mvid).ToByteArray();

                    // This is rather awkward because ModuleDefinition doesn't offer means to get to the custom attributes
                    Ecma.CustomAttributeHandleCollection moduleAttributes = reader.GetCustomAttributes(Ecma.Ecma335.MetadataTokens.EntityHandle(0x1));
                    if (moduleAttributes.Count > 0)
                    {
                        scopeDefinition.ModuleCustomAttributes = HandleCustomAttributes(ecmaAssembly, moduleAttributes);
                    }

                    HandleTypeForwarders(ecmaAssembly);
                }
            }
            else
            {
                throw new NotSupportedException("Multi-module assemblies");
            }
        }

        private EntityMap<AssemblyNameInfo, ScopeReference> _scopeRefs
            = new EntityMap<AssemblyNameInfo, ScopeReference>(new SimpleAssemblyNameComparer());
        private Action<AssemblyNameInfo, ScopeReference> _initScopeRef;

        private ScopeReference HandleScopeReference(Cts.ModuleDesc module)
        {
            var assembly = module as Cts.IAssemblyDesc;
            if (assembly != null)
                return HandleScopeReference(assembly.GetName());
            else
                throw new NotSupportedException("Multi-module assemblies");
        }

        private ScopeReference HandleScopeReference(AssemblyNameInfo assemblyName)
        {
            return _scopeRefs.GetOrCreate(assemblyName, _initScopeRef ??= InitializeScopeReference);
        }

        private void InitializeScopeReference(AssemblyNameInfo assemblyName, ScopeReference scopeReference)
        {
            scopeReference.Name = HandleString(assemblyName.Name);
            scopeReference.Culture = HandleString(assemblyName.CultureName);
            scopeReference.MajorVersion = checked((ushort)assemblyName.Version.Major);
            scopeReference.MinorVersion = checked((ushort)assemblyName.Version.Minor);
            scopeReference.BuildNumber = checked((ushort)assemblyName.Version.Build);
            scopeReference.RevisionNumber = checked((ushort)assemblyName.Version.Revision);
            scopeReference.Flags = (AssemblyFlags)assemblyName.Flags & (AssemblyFlags.Retargetable | AssemblyFlags.ContentTypeMask);

            // References use a public key token instead of full public key.
            ImmutableArray<byte> publicKeyOrToken = assemblyName.PublicKeyOrToken;
            if ((assemblyName.Flags & AssemblyNameFlags.PublicKey) != 0)
            {
                // Use AssemblyName to convert PublicKey to PublicKeyToken to avoid calling crypto APIs directly
                AssemblyName an = new();
                an.SetPublicKey(ImmutableCollectionsMarshal.AsArray<byte>(publicKeyOrToken));
                publicKeyOrToken = ImmutableCollectionsMarshal.AsImmutableArray<byte>(an.GetPublicKeyToken());
            }
            scopeReference.PublicKeyOrToken = publicKeyOrToken.ToArray();
        }

        private sealed class SimpleAssemblyNameComparer : IEqualityComparer<AssemblyNameInfo>
        {
            public bool Equals(AssemblyNameInfo x, AssemblyNameInfo y)
            {
                return Equals(x.Name, y.Name);
            }

            public int GetHashCode(AssemblyNameInfo obj)
            {
                return obj.Name.GetHashCode();
            }
        }
    }
}