File: ILCompiler\Metadata\Transform.Method.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 MethodAttributes = System.Reflection.MethodAttributes;
using MethodImplAttributes = System.Reflection.MethodImplAttributes;
using SignatureCallingConvention = Internal.Metadata.NativeFormat.SignatureCallingConvention;

namespace ILCompiler.Metadata
{
    internal partial class Transform<TPolicy>
    {
        internal EntityMap<Cts.MethodDesc, MetadataRecord> _methods
            = new EntityMap<Cts.MethodDesc, MetadataRecord>(EqualityComparer<Cts.MethodDesc>.Default);

        private Action<Cts.MethodDesc, Method> _initMethodDef;
        private Action<Cts.MethodDesc, MemberReference> _initMethodRef;
        private Action<Cts.MethodDesc, MethodInstantiation> _initMethodInst;

        public override MetadataRecord HandleQualifiedMethod(Cts.MethodDesc method)
        {
            MetadataRecord rec;

            if (method is Cts.InstantiatedMethod)
            {
                rec = HandleMethodInstantiation(method);
            }
            else if (method.IsTypicalMethodDefinition && _policy.GeneratesMetadata(method))
            {
                rec = new QualifiedMethod
                {
                    EnclosingType = (TypeDefinition)HandleType(method.OwningType),
                    Method = HandleMethodDefinition(method),
                };
            }
            else
            {
                rec = HandleMethodReference(method);
            }

            Debug.Assert(rec is QualifiedMethod || rec is MemberReference || rec is MethodInstantiation);

            return rec;
        }

        private Method HandleMethodDefinition(Cts.MethodDesc method)
        {
            Debug.Assert(method.IsTypicalMethodDefinition);
            Debug.Assert(_policy.GeneratesMetadata(method));
            return (Method)_methods.GetOrCreate(method, _initMethodDef ??= InitializeMethodDefinition);
        }

        private void InitializeMethodDefinition(Cts.MethodDesc entity, Method record)
        {
            record.Name = HandleString(entity.GetName());

            if (entity.HasInstantiation)
            {
                record.GenericParameters.Capacity = entity.Instantiation.Length;
                foreach (var p in entity.Instantiation)
                    record.GenericParameters.Add(HandleGenericParameter((Cts.GenericParameterDesc)p));
            }

            var ecmaEntity = entity as Cts.Ecma.EcmaMethod;
            if (ecmaEntity != null)
            {
                Ecma.MetadataReader reader = ecmaEntity.MetadataReader;
                Ecma.MethodDefinition methodDef = reader.GetMethodDefinition(ecmaEntity.Handle);
                Ecma.ParameterHandleCollection paramHandles = methodDef.GetParameters();

                Cts.MethodSignature sig = entity.Signature;
                record.Signature = sig.HasEmbeddedSignatureData
                    ? HandleMethodSignature(ecmaEntity.Module, methodDef.Signature)
                    : HandleMethodSignature(entity.Signature);

                record.Parameters.Capacity = paramHandles.Count;
                foreach (var paramHandle in paramHandles)
                {
                    if (!_policy.GeneratesMetadata(ecmaEntity.Module, paramHandle))
                        continue;

                    Ecma.Parameter param = reader.GetParameter(paramHandle);
                    Parameter paramRecord = new Parameter
                    {
                        Flags = param.Attributes,
                        Name = HandleString(reader.GetString(param.Name)),
                        Sequence = checked((ushort)param.SequenceNumber)
                    };

                    Ecma.ConstantHandle defaultValue = param.GetDefaultValue();
                    if (!defaultValue.IsNil)
                    {
                        paramRecord.DefaultValue = HandleConstant(ecmaEntity.Module, defaultValue);
                    }

                    Ecma.CustomAttributeHandleCollection paramAttributes = param.GetCustomAttributes();
                    if (paramAttributes.Count > 0)
                    {
                        paramRecord.CustomAttributes = HandleCustomAttributes(ecmaEntity.Module, paramAttributes);
                    }

                    record.Parameters.Add(paramRecord);
                }

                Ecma.CustomAttributeHandleCollection attributes = methodDef.GetCustomAttributes();
                if (attributes.Count > 0)
                {
                    record.CustomAttributes = HandleCustomAttributes(ecmaEntity.Module, attributes);
                }
            }
            else
            {
                throw new NotImplementedException();
            }

            record.Flags = GetMethodAttributes(entity);
            record.ImplFlags = GetMethodImplAttributes(entity);

            //TODO: RVA
        }

        private MemberReference HandleMethodReference(Cts.MethodDesc method)
        {
            Debug.Assert(method.IsMethodDefinition);
            return (MemberReference)_methods.GetOrCreate(method, _initMethodRef ??= InitializeMethodReference);
        }

        private void InitializeMethodReference(Cts.MethodDesc entity, MemberReference record)
        {
            record.Name = HandleString(entity.GetName());
            record.Parent = HandleType(entity.OwningType);
            record.Signature = HandleMethodSignature(entity.GetTypicalMethodDefinition().Signature);
        }

        private MethodInstantiation HandleMethodInstantiation(Cts.MethodDesc method)
        {
            return (MethodInstantiation)_methods.GetOrCreate(method, _initMethodInst ??= InitializeMethodInstantiation);
        }

