// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//
// siginfo.hpp
//


#ifndef _H_SIGINFO
#define _H_SIGINFO


#include "util.hpp"
#include "vars.hpp"
#include "clsload.hpp"
#include "sigparser.h"
#include "zapsig.h"
#include "threads.h"
#include "corinfo.h"

#include "eecontract.h"
#include "typectxt.h"

//---------------------------------------------------------------------------------------
// These macros define how arguments are mapped to the stack in the managed calling convention.
// We assume to be walking a method's signature left-to-right, in the virtual calling convention.
// See MethodDesc::Call for details on this virtual calling convention.
// These macros tell us whether the arguments we see as we proceed with the signature walk are mapped
//   to increasing or decreasing stack addresses. This is valid only for arguments that go on the stack.
//---------------------------------------------------------------------------------------
#if defined(TARGET_X86)
#define STACK_GROWS_DOWN_ON_ARGS_WALK
#else
#define STACK_GROWS_UP_ON_ARGS_WALK
#endif

BOOL IsTypeRefOrDef(LPCSTR szClassName, Module *pModule, mdToken token);

struct ElementTypeInfo {
#ifdef _DEBUG
    int            m_elementType;
#endif
    int            m_cbSize;
    CorInfoGCType  m_gc         : 3;
    int            m_enregister : 1;
};
extern const ElementTypeInfo gElementTypeInfo[];

unsigned GetSizeForCorElementType(CorElementType etyp);

class SigBuilder;
class ArgDestination;

typedef const struct HardCodedMetaSig *LPHARDCODEDMETASIG;

//---------------------------------------------------------------------------------------

struct ScanContext;
typedef void promote_func(PTR_PTR_Object, ScanContext*, uint32_t);
typedef void promote_carefully_func(promote_func*, PTR_PTR_Object, ScanContext*, uint32_t);

void PromoteCarefully(promote_func   fn,
                      PTR_PTR_Object obj,
                      ScanContext*   sc,
                      uint32_t       flags = GC_CALL_INTERIOR);

class LoaderAllocator;
void GcReportLoaderAllocator(promote_func* fn, ScanContext* sc, LoaderAllocator *pLoaderAllocator);

//---------------------------------------------------------------------------------------
//
// Encapsulates how compressed integers and typeref tokens are encoded into
// a bytestream.
//
// As you use this class please understand the implicit normalizations
// on the CorElementType's returned by the various methods, especially
// for variable types (e.g. !0 in generic signatures), string types
// (i.e. E_T_STRING), object types (E_T_OBJECT), constructed types
// (e.g. List<int>) and enums.
//
class SigPointer : public SigParser
{
    friend class MetaSig;

public:
    // Constructor.
    SigPointer() { LIMITED_METHOD_DAC_CONTRACT; }

    // Copy constructor.
    SigPointer(const SigPointer & sig) : SigParser(sig)
    {
        WRAPPER_NO_CONTRACT;
    }

    SigPointer(const SigParser & sig) : SigParser(sig)
    {
        WRAPPER_NO_CONTRACT;
    }

    // Signature from a pointer. INSECURE!!!
    // WARNING: Should not be used as it is insecure, because we do not have size of the signature and
    // therefore we can read behind the end of buffer/file.
    FORCEINLINE
    SigPointer(PCCOR_SIGNATURE ptr) : SigParser(ptr)
    {
        WRAPPER_NO_CONTRACT;
    }

    // Signature from a pointer and size.
    FORCEINLINE
    SigPointer(PCCOR_SIGNATURE ptr, DWORD len) : SigParser(ptr, len)
    {
        WRAPPER_NO_CONTRACT;
    }


    //=========================================================================
    // The RAW interface for reading signatures.  You see exactly the signature,
    // apart from custom modifiers which for historical reasons tend to get eaten.
    //
    // DO NOT USE THESE METHODS UNLESS YOU'RE TOTALLY SURE YOU WANT
    // THE RAW signature.  You nearly always want GetElemTypeClosed() or
    // PeekElemTypeClosed() or one of the MetaSig functions.  See the notes above.
    // These functions will return E_T_INTERNAL, E_T_VAR, E_T_MVAR and such
    // so the caller must be able to deal with those
    //=========================================================================


