File: src\System\Reflection\Emit\SignatureHelper.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Buffers.Binary;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
 
namespace System.Reflection.Emit
{
    public sealed class SignatureHelper
    {
        #region Consts Fields
        private const int NO_SIZE_IN_SIG = -1;
        #endregion
 
        #region Static Members
        public static SignatureHelper GetMethodSigHelper(Module? mod, Type? returnType, Type[]? parameterTypes)
        {
            return GetMethodSigHelper(mod, CallingConventions.Standard, returnType, null, null, parameterTypes, null, null);
        }
 
        public static SignatureHelper GetMethodSigHelper(Module? mod, CallingConventions callingConvention, Type? returnType)
        {
            return GetMethodSigHelper(mod, callingConvention, returnType, null, null, null, null, null);
        }
 
        internal static SignatureHelper GetMethodSpecSigHelper(Module? scope, Type[] inst)
        {
            SignatureHelper sigHelp = new SignatureHelper(scope, MdSigCallingConvention.GenericInst);
            sigHelp.AddData(inst.Length);
            foreach (Type t in inst)
                sigHelp.AddArgument(t);
            return sigHelp;
        }
 
        internal static SignatureHelper GetMethodSigHelper(
            Module? scope, CallingConventions callingConvention,
            Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers,
            Type[]? parameterTypes, Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers)
        {
            return GetMethodSigHelper(scope, callingConvention, 0, returnType, requiredReturnTypeCustomModifiers,
                optionalReturnTypeCustomModifiers, parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers);
        }
 