        private void InitializeMethodInstantiation(Cts.MethodDesc entity, MethodInstantiation record)
        {
            Cts.InstantiatedMethod instantiation = (Cts.InstantiatedMethod)entity;
            record.Method = HandleQualifiedMethod(instantiation.GetMethodDefinition());
            record.GenericTypeArguments.Capacity = instantiation.Instantiation.Length;
            foreach (Cts.TypeDesc typeArgument in instantiation.Instantiation)
            {
                record.GenericTypeArguments.Add(HandleType(typeArgument));
            }
        }

        public override MethodSignature HandleMethodSignature(Cts.MethodSignature signature)
        {
            // TODO: if Cts.MethodSignature implements Equals/GetHashCode, we could enable pooling here.

            var result = new MethodSignature
            {
                CallingConvention = GetSignatureCallingConvention(signature),
                GenericParameterCount = signature.GenericParameterCount,
                ReturnType = HandleType(signature.ReturnType),
                // TODO-NICE: VarArgParameters
            };

            result.Parameters.Capacity = signature.Length;
            for (int i = 0; i < signature.Length; i++)
            {
                result.Parameters.Add(HandleType(signature[i]));
            }

            return result;
        }

        private MethodSignature HandleMethodSignature(Cts.Ecma.EcmaModule module, Ecma.BlobHandle sigBlobHandle)
        {
            Ecma.BlobReader blobReader = module.MetadataReader.GetBlobReader(sigBlobHandle);
            return HandleMethodSignature(module, ref blobReader);
        }

        private MethodSignature HandleMethodSignature(Cts.Ecma.EcmaModule module, ref Ecma.BlobReader blobReader)
        {
            Ecma.SignatureHeader header = blobReader.ReadSignatureHeader();

            int arity = header.IsGeneric ? blobReader.ReadCompressedInteger() : 0;

            int count = blobReader.ReadCompressedInteger();

            var result = new MethodSignature
            {
                CallingConvention = GetSignatureCallingConvention(header),
                GenericParameterCount = arity,
                ReturnType = HandleType(module, ref blobReader),
                // TODO-NICE: VarArgParameters
            };

            result.Parameters.Capacity = count;
            for (int i = 0; i < count; i++)
            {
                result.Parameters.Add(HandleType(module, ref blobReader));
            }

            return result;
        }

        private static MethodAttributes GetMethodAttributes(Cts.MethodDesc method)
        {
            var ecmaMethod = method as Cts.Ecma.EcmaMethod;
            if (ecmaMethod != null)
            {
                Ecma.MetadataReader reader = ecmaMethod.MetadataReader;
                Ecma.MethodDefinition methodDef = reader.GetMethodDefinition(ecmaMethod.Handle);
                return methodDef.Attributes;
            }
            else
                throw new NotImplementedException();
        }

        private static MethodImplAttributes GetMethodImplAttributes(Cts.MethodDesc method)
        {
            var ecmaMethod = method as Cts.Ecma.EcmaMethod;
            if (ecmaMethod != null)
            {
                Ecma.MetadataReader reader = ecmaMethod.MetadataReader;
                Ecma.MethodDefinition methodDef = reader.GetMethodDefinition(ecmaMethod.Handle);
                return methodDef.ImplAttributes;
            }
            else
                throw new NotImplementedException();
        }

        private static SignatureCallingConvention GetSignatureCallingConvention(Cts.MethodSignature signature)
        {
            Debug.Assert((int)Cts.MethodSignatureFlags.UnmanagedCallingConventionCdecl == (int)SignatureCallingConvention.Cdecl);
            Debug.Assert((int)Cts.MethodSignatureFlags.UnmanagedCallingConventionThisCall == (int)SignatureCallingConvention.ThisCall);
            Debug.Assert((int)Cts.MethodSignatureFlags.UnmanagedCallingConventionMask == (int)SignatureCallingConvention.UnmanagedCallingConventionMask);
            SignatureCallingConvention callingConvention = (SignatureCallingConvention)(signature.Flags & Cts.MethodSignatureFlags.UnmanagedCallingConventionMask);
            if ((signature.Flags & Cts.MethodSignatureFlags.Static) == 0)
            {
                callingConvention |= SignatureCallingConvention.HasThis;
            }
            if ((signature.Flags & Cts.MethodSignatureFlags.ExplicitThis) != 0)
            {
                callingConvention |= SignatureCallingConvention.ExplicitThis;
            }
            return callingConvention;
        }

        private static SignatureCallingConvention GetSignatureCallingConvention(Ecma.SignatureHeader signature)
        {
            Debug.Assert((int)Ecma.SignatureCallingConvention.CDecl == (int)SignatureCallingConvention.Cdecl);
            Debug.Assert((int)Ecma.SignatureCallingConvention.ThisCall == (int)SignatureCallingConvention.ThisCall);
            Debug.Assert((int)Ecma.SignatureCallingConvention.Unmanaged == (int)SignatureCallingConvention.Unmanaged);
            SignatureCallingConvention callingConvention = (SignatureCallingConvention)(signature.CallingConvention);
            if (signature.IsInstance)
            {
                callingConvention |= SignatureCallingConvention.HasThis;
            }
            return callingConvention;
        }
    }
}