File: System\Reflection\Emit\SignatureHelper.cs
Web Access
Project: src\src\libraries\System.Reflection.Emit\src\System.Reflection.Emit.csproj (System.Reflection.Emit)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
 
namespace System.Reflection.Emit
{
    internal static class MetadataSignatureHelper
    {
        internal static BlobBuilder GetLocalSignature(List<LocalBuilder> locals, ModuleBuilderImpl module)
        {
            BlobBuilder localSignature = new();
            LocalVariablesEncoder encoder = new BlobEncoder(localSignature).LocalVariableSignature(locals.Count);
 
            foreach (LocalBuilder local in locals)
            {
                WriteSignatureForType(encoder.AddVariable().Type(local.LocalType.IsByRef, local.IsPinned),
                    local.LocalType.IsByRef ? local.LocalType.GetElementType()! : local.LocalType, module);
            }
 
            return localSignature;
        }
 
        internal static BlobBuilder GetFieldSignature(Type fieldType, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers, ModuleBuilderImpl module)
        {
            BlobBuilder fieldSignature = new();
            FieldTypeEncoder encoder = new BlobEncoder(fieldSignature).Field();
            WriteReturnTypeCustomModifiers(encoder.CustomModifiers(), requiredCustomModifiers, optionalCustomModifiers, module);
            WriteSignatureForType(encoder.Type(), fieldType, module);
 
            return fieldSignature;
        }
 
        internal static BlobBuilder GetConstructorSignature(ParameterInfo[]? parameters, ModuleBuilderImpl module)
        {
            BlobBuilder constructorSignature = new();
 
            parameters ??= Array.Empty<ParameterInfo>();
 
            new BlobEncoder(constructorSignature).
                MethodSignature(isInstanceMethod: true).
                Parameters(parameters.Length, out ReturnTypeEncoder retType, out ParametersEncoder parameterEncoder);
 
            retType.Void();
 
            WriteParametersSignature(module, Array.ConvertAll(parameters, p => p.ParameterType), parameterEncoder);
 
            return constructorSignature;
        }
 
        internal static BlobBuilder GetTypeSpecificationSignature(Type type, ModuleBuilderImpl module)
        {
            BlobBuilder typeSpecSignature = new();
            WriteSignatureForType(new BlobEncoder(typeSpecSignature).TypeSpecificationSignature(), type, module);
 
            return typeSpecSignature;
        }
 
        internal static BlobBuilder GetMethodSpecificationSignature(Type[] genericArguments, ModuleBuilderImpl module)
        {
            BlobBuilder methodSpecSignature = new();
            GenericTypeArgumentsEncoder encoder = new BlobEncoder(methodSpecSignature).MethodSpecificationSignature(genericArguments.Length);
 
            foreach (Type argument in genericArguments)
            {
                WriteSignatureForType(encoder.AddArgument(), argument, module);
            }
 
            return methodSpecSignature;
        }
 
        internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? parameters, Type? returnType, SignatureCallingConvention convention,
            int genParamCount = 0, bool isInstance = false, Type[]? optionalParameterTypes = null, Type[]? returnTypeRequiredModifiers = null,
            Type[]? returnTypeOptionalModifiers = null, Type[][]? parameterRequiredModifiers = null, Type[][]? parameterOptionalModifiers = null)
        {
            BlobBuilder methodSignature = new();
 
            int paramsLength = ((parameters == null) ? 0 : parameters.Length) + ((optionalParameterTypes == null) ? 0 : optionalParameterTypes.Length);
 
            new BlobEncoder(methodSignature).MethodSignature(convention, genParamCount, isInstance).
                    Parameters(paramsLength, out ReturnTypeEncoder retEncoder, out ParametersEncoder parEncoder);
 
            WriteReturnTypeCustomModifiers(retEncoder.CustomModifiers(), returnTypeRequiredModifiers, returnTypeOptionalModifiers, module);
 
            if (returnType != null && returnType != module.GetTypeFromCoreAssembly(CoreTypeId.Void))
            {
                WriteSignatureForType(retEncoder.Type(), returnType, module);
            }
            else
            {
                retEncoder.Void();
            }
 
            WriteParametersSignature(module, parameters, parEncoder, parameterRequiredModifiers, parameterOptionalModifiers);
 
            if (optionalParameterTypes != null && optionalParameterTypes.Length != 0)
            {
                WriteParametersSignature(module, optionalParameterTypes, parEncoder.StartVarArgs());
            }
 
            return methodSignature;
        }
 