        internal static SignatureHelper GetMethodSigHelper(
            Module? scope, CallingConventions callingConvention, int cGenericParam,
            Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers,
            Type[]? parameterTypes, Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers)
        {
            SignatureHelper sigHelp;
            MdSigCallingConvention intCall;
 
            returnType ??= typeof(void);
 
            intCall = MdSigCallingConvention.Default;
 
            if ((callingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs)
                intCall = MdSigCallingConvention.Vararg;
 
            if (cGenericParam > 0)
            {
                intCall |= MdSigCallingConvention.Generic;
            }
 
            if ((callingConvention & CallingConventions.HasThis) == CallingConventions.HasThis)
                intCall |= MdSigCallingConvention.HasThis;
 
            sigHelp = new SignatureHelper(scope, intCall, cGenericParam, returnType,
                                            requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers);
            sigHelp.AddArguments(parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers);
 
            return sigHelp;
        }
 
        internal static SignatureHelper GetMethodSigHelper(Module? mod, CallingConvention unmanagedCallConv, Type? returnType)
        {
            MdSigCallingConvention intCall;
 
            returnType ??= typeof(void);
 
            if (unmanagedCallConv == CallingConvention.Cdecl)
            {
                intCall = MdSigCallingConvention.C;
            }
            else if (unmanagedCallConv == CallingConvention.StdCall || unmanagedCallConv == CallingConvention.Winapi)
            {
                intCall = MdSigCallingConvention.StdCall;
            }
            else if (unmanagedCallConv == CallingConvention.ThisCall)
            {
                intCall = MdSigCallingConvention.ThisCall;
            }
            else if (unmanagedCallConv == CallingConvention.FastCall)
            {
                intCall = MdSigCallingConvention.FastCall;
            }
            else
            {
                throw new ArgumentException(SR.Argument_UnknownUnmanagedCallConv, nameof(unmanagedCallConv));
            }
 
            return new SignatureHelper(mod, intCall, returnType, null, null);
        }
 
        public static SignatureHelper GetLocalVarSigHelper()
        {
            return GetLocalVarSigHelper(null);
        }
 
        public static SignatureHelper GetMethodSigHelper(CallingConventions callingConvention, Type? returnType)
        {
            return GetMethodSigHelper(null, callingConvention, returnType);
        }
 
        internal static SignatureHelper GetMethodSigHelper(CallingConvention unmanagedCallingConvention, Type? returnType)
        {
            return GetMethodSigHelper(null, unmanagedCallingConvention, returnType);
        }
 
        public static SignatureHelper GetLocalVarSigHelper(Module? mod)
        {
            return new SignatureHelper(mod, MdSigCallingConvention.LocalSig);
        }
 
        public static SignatureHelper GetFieldSigHelper(Module? mod)
        {
            return new SignatureHelper(mod, MdSigCallingConvention.Field);
        }
 
        public static SignatureHelper GetPropertySigHelper(Module? mod, Type? returnType, Type[]? parameterTypes)
        {
            return GetPropertySigHelper(mod, returnType, null, null, parameterTypes, null, null);
        }
 
        public static SignatureHelper GetPropertySigHelper(Module? mod,
            Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers,
            Type[]? parameterTypes, Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers)
        {
            return GetPropertySigHelper(mod, (CallingConventions)0, returnType, requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers,
                parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers);
        }
        public static SignatureHelper GetPropertySigHelper(Module? mod, CallingConventions callingConvention,
            Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers,
            Type[]? parameterTypes, Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers)
        {
            SignatureHelper sigHelp;
 
            returnType ??= typeof(void);
 
            MdSigCallingConvention intCall = MdSigCallingConvention.Property;
 
            if ((callingConvention & CallingConventions.HasThis) == CallingConventions.HasThis)
                intCall |= MdSigCallingConvention.HasThis;
 
            sigHelp = new SignatureHelper(mod, intCall,
                returnType, requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers);
            sigHelp.AddArguments(parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers);
 
            return sigHelp;
        }
 
        internal static SignatureHelper GetTypeSigToken(Module module, Type type)
        {
            ArgumentNullException.ThrowIfNull(module);
            ArgumentNullException.ThrowIfNull(type);
 
            return new SignatureHelper(module, type);
        }
        #endregion
 
        #region Private Data Members
        private byte[] m_signature;
        private int m_currSig; // index into m_signature buffer for next available byte
        private int m_sizeLoc; // index into m_signature buffer to put m_argCount (will be NO_SIZE_IN_SIG if no arg count is needed)
        private ModuleBuilder? m_module;
        private bool m_sigDone;
        private int m_argCount; // tracking number of arguments in the signature
        #endregion
 
        #region Constructor
        private SignatureHelper(Module? mod, MdSigCallingConvention callingConvention)
        {
            // Use this constructor to instantiate a local var sig  or Field where return type is not applied.
            Init(mod, callingConvention);
        }
 
        private SignatureHelper(Module? mod, MdSigCallingConvention callingConvention, int cGenericParameters,
            Type returnType, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
        {
            // Use this constructor to instantiate a any signatures that will require a return type.
            Init(mod, callingConvention, cGenericParameters);
 
            if (callingConvention == MdSigCallingConvention.Field)
                throw new ArgumentException(SR.Argument_BadFieldSig);
 
            AddOneArgTypeHelper(returnType, requiredCustomModifiers, optionalCustomModifiers);
        }
 
        private SignatureHelper(Module? mod, MdSigCallingConvention callingConvention,
            Type returnType, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
            : this(mod, callingConvention, 0, returnType, requiredCustomModifiers, optionalCustomModifiers)
        {
        }
 
        private SignatureHelper(Module mod, Type type)
        {
            Init(mod);
 
            AddOneArgTypeHelper(type);
        }
 
        [MemberNotNull(nameof(m_signature))]
        private void Init(Module? mod)
        {
            m_signature = new byte[32];
            m_currSig = 0;
            m_module = mod as ModuleBuilder;
            m_argCount = 0;
            m_sigDone = false;
            m_sizeLoc = NO_SIZE_IN_SIG;
 
            if (m_module == null && mod != null)
                throw new ArgumentException(SR.NotSupported_MustBeModuleBuilder);
 
            AssemblyBuilder.EnsureDynamicCodeSupported();
        }
 
        [MemberNotNull(nameof(m_signature))]
        private void Init(Module? mod, MdSigCallingConvention callingConvention)
        {
            Init(mod, callingConvention, 0);
        }
 
        [MemberNotNull(nameof(m_signature))]
        private void Init(Module? mod, MdSigCallingConvention callingConvention, int cGenericParam)
        {
            Init(mod);
 
            AddData((byte)callingConvention);
 
            if (callingConvention == MdSigCallingConvention.Field ||
                callingConvention == MdSigCallingConvention.GenericInst)
            {
                m_sizeLoc = NO_SIZE_IN_SIG;
            }
            else
            {
                if (cGenericParam > 0)
                    AddData(cGenericParam);
 
                m_sizeLoc = m_currSig++;
            }
        }
 
        #endregion
 
        #region Private Members
        private void AddOneArgTypeHelper(Type argument, bool pinned)
        {
            if (pinned)
                AddElementType(CorElementType.ELEMENT_TYPE_PINNED);
 
            AddOneArgTypeHelper(argument);
        }
 
        private void AddOneArgTypeHelper(Type clsArgument, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
        {
            // This function will not increase the argument count. It only fills in bytes
            // in the signature based on clsArgument. This helper is called for return type.
 
            Debug.Assert(clsArgument != null);
 
            if (optionalCustomModifiers != null)
            {
                for (int i = 0; i < optionalCustomModifiers.Length; i++)
                {
                    Type t = optionalCustomModifiers[i];
                    ArgumentNullException.ThrowIfNull(t, nameof(optionalCustomModifiers));
 
                    if (t.HasElementType)
                        throw new ArgumentException(SR.Argument_ArraysInvalid, nameof(optionalCustomModifiers));
 
                    if (t.ContainsGenericParameters)
                        throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(optionalCustomModifiers));
 
                    AddElementType(CorElementType.ELEMENT_TYPE_CMOD_OPT);
 
                    int token = m_module!.GetTypeMetadataToken(t);
                    Debug.Assert(!MetadataToken.IsNullToken(token));
                    AddToken(token);
                }
            }
 
            if (requiredCustomModifiers != null)
            {
                for (int i = 0; i < requiredCustomModifiers.Length; i++)
                {
                    Type t = requiredCustomModifiers[i];
 
                    ArgumentNullException.ThrowIfNull(t, nameof(requiredCustomModifiers));
 
                    if (t.HasElementType)
                        throw new ArgumentException(SR.Argument_ArraysInvalid, nameof(requiredCustomModifiers));
 
                    if (t.ContainsGenericParameters)
                        throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(requiredCustomModifiers));
 
                    AddElementType(CorElementType.ELEMENT_TYPE_CMOD_REQD);
 
                    int token = m_module!.GetTypeMetadataToken(t);
                    Debug.Assert(!MetadataToken.IsNullToken(token));
                    AddToken(token);
                }
            }
 
            AddOneArgTypeHelper(clsArgument);
        }
 
        private void AddOneArgTypeHelper(Type clsArgument) { AddOneArgTypeHelperWorker(clsArgument, false); }
        private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst)
        {
            if (clsArgument.IsGenericParameter)
            {
                if (clsArgument.DeclaringMethod != null)
                    AddElementType(CorElementType.ELEMENT_TYPE_MVAR);
                else
                    AddElementType(CorElementType.ELEMENT_TYPE_VAR);
 
                AddData(clsArgument.GenericParameterPosition);
            }
            else if (clsArgument.IsGenericType && (!clsArgument.IsGenericTypeDefinition || !lastWasGenericInst))
            {
                AddElementType(CorElementType.ELEMENT_TYPE_GENERICINST);
 
                AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true);
 
                Type[] args = clsArgument.GetGenericArguments();
 
                AddData(args.Length);
 
                foreach (Type t in args)
                    AddOneArgTypeHelper(t);
            }
            else if (clsArgument is RuntimeTypeBuilder clsBuilder)
            {
                int tkType;
 
                if (clsBuilder.Module.Equals(m_module))
                {
                    tkType = clsBuilder.TypeToken;
                }
                else
                {
                    tkType = m_module!.GetTypeMetadataToken(clsArgument);
                }
 
                if (clsArgument.IsValueType)
                {
                    InternalAddTypeToken(tkType, CorElementType.ELEMENT_TYPE_VALUETYPE);
                }
                else
                {
                    InternalAddTypeToken(tkType, CorElementType.ELEMENT_TYPE_CLASS);
                }
            }
            else if (clsArgument is RuntimeEnumBuilder reBuilder)
            {
                RuntimeTypeBuilder rtBuilder = reBuilder.m_typeBuilder;
                int tkType;
 
                if (rtBuilder.Module.Equals(m_module))
                {
                    tkType = rtBuilder.TypeToken;
                }
                else
                {
                    tkType = m_module!.GetTypeMetadataToken(clsArgument);
                }
 
                if (clsArgument.IsValueType)
                {
                    InternalAddTypeToken(tkType, CorElementType.ELEMENT_TYPE_VALUETYPE);
                }
                else
                {
                    InternalAddTypeToken(tkType, CorElementType.ELEMENT_TYPE_CLASS);
                }
            }
            else if (clsArgument.IsByRef)
            {
                AddElementType(CorElementType.ELEMENT_TYPE_BYREF);
                clsArgument = clsArgument.GetElementType()!;
                AddOneArgTypeHelper(clsArgument);
            }
            else if (clsArgument.IsPointer)
            {
                AddElementType(CorElementType.ELEMENT_TYPE_PTR);
                AddOneArgTypeHelper(clsArgument.GetElementType()!);
            }
            else if (clsArgument.IsArray)
            {
                if (clsArgument.IsSZArray)
                {
                    AddElementType(CorElementType.ELEMENT_TYPE_SZARRAY);
 
                    AddOneArgTypeHelper(clsArgument.GetElementType()!);
                }
                else
                {
                    AddElementType(CorElementType.ELEMENT_TYPE_ARRAY);
 
                    AddOneArgTypeHelper(clsArgument.GetElementType()!);
 
                    // put the rank information
                    int rank = clsArgument.GetArrayRank();
                    AddData(rank);     // rank
                    AddData(0);     // upper bounds
                    AddData(rank);  // lower bound
                    for (int i = 0; i < rank; i++)
                        AddData(0);
                }
            }
            else
            {
                CorElementType type = CorElementType.ELEMENT_TYPE_MAX;
 
                if (clsArgument is RuntimeType)
                {
                    type = ((RuntimeType)clsArgument).GetCorElementType();
 
                    // GetCorElementType returns CorElementType.ELEMENT_TYPE_CLASS for both object and string
                    if (type == CorElementType.ELEMENT_TYPE_CLASS)
                    {
                        if (clsArgument == typeof(object))
                            type = CorElementType.ELEMENT_TYPE_OBJECT;
                        else if (clsArgument == typeof(string))
                            type = CorElementType.ELEMENT_TYPE_STRING;
                    }
                }
 
                if (IsSimpleType(type))
                {
                    AddElementType(type);
                }
                else if (m_module == null)
                {
                    InternalAddRuntimeType(clsArgument);
                }
                else if (clsArgument.IsValueType)
                {
                    InternalAddTypeToken(m_module.GetTypeMetadataToken(clsArgument), CorElementType.ELEMENT_TYPE_VALUETYPE);
                }
                else
                {
                    InternalAddTypeToken(m_module.GetTypeMetadataToken(clsArgument), CorElementType.ELEMENT_TYPE_CLASS);
                }
            }
        }
 