        void ConvertToInternalExactlyOne(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
        void ConvertToInternalSignature(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);


    //=========================================================================
    // The CLOSED interface for reading signatures.  With the following
    // methods you see the signature "as if" all type variables are
    // replaced by the given instantiations.  However, no type loads happen.
    //
    // In general this is what you want to use if the signature may include
    // generic type variables.  Even if you know it doesn't you can always
    // pass in NULL for the instantiations and put a comment to that effect.
    //
    // The CLOSED api also hides E_T_INTERNAL by return E_T_CLASS or E_T_VALUETYPE
    // appropriately (as directed by the TypeHandle following E_T_INTERNAL)
    //=========================================================================

        // The CorElementTypes returned correspond
        // to those returned by TypeHandle::GetSignatureCorElementType.
        CorElementType PeekElemTypeClosed(Module *pModule, const SigTypeContext *pTypeContext) const;

        //------------------------------------------------------------------------
        // Fetch the token for a CLASS, VALUETYPE or GENRICINST, or a type
        // variable instantiatied to be one of these, taking into account
        // the given instantiations.
        //
        // SigPointer should be in a position that satisfies
        //  ptr.PeekElemTypeClosed(pTypeContext) = ELEMENT_TYPE_VALUETYPE
        //
        // A type ref or def is returned.  For an instantiated generic struct
        // this will return the token for the generic class, e.g. for a signature
        // for "struct Pair<int,int>" this will return a token for "Pair".
        //
        // The token will only make sense in the context of the module where
        // the signature occurs.
        //
        // WARNING: This api will return a mdTokenNil for a E_T_VALUETYPE obtained
        //          from a E_T_INTERNAL, as the token is meaningless in that case
        //          Users of this api must be prepared to deal with a null token
        //------------------------------------------------------------------------
        mdTypeRef PeekValueTypeTokenClosed(Module *pModule, const SigTypeContext *pTypeContext, Module **ppModuleOfToken) const;


    //=========================================================================
    // The INTERNAL-NORMALIZED interface for reading signatures.  You see
    // information concerning the signature, but taking into account normalizations
    // performed for layout of data, e.g. enums and one-field VCs.
    //=========================================================================

        // The CorElementTypes returned correspond
        // to those returned by TypeHandle::GetInternalCorElementType.
        CorElementType PeekElemTypeNormalized(Module* pModule, const SigTypeContext *pTypeContext, TypeHandle * pthValueType = NULL) const;

        //------------------------------------------------------------------------
        // Assumes that the SigPointer points to the start of an element type.
        // Returns size of that element in bytes. This is the minimum size that a
        // field of this type would occupy inside an object.
        //------------------------------------------------------------------------
        UINT SizeOf(Module* pModule, const SigTypeContext *pTypeContext, TypeHandle* pTypeHandle) const;

private:

        // SigPointer should be just after E_T_VAR or E_T_MVAR
        TypeHandle GetTypeVariable(CorElementType et,const SigTypeContext *pTypeContext);
        TypeHandle GetTypeVariableThrowing(ModuleBase *pModule,
                                           CorElementType et,
                                           ClassLoader::LoadTypesFlag fLoadTypes,
                                           const SigTypeContext *pTypeContext);

        // Parse type following E_T_GENERICINST
        TypeHandle GetGenericInstType(ModuleBase *        pModule,
                                      ClassLoader::LoadTypesFlag = ClassLoader::LoadTypes,
                                      ClassLoadLevel level = CLASS_LOADED,
                                      const ZapSig::Context *pZapSigContext = NULL);

public:

        //------------------------------------------------------------------------
        // Assuming that the SigPointer points the start if an element type.
        // Use SigTypeContext to fill in any  type parameters
        //
        // Also advance the pointer to after the element type.
        //------------------------------------------------------------------------

        // OBSOLETE - Use GetTypeHandleThrowing()
        TypeHandle GetTypeHandleNT(Module* pModule,
                                   const SigTypeContext *pTypeContext) const;

        struct HandleRecursiveGenericsForFieldLayoutLoad
        {
            Module* pModuleWithTokenToAvoidIfPossible;
            mdToken tkTypeDefToAvoidIfPossible;
        };

        // pTypeContext indicates how to instantiate any generic type parameters we come
        // However, first we implicitly apply the substitution pSubst to the metadata if pSubst is supplied.
        // That is, if the metadata contains a type variable "!0" then we first look up
        // !0 in pSubst to produce another item of metadata and continue processing.
        // If pSubst is empty then we look up !0 in the pTypeContext to produce a final
        // type handle.  If any of these are out of range we throw an exception.
        //
        // The level is the level to which the result type will be loaded (see classloadlevel.h)
        // If dropGenericArgumentLevel is TRUE, and the metadata represents an instantiated generic type,
        // then generic arguments to the generic type will be loaded one level lower. (This is used by the
        // class loader to avoid looping on definitions such as class C : D<C>)
        //
        // If dropGenericArgumentLevel is TRUE and
        // level=CLASS_LOAD_APPROXPARENTS, then the instantiated
        // generic type is "approximated" in the following way:
        // - for generic interfaces, the generic type (uninstantiated) is returned
        // - for other generic instantiations, System.Object is used in place of any reference types
        //   occurring in the type arguments
        // This semantics is used by the class loader to load tricky recursive definitions in phases
        // (e.g. class C : D<C>, or struct S : I<S>)
        TypeHandle GetTypeHandleThrowing(ModuleBase* pModule,
                                         const SigTypeContext *pTypeContext,
                                         ClassLoader::LoadTypesFlag fLoadTypes = ClassLoader::LoadTypes,
                                         ClassLoadLevel level = CLASS_LOADED,
                                         BOOL dropGenericArgumentLevel = FALSE,
                                         const Substitution *pSubst = NULL,
                                         const ZapSig::Context *pZapSigContext = NULL,
                                         MethodTable *pMTInterfaceMapOwner = NULL,
                                         HandleRecursiveGenericsForFieldLayoutLoad *pRecursiveFieldGenericHandling = NULL
                                         ) const;

public:
        //------------------------------------------------------------------------
        // Tests if the element type is a System.String. Accepts
        // either ELEMENT_TYPE_STRING or ELEMENT_TYPE_CLASS encoding.
        //------------------------------------------------------------------------
        BOOL IsStringType(Module* pModule, const SigTypeContext *pTypeContext) const;
        BOOL IsStringTypeThrowing(Module* pModule, const SigTypeContext *pTypeContext) const;

private:
        BOOL IsStringTypeHelper(Module* pModule, const SigTypeContext* pTypeContext, BOOL fThrow) const;

public:


        //------------------------------------------------------------------------
        // Tests if the element class name is szClassName.
        //------------------------------------------------------------------------
        BOOL IsClass(Module* pModule, LPCUTF8 szClassName, const SigTypeContext *pTypeContext = NULL) const;
        BOOL IsClassThrowing(Module* pModule, LPCUTF8 szClassName, const SigTypeContext *pTypeContext = NULL) const;

private:
        BOOL IsClassHelper(Module* pModule, LPCUTF8 szClassName, const SigTypeContext* pTypeContext, BOOL fThrow) const;

public:
        //------------------------------------------------------------------------
        // Tests for the existence of a custom modifier
        //------------------------------------------------------------------------
        BOOL HasCustomModifier(Module *pModule, LPCSTR szModName, CorElementType cmodtype) const;

        //------------------------------------------------------------------------
        // Tests for ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUETYPE followed by a TypeDef,
        // and returns the TypeDef
        //------------------------------------------------------------------------
        BOOL IsTypeDef(mdTypeDef* pTypeDef) const;

};  // class SigPointer

// forward declarations needed for the friends declared in Signature
struct FrameInfo;
struct VASigCookie;
#if defined(DACCESS_COMPILE)
class  DacDbiInterfaceImpl;
#endif // DACCESS_COMPILE

//---------------------------------------------------------------------------------------
//
// Currently, PCCOR_SIGNATURE is used all over the runtime to represent a signature, which is just
// an array of bytes.  The problem with PCCOR_SIGNATURE is that it doesn't tell you the length of
// the signature (i.e. the number of bytes in the array).  This is particularly troublesome for DAC,
// which needs to know how much memory to grab from out of process.  This class is an encapsulation
// over PCCOR_SIGNATURE AND the length of the signature it points to.
//
// Notes:
//    This class is meant to be read-only.  Moreover, preferrably we should never read the raw
//    PCCOR_SIGNATURE pointer directly, but there are likely some cases where it is inevitable.
//    We should keep these to a minimum.
//
//    We should move over to Signature instead of PCCOR_SIGNATURE.
//
//    To get a Signature, you can create one yourself by using a constructor.  However, it's recommended
//    that you check whether the Signature should be constructed at a lower level.  For example, instead of
//    creating a Signature in FramedMethodFrame::PromoteCallerStackWalker(), we should add a member function
//    to MethodDesc to return a Signature.
//

class Signature
{
public:
    // create an empty Signature
    Signature();