        private static void WriteReturnTypeCustomModifiers(CustomModifiersEncoder encoder,
            Type[]? requiredModifiers, Type[]? optionalModifiers, ModuleBuilderImpl module)
        {
            if (requiredModifiers != null)
            {
                WriteCustomModifiers(encoder, requiredModifiers, isOptional: false, module);
            }
 
            if (optionalModifiers != null)
            {
                WriteCustomModifiers(encoder, optionalModifiers, isOptional: true, module);
            }
        }
 
        private static void WriteCustomModifiers(CustomModifiersEncoder encoder, Type[] customModifiers, bool isOptional, ModuleBuilderImpl module)
        {
            foreach (Type modifier in customModifiers)
            {
                encoder.AddModifier(module.GetTypeHandle(modifier), isOptional);
            }
        }
 
        private static void WriteParametersSignature(ModuleBuilderImpl module, Type[]? parameters,
            ParametersEncoder parameterEncoder, Type[][]? requiredModifiers = null, Type[][]? optionalModifiers = null)
        {
            if (parameters != null) // If parameters null, just keep the ParametersEncoder empty
            {
                for (int i = 0; i < parameters.Length; i++)
                {
                    ParameterTypeEncoder encoder = parameterEncoder.AddParameter();
 
                    if (requiredModifiers != null && requiredModifiers.Length > i && requiredModifiers[i] != null)
                    {
                        WriteCustomModifiers(encoder.CustomModifiers(), requiredModifiers[i], isOptional: false, module);
                    }
 
                    if (optionalModifiers != null && optionalModifiers.Length > i && optionalModifiers[i] != null)
                    {
                        WriteCustomModifiers(encoder.CustomModifiers(), optionalModifiers[i], isOptional: true, module);
                    }
 
                    WriteSignatureForType(encoder.Type(), parameters[i], module);
                }
            }
        }
 
        internal static BlobBuilder GetPropertySignature(PropertyBuilderImpl property, ModuleBuilderImpl module)
        {
            BlobBuilder propertySignature = new();
 
            new BlobEncoder(propertySignature).
                PropertySignature(isInstanceProperty: property.CallingConventions.HasFlag(CallingConventions.HasThis)).
                Parameters(property.ParameterTypes == null ? 0 : property.ParameterTypes.Length, out ReturnTypeEncoder retType, out ParametersEncoder paramEncoder);
 
            WriteReturnTypeCustomModifiers(retType.CustomModifiers(), property._returnTypeRequiredCustomModifiers, property._returnTypeOptionalCustomModifiers, module);
            WriteSignatureForType(retType.Type(), property.PropertyType, module);
            WriteParametersSignature(module, property.ParameterTypes, paramEncoder, property._parameterTypeRequiredCustomModifiers, property._parameterTypeOptionalCustomModifiers);
 
            return propertySignature;
        }
 