        private void AddData(int data)
        {
            if (m_currSig + 4 > m_signature.Length)
            {
                m_signature = ExpandArray(m_signature);
            }
 
            if (data <= 0x7F)
            {
                m_signature[m_currSig++] = (byte)data;
            }
            else if (data <= 0x3F_FF)
            {
                BinaryPrimitives.WriteInt16BigEndian(m_signature.AsSpan(m_currSig), (short)(data | 0x80_00));
                m_currSig += 2;
            }
            else if (data <= 0x1F_FF_FF_FF)
            {
                BinaryPrimitives.WriteInt32BigEndian(m_signature.AsSpan(m_currSig), (int)(data | 0xC0_00_00_00));
                m_currSig += 4;
            }
            else
            {
                throw new ArgumentException(SR.Argument_LargeInteger);
            }
        }
 
        private void AddElementType(CorElementType cvt)
        {
            // Adds an element to the signature.  A managed representation of CorSigCompressElement
            if (m_currSig + 1 > m_signature.Length)
                m_signature = ExpandArray(m_signature);
 
            m_signature[m_currSig++] = (byte)cvt;
        }
 
        private void AddToken(int token)
        {
            // A managed representation of CompressToken
            // Pulls the token appart to get a rid, adds some appropriate bits
            // to the token and then adds this to the signature.
 
            int rid = (token & 0x00FFFFFF); // This is RidFromToken;
            MetadataTokenType type = (MetadataTokenType)(token & unchecked((int)0xFF000000)); // This is TypeFromToken;
 
            if (rid > 0x3FFFFFF)
            {
                // token is too big to be compressed
                throw new ArgumentException(SR.Argument_LargeInteger);
            }
 
            rid <<= 2;
 
            // TypeDef is encoded with low bits 00
            // TypeRef is encoded with low bits 01
            // TypeSpec is encoded with low bits 10
            if (type == MetadataTokenType.TypeRef)
            {
                // if type is mdtTypeRef
                rid |= 0x1;
            }
            else if (type == MetadataTokenType.TypeSpec)
            {
                // if type is mdtTypeSpec
                rid |= 0x2;
            }
 
            AddData(rid);
        }
 