    // this is the primary constructor
    Signature(PCCOR_SIGNATURE pSig,
              DWORD           cbSig);

    // check whether the signature is empty, i.e. have a NULL PCCOR_SIGNATURE
    BOOL IsEmpty() const;

    // create a SigParser from the signature
    SigParser CreateSigParser() const;

    // create a SigPointer from the signature
    SigPointer CreateSigPointer() const;

    // pretty print the signature
    void PrettyPrint(const CHAR *        pszMethodName,
                     CQuickBytes *       pqbOut,
                     IMDInternalImport * pIMDI) const;

    // retrieve the raw PCCOR_SIGNATURE pointer
    PCCOR_SIGNATURE GetRawSig() const;

    // retrieve the length of the signature
    DWORD           GetRawSigLen() const;

private:
    PCCOR_SIGNATURE m_pSig;
    DWORD           m_cbSig;
};  // class Signature


#ifdef _DEBUG
#define MAX_CACHED_SIG_SIZE     3       // To excercize non-cached code path
#else
#define MAX_CACHED_SIG_SIZE     15
#endif


//---------------------------------------------------------------------------------------
//
// A substitution represents the composition of several formal type instantiations
// It is used when matching formal signatures across the inheritance hierarchy.
//
// It has the form of a linked list:
//   [mod_1, <inst_1>] ->
//   [mod_2, <inst_2>] ->
//   ...
//   [mod_n, <inst_n>]
//
// Here the types in <inst_1> must be resolved in the scope of module mod_1 but
// may contain type variables instantiated by <inst_2>
// ...
// and the types in <inst_(n-1)> must be resolved in the scope of mould mod_(n-1) but
// may contain type variables instantiated by <inst_n>
//
// Any type variables in <inst_n> are treated as "free".
//
class Substitution
{
private:
    ModuleBase *         m_pModule; // Module in which instantiation lives (needed to resolve typerefs)
    SigPointer           m_sigInst;
    const Substitution * m_pNext;

public:
    Substitution()
    {
        LIMITED_METHOD_CONTRACT;
        m_pModule = NULL;
        m_pNext = NULL;
    }

    Substitution(
        ModuleBase *         pModuleArg,
        SigPointer           sigInst,
        const Substitution * pNextSubstitution)
    {
        LIMITED_METHOD_CONTRACT;
        m_pModule = pModuleArg;
        m_sigInst = sigInst;
        m_pNext = pNextSubstitution;
    }

    Substitution(
        mdToken              parentTypeDefOrRefOrSpec,
        ModuleBase *         pModuleArg,
        const Substitution * nextArg);

    Substitution(const Substitution & subst)
    {
        LIMITED_METHOD_CONTRACT;
        m_pModule = subst.m_pModule;
        m_sigInst = subst.m_sigInst;
        m_pNext = subst.m_pNext;
    }
    void DeleteChain();

    ModuleBase * GetModule() const { LIMITED_METHOD_DAC_CONTRACT; return m_pModule; }
    const Substitution * GetNext() const { LIMITED_METHOD_DAC_CONTRACT; return m_pNext; }
    const SigPointer & GetInst() const { LIMITED_METHOD_DAC_CONTRACT; return m_sigInst; }
    DWORD GetLength() const;