        private static void WriteSignatureForType(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module)
        {
            if (type.IsArray)
            {
                Type elementType = type.GetElementType()!;
                int rank = type.GetArrayRank();
                if (rank == 1)
                {
                    WriteSignatureForType(signature.SZArray(), elementType, module);
                }
                else
                {
                    signature.Array(out SignatureTypeEncoder elTypeSignature, out ArrayShapeEncoder arrayEncoder);
                    WriteSimpleSignature(elTypeSignature, elementType, module);
                    arrayEncoder.Shape(type.GetArrayRank(), ImmutableArray.Create<int>(), ImmutableArray.Create<int>(new int[rank]));
                }
            }
            else if (type.IsPointer)
            {
                WriteSignatureForType(signature.Pointer(), type.GetElementType()!, module);
            }
            else if (type.IsByRef)
            {
                signature.Builder.WriteByte((byte)SignatureTypeCode.ByReference);
                WriteSignatureForType(signature, type.GetElementType()!, module);
            }
            else if (type.IsGenericType)
            {
                Type[] genericArguments = type.GetGenericArguments();
 
                GenericTypeArgumentsEncoder encoder = signature.GenericInstantiation(
                    module.GetTypeHandle(type.GetGenericTypeDefinition()), genericArguments.Length, type.IsValueType);
                foreach (Type gType in genericArguments)
                {
                    if (gType.IsGenericMethodParameter)
                    {
                        encoder.AddArgument().GenericMethodTypeParameter(gType.GenericParameterPosition);
                    }
                    else if (gType.IsGenericParameter)
                    {
                        encoder.AddArgument().GenericTypeParameter(gType.GenericParameterPosition);
                    }
                    else
                    {
                        WriteSignatureForType(encoder.AddArgument(), gType, module);
                    }
                }
            }
            else if (type.IsGenericMethodParameter)
            {
                signature.GenericMethodTypeParameter(type.GenericParameterPosition);
            }
            else if (type.IsGenericParameter)
            {
                signature.GenericTypeParameter(type.GenericParameterPosition);
            }
            else
            {
                WriteSimpleSignature(signature, type, module);
            }
        }
 
        private static void WriteSimpleSignature(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module)
        {
            CoreTypeId? typeId = module.GetTypeIdFromCoreTypes(type);
 
            switch (typeId)
            {
                case CoreTypeId.Void:
                    signature.Builder.WriteByte((byte)SignatureTypeCode.Void);
                    break;
                case CoreTypeId.Boolean:
                    signature.Boolean();
                    break;
                case CoreTypeId.Byte:
                    signature.Byte();
                    break;
                case CoreTypeId.SByte:
                    signature.SByte();
                    break;
                case CoreTypeId.Char:
                    signature.Char();
                    break;
                case CoreTypeId.Int16:
                    signature.Int16();
                    break;
                case CoreTypeId.UInt16:
                    signature.UInt16();
                    break;
                case CoreTypeId.Int32:
                    signature.Int32();
                    break;
                case CoreTypeId.UInt32:
                    signature.UInt32();
                    break;
                case CoreTypeId.Int64:
                    signature.Int64();
                    break;
                case CoreTypeId.UInt64:
                    signature.UInt64();
                    break;
                case CoreTypeId.Single:
                    signature.Single();
                    break;
                case CoreTypeId.Double:
                    signature.Double();
                    break;
                case CoreTypeId.IntPtr:
                    signature.IntPtr();
                    break;
                case CoreTypeId.UIntPtr:
                    signature.UIntPtr();
                    break;
                case CoreTypeId.Object:
                    signature.Object();
                    break;
                case CoreTypeId.String:
                    signature.String();
                    break;
                case CoreTypeId.TypedReference:
                    signature.TypedReference();
                    break;
                default:    // handles null and all other types
                    EntityHandle typeHandle = module.GetTypeHandle(type);
                    signature.Type(typeHandle, type.IsValueType);
                    break;
            }
        }
    }
 
    // The order of the enum values should match with the ModuleBuilderImpl.s_coreTypes array elements order.
    internal enum CoreTypeId
    {
        Void,
        Object,
        Boolean,
        Char,
        SByte,
        Byte,
        Int16,
        UInt16,
        Int32,
        UInt32,
        Int64,
        UInt64,
        Single,
        Double,
        String,
        IntPtr,
        UIntPtr,
        TypedReference,
        ValueType
    }
}