        private void InternalAddTypeToken(int clsToken, CorElementType CorType)
        {
            // Add a type token into signature. CorType will be either CorElementType.ELEMENT_TYPE_CLASS or CorElementType.ELEMENT_TYPE_VALUETYPE
            AddElementType(CorType);
            AddToken(clsToken);
        }
 
        private unsafe void InternalAddRuntimeType(Type type)
        {
            // Add a runtime type into the signature.
 
            AddElementType(CorElementType.ELEMENT_TYPE_INTERNAL);
 
            IntPtr handle = type.TypeHandle.Value;
 
            // Internal types must have their pointer written into the signature directly (we don't
            // want to convert to little-endian format on big-endian machines because the value is
            // going to be extracted and used directly as a pointer (and only within this process)).
 
            if (m_currSig + sizeof(void*) > m_signature.Length)
                m_signature = ExpandArray(m_signature);
 
            byte* phandle = (byte*)&handle;
            for (int i = 0; i < sizeof(void*); i++)
                m_signature[m_currSig++] = phandle[i];
        }
 
        private static byte[] ExpandArray(byte[] inArray)
        {
            // Expand the signature buffer size
            return ExpandArray(inArray, inArray.Length * 2);
        }
 
        private static byte[] ExpandArray(byte[] inArray, int requiredLength)
        {
            // Expand the signature buffer size
 
            if (requiredLength < inArray.Length)
                requiredLength = inArray.Length * 2;
 
            byte[] outArray = new byte[requiredLength];
            Buffer.BlockCopy(inArray, 0, outArray, 0, inArray.Length);
            return outArray;
        }
 