    void CopyToArray(Substitution * pTarget /* must have type Substitution[GetLength()] */ ) const;

};  // class Substitution

//---------------------------------------------------------------------------------------
//
// Linked list that records what tokens are currently being compared for equivalence. This prevents
// infinite recursion when types refer to each other in a cycle, e.g. a delegate that takes itself as
// a parameter or a struct that declares a field of itself (illegal but we don't know at this point).
//
class TokenPairList final
{
public:

    // Chain using this constructor when comparing two typedefs for equivalence.
    TokenPairList(mdToken token1, ModuleBase *pModule1, mdToken token2, ModuleBase *pModule2, TokenPairList *pNext)
        : m_token1(token1), m_token2(token2),
          m_pModule1(pModule1), m_pModule2(pModule2),
          m_bInTypeEquivalenceForbiddenScope(pNext == NULL ? FALSE : pNext->m_bInTypeEquivalenceForbiddenScope),
          m_pNext(pNext)
    { LIMITED_METHOD_CONTRACT; }

    static BOOL Exists(TokenPairList *pList, mdToken token1, ModuleBase *pModule1, mdToken token2, ModuleBase *pModule2)
    {
        LIMITED_METHOD_CONTRACT;
        while (pList != NULL)
        {
            if (pList->m_token1 == token1 && pList->m_pModule1 == pModule1 &&
                pList->m_token2 == token2 && pList->m_pModule2 == pModule2)
                return TRUE;

            if (pList->m_token1 == token2 && pList->m_pModule1 == pModule2 &&
                pList->m_token2 == token1 && pList->m_pModule2 == pModule1)
                return TRUE;

            pList = pList->m_pNext;
        }
        return FALSE;
    }

    static BOOL InTypeEquivalenceForbiddenScope(TokenPairList *pList)
    {
        return (pList == NULL ? FALSE : pList->m_bInTypeEquivalenceForbiddenScope);
    }

    // Chain using this method when comparing type specs.
    static TokenPairList AdjustForTypeSpec(TokenPairList *pTemplate, ModuleBase *pTypeSpecModule, PCCOR_SIGNATURE pTypeSpecSig, DWORD cbTypeSpecSig);
    static TokenPairList AdjustForTypeEquivalenceForbiddenScope(TokenPairList *pTemplate);

    TokenPairList(TokenPairList *pTemplate)
        : m_token1(pTemplate ? pTemplate->m_token1 : mdTokenNil),
          m_token2(pTemplate ? pTemplate->m_token2 : mdTokenNil),
          m_pModule1(pTemplate ? pTemplate->m_pModule1 : NULL),
          m_pModule2(pTemplate ? pTemplate->m_pModule2 : NULL),
          m_bInTypeEquivalenceForbiddenScope(pTemplate ? pTemplate->m_bInTypeEquivalenceForbiddenScope : FALSE),
          m_pNext(pTemplate ? pTemplate->m_pNext : NULL)
    { LIMITED_METHOD_CONTRACT; }

private:
    mdToken m_token1, m_token2;
    ModuleBase *m_pModule1, *m_pModule2;
    BOOL m_bInTypeEquivalenceForbiddenScope;
    TokenPairList *m_pNext;
};  // class TokenPairList

//---------------------------------------------------------------------------------------
//
class MetaSig
{
    public:
        enum MetaSigKind {
            sigMember,
            sigLocalVars,
            sigField,
            };

        //------------------------------------------------------------------
        // Common init used by other constructors
        //------------------------------------------------------------------
        void Init(PCCOR_SIGNATURE szMetaSig,
                DWORD cbMetaSig,
                Module* pModule,
                const SigTypeContext *pTypeContext,
                MetaSigKind kind = sigMember);

        //------------------------------------------------------------------
        // Constructor. Warning: Does NOT make a copy of szMetaSig.
        //
        // The instantiations are used to fill in type variables on calls
        // to PeekArg, GetReturnType, GetNextArg, GetTypeHandle, GetRetTypeHandle and
        // so on.
        //
        // Please make sure you know what you're doing by leaving classInst and methodInst to default NULL
        // Are you sure the signature cannot contain type parameters (E_T_VAR, E_T_MVAR)?
        //------------------------------------------------------------------
        MetaSig(PCCOR_SIGNATURE szMetaSig,
                DWORD cbMetaSig,
                Module* pModule,
                const SigTypeContext *pTypeContext,
                MetaSigKind kind = sigMember)
        {
            WRAPPER_NO_CONTRACT;
            Init(szMetaSig, cbMetaSig, pModule, pTypeContext, kind);
        }

        // this is just a variation of the previous constructor to ease the transition to Signature
        MetaSig(const Signature &      signature,
                Module               * pModule,
                const SigTypeContext * pTypeContext,
                MetaSigKind            kind = sigMember)
        {
            WRAPPER_NO_CONTRACT;
            Init(signature.GetRawSig(), signature.GetRawSigLen(), pModule, pTypeContext, kind);
        }

        // The following create MetaSigs for parsing the signature of the given method.
        // They are identical except that they give slightly different
        // type contexts.  (Note the type context will only be relevant if we
        // are parsing a method on an array type or on a generic type.)
        // See TypeCtxt.h for more details.
        // If declaringType is omitted then a *representative* instantiation may be obtained from pMD or pFD
        MetaSig(MethodDesc *pMD, TypeHandle declaringType = TypeHandle());
        MetaSig(MethodDesc *pMD, Instantiation classInst, Instantiation methodInst);

        MetaSig(FieldDesc *pFD, TypeHandle declaringType = TypeHandle());

        // Used to avoid touching metadata for CoreLib methods.  Nb. only use for non-generic methods.
        MetaSig(BinderMethodID id);