        private void IncrementArgCounts()
        {
            if (m_sizeLoc == NO_SIZE_IN_SIG)
            {
                // We don't have a size if this is a field.
                return;
            }
 
            m_argCount++;
        }
 
        private void SetNumberOfSignatureElements(bool forceCopy)
        {
            // For most signatures, this will set the number of elements in a byte which we have reserved for it.
            // However, if we have a field signature, we don't set the length and return.
            // If we have a signature with more than 128 arguments, we can't just set the number of elements,
            // we actually have to allocate more space (e.g. shift everything in the array one or more spaces to the
            // right.  We do this by making a copy of the array and leaving the correct number of blanks.  This new
            // array is now set to be m_signature and we use the AddData method to set the number of elements properly.
            // The forceCopy argument can be used to force SetNumberOfSignatureElements to make a copy of
            // the array.  This is useful for GetSignature which promises to trim the array to be the correct size anyway.
 
            byte[] temp;
            int newSigSize;
            int currSigHolder = m_currSig;
 
            if (m_sizeLoc == NO_SIZE_IN_SIG)
                return;
 
            // If we have fewer than 128 arguments and we haven't been told to copy the
            // array, we can just set the appropriate bit and return.
            if (m_argCount < 0x80 && !forceCopy)
            {
                m_signature[m_sizeLoc] = (byte)m_argCount;
                return;
            }
 
            // We need to have more bytes for the size.  Figure out how many bytes here.
            // Since we need to copy anyway, we're just going to take the cost of doing a
            // new allocation.
            if (m_argCount < 0x80)
            {
                newSigSize = 1;
            }
            else if (m_argCount < 0x4000)
            {
                newSigSize = 2;
            }
            else
            {
                newSigSize = 4;
            }
 
            // Allocate the new array.
            temp = new byte[m_currSig + newSigSize - 1];
 
            // Copy the calling convention.  The calling convention is always just one byte
            // so we just copy that byte.  Then copy the rest of the array, shifting everything
            // to make room for the new number of elements.
            temp[0] = m_signature[0];
            Buffer.BlockCopy(m_signature, m_sizeLoc + 1, temp, m_sizeLoc + newSigSize, currSigHolder - (m_sizeLoc + 1));
            m_signature = temp;
 
            // Use the AddData method to add the number of elements appropriately compressed.
            m_currSig = m_sizeLoc;
            AddData(m_argCount);
            m_currSig = currSigHolder + (newSigSize - 1);
        }
 
        #endregion
 
        #region Internal Members
        internal int ArgumentCount => m_argCount;
 
        internal static bool IsSimpleType(CorElementType type)
        {
            if (type <= CorElementType.ELEMENT_TYPE_STRING)
                return true;
 
            if (type == CorElementType.ELEMENT_TYPE_TYPEDBYREF || type == CorElementType.ELEMENT_TYPE_I || type == CorElementType.ELEMENT_TYPE_U || type == CorElementType.ELEMENT_TYPE_OBJECT)
                return true;
 
            return false;
        }
 
        internal byte[] InternalGetSignature(out int length)
        {
            // An internal method to return the signature.  Does not trim the
            // array, but passes out the length of the array in an out parameter.
            // This is the actual array -- not a copy -- so the callee must agree
            // to not copy it.
            //
            // param length : an out param indicating the length of the array.
            // return : A reference to the internal ubyte array.
 
            if (!m_sigDone)
            {
                m_sigDone = true;
 
                // If we have more than 128 variables, we can't just set the length, we need
                // to compress it.  Unfortunately, this means that we need to copy the entire
                // array.
                SetNumberOfSignatureElements(false);
            }
 
            length = m_currSig;
            return m_signature;
        }
 
        internal byte[] InternalGetSignatureArray()
        {
            int argCount = m_argCount;
            int currSigLength = m_currSig;
            int newSigSize = currSigLength;
 
            // Allocate the new array.
            if (argCount < 0x7F)
                newSigSize++;
            else if (argCount < 0x3FFF)
                newSigSize += 2;
            else
                newSigSize += 4;
            byte[] temp = new byte[newSigSize];
 
            // copy the sig
            int sigCopyIndex = 0;
            // calling convention
            temp[sigCopyIndex++] = m_signature[0];
            // arg size
            if (argCount <= 0x7F)
                temp[sigCopyIndex++] = (byte)(argCount & 0xFF);
            else if (argCount <= 0x3FFF)
            {
                temp[sigCopyIndex++] = (byte)((argCount >> 8) | 0x80);
                temp[sigCopyIndex++] = (byte)(argCount & 0xFF);
            }
            else if (argCount <= 0x1FFFFFFF)
            {
                temp[sigCopyIndex++] = (byte)((argCount >> 24) | 0xC0);
                temp[sigCopyIndex++] = (byte)((argCount >> 16) & 0xFF);
                temp[sigCopyIndex++] = (byte)((argCount >> 8) & 0xFF);
                temp[sigCopyIndex++] = (byte)(argCount & 0xFF);
            }
            else
                throw new ArgumentException(SR.Argument_LargeInteger);
            // copy the sig part of the sig
            Buffer.BlockCopy(m_signature, 2, temp, sigCopyIndex, currSigLength - 2);
            // mark the end of sig
            temp[newSigSize - 1] = (byte)CorElementType.ELEMENT_TYPE_END;
 
            return temp;
        }
 
        internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
        {
            IncrementArgCounts();
 
            Debug.Assert(clsArgument != null);
 
            if (optionalCustomModifiers != null)
            {
                for (int i = 0; i < optionalCustomModifiers.Length; i++)
                {
                    Type t = optionalCustomModifiers[i];
 
                    if (t is not RuntimeType rtType)
                        throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(optionalCustomModifiers));
 
                    if (t.HasElementType)
                        throw new ArgumentException(SR.Argument_ArraysInvalid, nameof(optionalCustomModifiers));
 
                    if (t.ContainsGenericParameters)
                        throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(optionalCustomModifiers));
 
                    AddElementType(CorElementType.ELEMENT_TYPE_CMOD_OPT);
 
                    int token = dynamicScope.GetTokenFor(rtType.TypeHandle);
                    Debug.Assert(!MetadataToken.IsNullToken(token));
                    AddToken(token);
                }
            }
 
            if (requiredCustomModifiers != null)
            {
                for (int i = 0; i < requiredCustomModifiers.Length; i++)
                {
                    Type t = requiredCustomModifiers[i];
 
                    if (t is not RuntimeType rtType)
                        throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(requiredCustomModifiers));
 
                    if (t.HasElementType)
                        throw new ArgumentException(SR.Argument_ArraysInvalid, nameof(requiredCustomModifiers));
 
                    if (t.ContainsGenericParameters)
                        throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(requiredCustomModifiers));
 
                    AddElementType(CorElementType.ELEMENT_TYPE_CMOD_REQD);
 