        MetaSig(LPHARDCODEDMETASIG pwzMetaSig);

        //------------------------------------------------------------------
        // Returns type of current argument index. Returns ELEMENT_TYPE_END
        // if already past end of arguments.
        //------------------------------------------------------------------
        CorElementType PeekArg() const;

        //------------------------------------------------------------------
        // Returns type of current argument index. Returns ELEMENT_TYPE_END
        // if already past end of arguments.
        //------------------------------------------------------------------
        CorElementType PeekArgNormalized(TypeHandle * pthValueType = NULL) const;

        //------------------------------------------------------------------
        // Returns type of current argument, then advances the argument
        // index. Returns ELEMENT_TYPE_END if already past end of arguments.
        // This method updates m_pLastType
        //------------------------------------------------------------------
        CorElementType NextArg();

        //------------------------------------------------------------------
        // Advance the argument index. Can be used with GetArgProps() to
        // to iterate when you do not have a valid type context.
        // This method updates m_pLastType
        //------------------------------------------------------------------
        void SkipArg();

        //------------------------------------------------------------------
        // Returns a read-only SigPointer for the m_pLastType set by one
        // of NextArg() or SkipArg()
        // This allows extracting more information for complex types.
        //------------------------------------------------------------------
        const SigPointer & GetArgProps() const
        {
            LIMITED_METHOD_CONTRACT;
            return m_pLastType;
        }

        //------------------------------------------------------------------
        // Returns a read-only SigPointer for the return type.
        // This allows extracting more information for complex types.
        //------------------------------------------------------------------
        const SigPointer & GetReturnProps() const
        {
            LIMITED_METHOD_CONTRACT;
            return m_pRetType;
        }


        //------------------------------------------------------------------------
        // Returns # of arguments. Does not count the return value.
        // Does not count the "this" argument (which is not reflected on the
        // sig.) 64-bit arguments are counted as one argument.
        //------------------------------------------------------------------------
        UINT NumFixedArgs()
        {
            LIMITED_METHOD_DAC_CONTRACT;
            return m_nArgs;
        }