                    int token = dynamicScope.GetTokenFor(rtType.TypeHandle);
                    Debug.Assert(!MetadataToken.IsNullToken(token));
                    AddToken(token);
                }
            }
 
            AddOneArgTypeHelper(clsArgument);
        }
 
        #endregion
 
        #region Public Methods
        public void AddArgument(Type clsArgument)
        {
            AddArgument(clsArgument, null, null);
        }
 
        public void AddArgument(Type argument, bool pinned)
        {
            ArgumentNullException.ThrowIfNull(argument);
 
            IncrementArgCounts();
            AddOneArgTypeHelper(argument, pinned);
        }
 
        public void AddArguments(Type[]? arguments, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers)
        {
            if (requiredCustomModifiers != null && (arguments == null || requiredCustomModifiers.Length != arguments.Length))
                throw new ArgumentException(SR.Format(SR.Argument_MismatchedArrays, nameof(requiredCustomModifiers), nameof(arguments)));
 
            if (optionalCustomModifiers != null && (arguments == null || optionalCustomModifiers.Length != arguments.Length))
                throw new ArgumentException(SR.Format(SR.Argument_MismatchedArrays, nameof(optionalCustomModifiers), nameof(arguments)));
 
            if (arguments != null)
            {
                for (int i = 0; i < arguments.Length; i++)
                {
                    AddArgument(arguments[i], requiredCustomModifiers?[i], optionalCustomModifiers?[i]);
                }
            }
        }
 
        public void AddArgument(Type argument, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
        {
            if (m_sigDone)
                throw new ArgumentException(SR.Argument_SigIsFinalized);
 
            ArgumentNullException.ThrowIfNull(argument);
 
            IncrementArgCounts();
 
            // Add an argument to the signature. Takes a Type and determines whether it
            // is one of the primitive types of which we have special knowledge or a more
            // general class.  In the former case, we only add the appropriate short cut encoding,
            // otherwise we will calculate proper description for the type.
            AddOneArgTypeHelper(argument, requiredCustomModifiers, optionalCustomModifiers);
        }
 
        public void AddSentinel()
        {
            AddElementType(CorElementType.ELEMENT_TYPE_SENTINEL);
        }
 
        public override bool Equals(object? obj) =>
            obj is SignatureHelper other &&
            other.m_module!.Equals(m_module) &&
            other.m_currSig == m_currSig &&
            other.m_sizeLoc == m_sizeLoc &&
            other.m_sigDone == m_sigDone &&
            m_signature.AsSpan(0, m_currSig).SequenceEqual(other.m_signature.AsSpan(0, m_currSig));
 
        public override int GetHashCode()
        {
            // Start the hash code with the hash code of the module and the values of the member variables.
            int HashCode = m_module!.GetHashCode() + m_currSig + m_sizeLoc;
 
            // Add one if the sig is done.
            if (m_sigDone)
                HashCode++;
 
            // Then add the hash code of all the arguments.
            for (int i = 0; i < m_currSig; i++)
                HashCode += m_signature[i].GetHashCode();
 
            return HashCode;
        }
 
        public byte[] GetSignature()
        {
            return GetSignature(false);
        }
 
        internal byte[] GetSignature(bool appendEndOfSig)
        {
            // Chops the internal signature to the appropriate length.  Adds the
            // end token to the signature and marks the signature as finished so that
            // no further tokens can be added. Return the full signature in a trimmed array.
            if (!m_sigDone)
            {
                if (appendEndOfSig)
                    AddElementType(CorElementType.ELEMENT_TYPE_END);
                SetNumberOfSignatureElements(true);
                m_sigDone = true;
            }
 
            // This case will only happen if the user got the signature through
            // InternalGetSignature first and then called GetSignature.
            if (m_signature.Length > m_currSig)
            {
                byte[] temp = new byte[m_currSig];
                Array.Copy(m_signature, temp, m_currSig);
                m_signature = temp;
            }
 
            return m_signature;
        }
 
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Length: ").Append(m_currSig).AppendLine();
 
            if (m_sizeLoc != -1)
            {
                sb.Append("Arguments: ").Append(m_signature[m_sizeLoc]).AppendLine();
            }
            else
            {
                sb.AppendLine("Field Signature");
            }
 
            sb.AppendLine("Signature: ");
            for (int i = 0; i <= m_currSig; i++)
            {
                sb.Append(m_signature[i]).Append("  ");
            }
 
            sb.AppendLine();
            return sb.ToString();
        }
 
        #endregion
    }
}