        //----------------------------------------------------------
        // Returns the calling convention (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h) - throws.
        //----------------------------------------------------------
        static BYTE GetCallingConvention(
            const Signature &signature)
        {
            CONTRACTL
            {
                THROWS;
                GC_NOTRIGGER;
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END

            PCCOR_SIGNATURE pSig = signature.GetRawSig();

            if (signature.GetRawSigLen() < 1)
            {
                ThrowHR(COR_E_BADIMAGEFORMAT);
            }
            return (BYTE)(IMAGE_CEE_CS_CALLCONV_MASK & CorSigUncompressCallingConv(/*modifies*/pSig));
        }

        //----------------------------------------------------------
        // Returns the calling convention (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h) - doesn't throw.
        //----------------------------------------------------------
        __checkReturn
        static HRESULT GetCallingConvention_NoThrow(
            const Signature &signature,
            BYTE            *pbCallingConvention)
        {
            CONTRACTL
            {
                NOTHROW;
                GC_NOTRIGGER;
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END

            PCCOR_SIGNATURE pSig = signature.GetRawSig();

            if (signature.GetRawSigLen() < 1)
            {
                *pbCallingConvention = 0;
                return COR_E_BADIMAGEFORMAT;
            }
            *pbCallingConvention = (BYTE)(IMAGE_CEE_CS_CALLCONV_MASK & CorSigUncompressCallingConv(/*modifies*/pSig));
            return S_OK;
        }

        //----------------------------------------------------------
        // Returns the calling convention (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h)
        //----------------------------------------------------------
        BYTE GetCallingConvention()
        {
            LIMITED_METHOD_CONTRACT;
            SUPPORTS_DAC;
            return m_CallConv & IMAGE_CEE_CS_CALLCONV_MASK;
        }

        //----------------------------------------------------------
        // Returns the calling convention & flags (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h)
        //----------------------------------------------------------
        BYTE GetCallingConventionInfo()
        {
            LIMITED_METHOD_DAC_CONTRACT;

            return m_CallConv;
        }

        //----------------------------------------------------------
        // Has a 'this' pointer?
        //----------------------------------------------------------
        BOOL HasThis()
        {
            LIMITED_METHOD_CONTRACT;

            return m_CallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS;
        }

        //----------------------------------------------------------
        // Has a explicit 'this' pointer?
        //----------------------------------------------------------
        BOOL HasExplicitThis()
        {
            LIMITED_METHOD_CONTRACT;

            return m_CallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS;
        }

        //----------------------------------------------------------
        // Is a generic method with explicit arity?
        //----------------------------------------------------------
        BOOL IsGenericMethod()
        {
            LIMITED_METHOD_CONTRACT;
            return m_CallConv & IMAGE_CEE_CS_CALLCONV_GENERIC;
        }

        //----------------------------------------------------------
        // Is vararg?
        //----------------------------------------------------------
        BOOL IsVarArg()
        {
            WRAPPER_NO_CONTRACT;
            SUPPORTS_DAC;
            return GetCallingConvention() == IMAGE_CEE_CS_CALLCONV_VARARG;
        }

        //----------------------------------------------------------
        // Is vararg?
        //----------------------------------------------------------
        static BOOL IsVarArg(const Signature &signature)
        {
            CONTRACTL
            {
                NOTHROW;
                GC_NOTRIGGER;
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END

            HRESULT hr;
            BYTE    nCallingConvention;

            hr = GetCallingConvention_NoThrow(signature, &nCallingConvention);
            if (FAILED(hr))
            {   // Invalid signatures are not VarArg
                return FALSE;
            }
            return nCallingConvention == IMAGE_CEE_CS_CALLCONV_VARARG;
        }

        Module* GetModule() const
        {
            LIMITED_METHOD_DAC_CONTRACT;

            return m_pModule;
        }

        //------------------------------------------------------------------
        // Like NextArg, but return only normalized type (enums flattened to
        // the underlying type ...
        //------------------------------------------------------------------
        CorElementType
        NextArgNormalized(TypeHandle * pthValueType = NULL)
        {
            CONTRACTL
            {
                if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
                if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
                if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END

            m_pLastType = m_pWalk;
            if (m_iCurArg == m_nArgs)
            {
                return ELEMENT_TYPE_END;
            }
            else
            {
                m_iCurArg++;
                CorElementType mt = m_pWalk.PeekElemTypeNormalized(m_pModule, &m_typeContext, pthValueType);
                // We should not hit ELEMENT_TYPE_END in the middle of the signature
                if (mt == ELEMENT_TYPE_END)
                {
                    THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (ModuleBase*)NULL);
                }
                IfFailThrowBF(m_pWalk.SkipExactlyOne(), BFA_BAD_SIGNATURE, (ModuleBase*)NULL);
                return mt;
            }
        } // NextArgNormalized

        // Tests if the return type is an object ref.  Loads types
        // if needed (though it shouldn't really need to)
        BOOL IsObjectRefReturnType();

        //------------------------------------------------------------------------
        // Compute element size from CorElementType and optional valuetype.
        //------------------------------------------------------------------------
        static UINT GetElemSize(CorElementType etype, TypeHandle thValueType);

        UINT GetReturnTypeSize()
        {
            WRAPPER_NO_CONTRACT;
            SUPPORTS_DAC;
            TypeHandle thValueType;
            return m_pRetType.SizeOf(m_pModule, &m_typeContext, &thValueType);
        }

        //------------------------------------------------------------------
        // Perform type-specific GC promotion on the value (based upon the
        // last type retrieved by NextArg()).
        //------------------------------------------------------------------
        VOID GcScanRoots(ArgDestination *pValue, promote_func *fn,
                         ScanContext* sc, promote_carefully_func *fnc = NULL);

        //------------------------------------------------------------------
        // Is the return type 64 bit?
        //------------------------------------------------------------------
        BOOL Is64BitReturn() const
        {
            WRAPPER_NO_CONTRACT;
            CorElementType rt = GetReturnTypeNormalized();
            return (rt == ELEMENT_TYPE_I8 || rt == ELEMENT_TYPE_U8 || rt == ELEMENT_TYPE_R8);
        }

        //------------------------------------------------------------------
        // Is the return type floating point?
        //------------------------------------------------------------------
        BOOL HasFPReturn()
        {
            WRAPPER_NO_CONTRACT;
            CorElementType rt = GetReturnTypeNormalized();
            return (rt == ELEMENT_TYPE_R4 || rt == ELEMENT_TYPE_R8);
        }

        //------------------------------------------------------------------
        // reset: goto start pos
        //------------------------------------------------------------------
        VOID Reset();

        //------------------------------------------------------------------
        // current position of the arg iterator
        //------------------------------------------------------------------
        UINT GetArgNum()
        {
            LIMITED_METHOD_CONTRACT;
            return m_iCurArg;
        }

        //------------------------------------------------------------------
        // Returns CorElementType of return value, taking into account
        // any instantiations due to generics.  Does not load types.
        // Does not return normalized type.
        //------------------------------------------------------------------
        CorElementType GetReturnType() const;

        BOOL IsReturnTypeVoid() const;

        CorElementType GetReturnTypeNormalized(TypeHandle * pthValueType = NULL) const;

        //------------------------------------------------------------------
        // used to treat some sigs as special case vararg
        // used by calli to unmanaged target
        //------------------------------------------------------------------
        BOOL IsTreatAsVarArg()
        {
            LIMITED_METHOD_DAC_CONTRACT;

            return (m_flags & TREAT_AS_VARARG);
        }

        //------------------------------------------------------------------
        // Determines if the current argument is System/String.
        // Caller must determine first that the argument type is
        // ELEMENT_TYPE_CLASS or ELEMENT_TYPE_STRING.  This may be used during
        // GC.
        //------------------------------------------------------------------
        BOOL IsStringType() const;

        //------------------------------------------------------------------
        // Determines if the current argument is a particular class.
        // Caller must determine first that the argument type
        // is ELEMENT_TYPE_CLASS.
        //------------------------------------------------------------------
        BOOL IsClass(LPCUTF8 szClassName) const;


        //------------------------------------------------------------------
        // This method will return a TypeHandle for the last argument
        // examined.
        // If NextArg() returns ELEMENT_TYPE_BYREF, you can also call GetByRefType()
        // to get to the underlying type of the byref
        //------------------------------------------------------------------
        TypeHandle GetLastTypeHandleNT() const
        {
             WRAPPER_NO_CONTRACT;
             return m_pLastType.GetTypeHandleNT(m_pModule, &m_typeContext);
        }

        //------------------------------------------------------------------
        // This method will return a TypeHandle for the last argument
        // examined.
        // If NextArg() returns ELEMENT_TYPE_BYREF, you can also call GetByRefType()
        // to get to the underlying type of the byref
        //------------------------------------------------------------------
        TypeHandle GetLastTypeHandleThrowing(ClassLoader::LoadTypesFlag fLoadTypes = ClassLoader::LoadTypes,
                                             ClassLoadLevel level = CLASS_LOADED,
                                             BOOL dropGenericArgumentLevel = FALSE) const
        {
             WRAPPER_NO_CONTRACT;
             return m_pLastType.GetTypeHandleThrowing((ModuleBase*)m_pModule, &m_typeContext, fLoadTypes,
                                                      level, dropGenericArgumentLevel);
        }

        //------------------------------------------------------------------
        // Returns the TypeHandle for the return type of the signature
        //------------------------------------------------------------------
        TypeHandle GetRetTypeHandleNT() const
        {
             WRAPPER_NO_CONTRACT;
             return m_pRetType.GetTypeHandleNT(m_pModule, &m_typeContext);
        }

        TypeHandle GetRetTypeHandleThrowing(ClassLoader::LoadTypesFlag fLoadTypes = ClassLoader::LoadTypes,
                                            ClassLoadLevel level = CLASS_LOADED) const
        {
             WRAPPER_NO_CONTRACT;
             return m_pRetType.GetTypeHandleThrowing((ModuleBase*)m_pModule, &m_typeContext, fLoadTypes, level);
        }

        //------------------------------------------------------------------
        // Returns the base type of the byref type of the last argument examined
        // which needs to have been ELEMENT_TYPE_BYREF.
        // For object references, the class being accessed byref is also returned in *pTy.
        // eg. for "int32 &",            return value = ELEMENT_TYPE_I4,    *pTy= ???
        //     for "System.Exception &", return value = ELEMENT_TYPE_CLASS, *pTy=System.Exception
        // Note that byref to byref is not allowed, and so the return value
        // can never be ELEMENT_TYPE_BYREF.
        //------------------------------------------------------------------
        CorElementType GetByRefType(TypeHandle* pTy) const;

        //------------------------------------------------------------------
        // Consume the custom modifiers, if any, in the current signature
        // and update it.
        // This is a non destructive operation if the current signature is not
        // pointing at a sequence of ELEMENT_TYPE_CMOD_REQD or ELEMENT_TYPE_CMOD_OPT.
        //------------------------------------------------------------------
        static void ConsumeCustomModifiers(PCCOR_SIGNATURE& pSig, PCCOR_SIGNATURE pEndSig);

        // Struct used to capture in/out state during the comparison
        // of element types.
        struct CompareState
        {
            // List of tokens that are currently being compared.
            // See TokenPairList for more use details.
            TokenPairList*  Visited;

            // Boolean indicating if custom modifiers should
            // be compared.
            bool IgnoreCustomModifiers;

            CompareState() = default;

            CompareState(TokenPairList* list)
                : Visited{ list }
                , IgnoreCustomModifiers{ false }
            { }
        };

        //------------------------------------------------------------------
        // Compare types in two signatures, first applying
        // - optional substitutions pSubst1 and pSubst2
        //   to class type parameters (E_T_VAR) in the respective signatures
        //------------------------------------------------------------------
        static BOOL CompareElementType(
            PCCOR_SIGNATURE &    pSig1,
            PCCOR_SIGNATURE &    pSig2,
            PCCOR_SIGNATURE      pEndSig1,
            PCCOR_SIGNATURE      pEndSig2,
            ModuleBase *         pModule1,
            ModuleBase *         pModule2,
            const Substitution * pSubst1,
            const Substitution * pSubst2,
            CompareState *       state = NULL);



        // If pTypeDef1 is C<...> and pTypeDef2 is C<...> (for possibly different instantiations)
        // then check C<!0, ... !n> @ pSubst1 == C<!0, ..., !n> @ pSubst2, i.e.
        // that the head type (C) is the same and that when the head type is treated
        // as an uninstantiated type definition and we apply each of the substitutions
        // then the same type results.  This effectively checks that the two substitutions
        // are equivalent.
        static BOOL CompareTypeDefsUnderSubstitutions(MethodTable *pTypeDef1,          MethodTable *pTypeDef2,
                                                      const Substitution*   pSubst1,   const Substitution*   pSubst2,
                                                      TokenPairList *pVisited = NULL);


        // Compare two complete method signatures, first applying optional substitutions pSubst1 and pSubst2
        // to class type parameters (E_T_VAR) in the respective signatures
        static BOOL CompareMethodSigs(
            PCCOR_SIGNATURE     pSig1,
            DWORD               cSig1,
            ModuleBase*         pModule1,
            const Substitution* pSubst1,
            PCCOR_SIGNATURE     pSig2,
            DWORD               cSig2,
            ModuleBase*         pModule2,
            const Substitution* pSubst2,
            BOOL                skipReturnTypeSig,
            TokenPairList*      pVisited = NULL
        );

        static BOOL CompareFieldSigs(
            PCCOR_SIGNATURE pSig1,
            DWORD       cSig1,
            ModuleBase* pModule1,
            PCCOR_SIGNATURE pSig2,
            DWORD       cSig2,
            ModuleBase* pModule2,
            TokenPairList *pVisited = NULL
        );

        static BOOL CompareMethodSigs(MetaSig &msig1,
                                      MetaSig &msig2,
                                      BOOL ignoreCallconv);

        // Is each set of constraints on the implementing method's type parameters a subset
        // of the corresponding set of constraints on the declared method's type parameters,
        // given a substitution for the latter's (class) type parameters.
        // This is used by the class loader to verify type safety of method overriding and interface implementation.
        static BOOL CompareMethodConstraints(const Substitution *pSubst1,
                                             Module *pModule1,
                                             mdMethodDef tok1, //implementing method
                                             const Substitution *pSubst2,
                                             Module *pModule2,
                                             mdMethodDef tok2); //declared method

private:
        static BOOL CompareVariableConstraints(const Substitution *pSubst1,
                                               Module *pModule1, mdGenericParam tok1, //overriding
                                               const Substitution *pSubst2,
                                               Module *pModule2, mdGenericParam tok2); //overridden

        static BOOL CompareTypeDefOrRefOrSpec(ModuleBase *pModule1, mdToken tok1,
                                              const Substitution *pSubst1,
                                              ModuleBase *pModule2, mdToken tok2,
                                              const Substitution *pSubst2,
                                              TokenPairList *pVisited);
        static BOOL CompareTypeSpecToToken(mdTypeSpec tk1,
                                           mdToken tk2,
                                           ModuleBase *pModule1,
                                           ModuleBase *pModule2,
                                           const Substitution *pSubst1,
                                           TokenPairList *pVisited);

        static BOOL CompareElementTypeToToken(PCCOR_SIGNATURE &pSig1,
                                             PCCOR_SIGNATURE pEndSig1, // end of sig1
                                             mdToken         tk2,
                                             ModuleBase*         pModule1,
                                             ModuleBase*         pModule2,
                                             const Substitution*   pSubst1,
                                             TokenPairList *pVisited);

public:

        //------------------------------------------------------------------
        // Ensures that all the value types in the sig are loaded. This
        // should be called on sig's that have value types before they
        // are passed to Call(). This ensures that value classes will not
        // be loaded during the operation to determine the size of the
        // stack. Thus preventing the resulting GC hole.
        //------------------------------------------------------------------
        static void EnsureSigValueTypesLoaded(MethodDesc *pMD);

        // this walks the sig and checks to see if all  types in the sig can be loaded
        static void CheckSigTypesCanBeLoaded(MethodDesc *pMD);

        const SigTypeContext *GetSigTypeContext() const { LIMITED_METHOD_CONTRACT; return &m_typeContext; }

        // Disallow copy constructor.
        MetaSig(MetaSig *pSig);

        void SetHasParamTypeArg()
        {
            LIMITED_METHOD_CONTRACT;
            m_CallConv |= CORINFO_CALLCONV_PARAMTYPE;
        }

        void SetTreatAsVarArg()
        {
            LIMITED_METHOD_CONTRACT;
            m_flags |= TREAT_AS_VARARG;
        }

        void ClearHasThis()
        {
            LIMITED_METHOD_CONTRACT;
            m_CallConv &= ~IMAGE_CEE_CS_CALLCONV_HASTHIS;
        }

    // These are protected because Reflection subclasses Metasig
    protected:

        enum MetaSigFlags
        {
            SIG_RET_TYPE_INITTED    = 0x01,
            TREAT_AS_VARARG         = 0x02,     // used to treat some sigs as special case vararg
                                                // used by calli to unmanaged target
        };

        Module*      m_pModule;
        SigTypeContext m_typeContext;   // Instantiation for type parameters

        SigPointer   m_pStart;
        SigPointer   m_pWalk;
        SigPointer   m_pLastType;
        SigPointer   m_pRetType;
        UINT32       m_nArgs;
        UINT32       m_iCurArg;

        // The following are cached so we don't the signature
        //  multiple times

        CorElementType  m_corNormalizedRetType;
        BYTE            m_flags;
        BYTE            m_CallConv;
};  // class MetaSig

BOOL IsTypeRefOrDef(LPCSTR szClassName, Module *pModule, mdToken token);

#if defined(FEATURE_TYPEEQUIVALENCE) && !defined(DACCESS_COMPILE)

// A helper struct representing data stored in the TypeIdentifierAttribute.
struct TypeIdentifierData
{
    TypeIdentifierData() : m_cbScope(0), m_pchScope(NULL), m_cbIdentifierNamespace(0), m_pchIdentifierNamespace(NULL),
                           m_cbIdentifierName(0), m_pchIdentifierName(NULL) {}

    HRESULT Init(Module *pModule, mdToken tk);
    BOOL IsEqual(const TypeIdentifierData & data) const;

private:
    SIZE_T  m_cbScope;
    LPCUTF8 m_pchScope;
    SIZE_T  m_cbIdentifierNamespace;
    LPCUTF8 m_pchIdentifierNamespace;
    SIZE_T  m_cbIdentifierName;
    LPCUTF8 m_pchIdentifierName;
};

#endif // FEATURE_TYPEEQUIVALENCE && !DACCESS_COMPILE

// fResolved is TRUE when one of the tokens is a resolved TypeRef. This is used to restrict
// type equivalence checks for value types.
BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, ModuleBase *pModule1, ModuleBase *pModule2, TokenPairList *pVisited = NULL);

// Nonthrowing version of CompareTypeTokens.
//
//   Return S_OK if they match
//          S_FALSE if they don't match
//          FAILED  if OOM or some other blocking error
//
HRESULT  CompareTypeTokensNT(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2);

// TRUE if the two TypeDefs have the same layout and field marshal information.
BOOL CompareTypeLayout(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2);
BOOL CompareTypeDefsForEquivalence(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2, TokenPairList *pVisited);
BOOL IsTypeDefEquivalent(mdToken tk, Module *pModule);
BOOL IsTypeDefExternallyVisible(mdToken tk, Module *pModule, DWORD dwAttrs);

void ReportPointersFromValueType(promote_func *fn, ScanContext *sc, PTR_MethodTable pMT, PTR_VOID pSrc);

#endif /* _H_SIGINFO */
