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

/*****************************************************************************
 **                                                                         **
 ** Corhlpr.h -                                                                 **
 **                                                                         **
 *****************************************************************************/


#ifndef __CORHLPR_H__
#define __CORHLPR_H__

#if defined(_MSC_VER) && defined(HOST_X86) && !defined(FPO_ON)
#pragma optimize("y", on)		// Small critical routines, don't put in EBP frame
#define FPO_ON 1
#define CORHLPR_TURNED_FPO_ON 1
#endif

#include <assert.h>
#include "cor.h"
#include "corhdr.h"
#include "corerror.h"
#include "unreachable.h"
#include <new>

using std::nothrow;

#define NEW_NOTHROW(_bytes) new (nothrow) BYTE[_bytes]
#define NEW_THROWS(_bytes) new BYTE[_bytes]
void DECLSPEC_NORETURN ThrowOutOfMemory();
inline void DECLSPEC_NORETURN THROW_OUT_OF_MEMORY()
{
    ThrowOutOfMemory();
}

//*****************************************************************************
// There are a set of macros commonly used in the helpers which you will want
// to override to get richer behavior.  The following defines what is needed
// if you chose not to do the extra work.
//*****************************************************************************
#ifndef IfFailGoto
#define IfFailGoto(EXPR, LABEL) \
do { hr = (EXPR); if(FAILED(hr)) { goto LABEL; } } while (0)
#endif

#ifndef IfFailGo
#define IfFailGo(EXPR) IfFailGoto(EXPR, ErrExit)
#endif

#ifndef IfFailRet
#define IfFailRet(EXPR) do { hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0)
#endif

#ifndef IfNullRet
#define IfNullRet(EXPR) do { if ((EXPR) == NULL){ return (E_OUTOFMEMORY); } } while (0)
#endif


#if !BIGENDIAN
#define VAL16(x) x
#define VAL32(x) x
#endif

//*****************************************************************************
//
//***** Macro to assist with cleaning up local static variables
//
//*****************************************************************************

#define CHECK_LOCAL_STATIC_VAR(x)   \
    x                                \

//*****************************************************************************
//
//***** Utility helpers
//
//*****************************************************************************


#define MAX_CLASSNAME_LENGTH 1024

//*****************************************************************************
//
//***** Signature helpers
//
//*****************************************************************************

inline bool isCallConv(unsigned sigByte, CorCallingConvention conv)
{
    return ((sigByte & IMAGE_CEE_CS_CALLCONV_MASK) == (unsigned) conv);
}

//*****************************************************************************
//
//***** File format helper classes
//
//*****************************************************************************



//*****************************************************************************
typedef struct tagCOR_ILMETHOD_SECT_SMALL : IMAGE_COR_ILMETHOD_SECT_SMALL {
        //Data follows
    const BYTE* Data() const
    {
        return(((const BYTE*) this) + sizeof(struct tagCOR_ILMETHOD_SECT_SMALL));
    }

    bool IsSmall() const
    {
        return (Kind & CorILMethod_Sect_FatFormat) == 0;
    }

    bool More() const
    {
        return (Kind & CorILMethod_Sect_MoreSects) != 0;
    }
} COR_ILMETHOD_SECT_SMALL;


/************************************/
/* NOTE this structure must be DWORD aligned!! */
typedef struct tagCOR_ILMETHOD_SECT_FAT : IMAGE_COR_ILMETHOD_SECT_FAT {
        //Data follows
    const BYTE* Data() const
    {
        return(((const BYTE*) this) + sizeof(struct tagCOR_ILMETHOD_SECT_FAT));
    }

        //Endian-safe wrappers
    unsigned GetKind() const {
        /* return Kind; */
        return *(BYTE*)this;
    }
    void SetKind(unsigned kind) {
        /* Kind = kind; */
        *(BYTE*)this = (BYTE)kind;
    }

    unsigned GetDataSize() const {
        /* return DataSize; */
        BYTE* p = (BYTE*)this;
        return ((unsigned)*(p+1)) |
            (((unsigned)*(p+2)) << 8) |
            (((unsigned)*(p+3)) << 16);
    }
    void SetDataSize(unsigned datasize) {
        /* DataSize = dataSize; */
        BYTE* p = (BYTE*)this;
        *(p+1) = (BYTE)(datasize);
        *(p+2) = (BYTE)(datasize >> 8);
        *(p+3) = (BYTE)(datasize >> 16);
    }
} COR_ILMETHOD_SECT_FAT;

typedef struct tagCOR_ILMETHOD_SECT_EH_CLAUSE_FAT : public IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT {
    //Endian-safe wrappers
    CorExceptionFlag GetFlags() const {
        return (CorExceptionFlag)VAL32((unsigned)Flags);
    }
    void SetFlags(CorExceptionFlag flags) {
        Flags = (CorExceptionFlag)VAL32((unsigned)flags);
    }

    DWORD GetTryOffset() const {
        return VAL32(TryOffset);
    }
    void SetTryOffset(DWORD Offset) {
        TryOffset = VAL32(Offset);
    }

    DWORD GetTryLength() const {
        return VAL32(TryLength);
    }
    void SetTryLength(DWORD Length) {
        TryLength = VAL32(Length);
    }

    DWORD GetHandlerOffset() const {
        return VAL32(HandlerOffset);
    }
    void SetHandlerOffset(DWORD Offset) {
        HandlerOffset = VAL32(Offset);
    }

    DWORD GetHandlerLength() const {
        return VAL32(HandlerLength);
    }
    void SetHandlerLength(DWORD Length) {
        HandlerLength = VAL32(Length);
    }

    DWORD GetClassToken() const {
        return VAL32(ClassToken);
    }
    void SetClassToken(DWORD tok) {
        ClassToken = VAL32(tok);
    }

    DWORD GetFilterOffset() const {
        return VAL32(FilterOffset);
    }
    void SetFilterOffset(DWORD offset) {
        FilterOffset = VAL32(offset);
    }

} COR_ILMETHOD_SECT_EH_CLAUSE_FAT;

//*****************************************************************************
struct COR_ILMETHOD_SECT_EH_FAT : public COR_ILMETHOD_SECT_FAT {
    static unsigned Size(unsigned ehCount) {
        return (sizeof(COR_ILMETHOD_SECT_EH_FAT) +
                sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT) * (ehCount-1));
        }

    IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT Clauses[1];     // actually variable size
};

typedef struct tagCOR_ILMETHOD_SECT_EH_CLAUSE_SMALL : public IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL {
    //Endian-safe wrappers
    CorExceptionFlag GetFlags() const {
        return (CorExceptionFlag)VAL16((SHORT)Flags);
    }
    void SetFlags(CorExceptionFlag flags) {
        Flags = (CorExceptionFlag)VAL16((SHORT)flags);
    }

    DWORD GetTryOffset() const {
        return VAL16(TryOffset);
    }
    void SetTryOffset(DWORD Offset) {
        assert((Offset & ~0xffff) == 0);
        TryOffset = VAL16(Offset);
    }

    DWORD GetTryLength() const {
        return TryLength;
    }
    void SetTryLength(DWORD Length) {
        assert((Length & ~0xff) == 0);
        TryLength = Length;
    }

    DWORD GetHandlerOffset() const {
        return VAL16(HandlerOffset);
    }
    void SetHandlerOffset(DWORD Offset) {
        assert((Offset & ~0xffff) == 0);
        HandlerOffset = VAL16(Offset);
    }

    DWORD GetHandlerLength() const {
        return HandlerLength;
    }
    void SetHandlerLength(DWORD Length) {
        assert((Length & ~0xff) == 0);
        HandlerLength = Length;
    }

    DWORD GetClassToken() const {
        return VAL32(ClassToken);
    }
    void SetClassToken(DWORD tok) {
        ClassToken = VAL32(tok);
    }

    DWORD GetFilterOffset() const {
        return VAL32(FilterOffset);
    }
    void SetFilterOffset(DWORD offset) {
        FilterOffset = VAL32(offset);
    }
} COR_ILMETHOD_SECT_EH_CLAUSE_SMALL;

//*****************************************************************************
struct COR_ILMETHOD_SECT_EH_SMALL : public COR_ILMETHOD_SECT_SMALL {
    static unsigned Size(unsigned ehCount) {
        return (sizeof(COR_ILMETHOD_SECT_EH_SMALL) +
                sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL) * (ehCount-1));
        }

    WORD Reserved;                                  // alignment padding
    IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL Clauses[1];   // actually variable size
};


/************************************/
/* NOTE this structure must be DWORD aligned!! */
struct COR_ILMETHOD_SECT
{
    bool More() const
    {
        return((AsSmall()->Kind & CorILMethod_Sect_MoreSects) != 0);
    }

    CorILMethodSect Kind() const
    {
        return((CorILMethodSect) (AsSmall()->Kind & CorILMethod_Sect_KindMask));
    }

    const COR_ILMETHOD_SECT* Next() const
    {
        if (!More()) return(0);
        return ((COR_ILMETHOD_SECT*)Align(((BYTE *)this) + DataSize()));
    }

    const BYTE* Data() const
    {
        if (IsFat()) return(AsFat()->Data());
        return(AsSmall()->Data());
    }

    unsigned DataSize() const
    {
        if (Kind() == CorILMethod_Sect_EHTable)
        {
            // VB and MC++ shipped with bug where they have not accounted for size of COR_ILMETHOD_SECT_EH_XXX
            // in DataSize. To avoid breaking these images, we will align the size of EH sections up. This works
            // because IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_XXX is bigger than COR_ILMETHOD_SECT_EH_XXX
            // (see VSWhidbey #99031 and related bugs for details).

            if (IsFat())
                return Fat.Size(Fat.GetDataSize() / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT));
            else
                return Small.Size(Small.DataSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL));
        }
        else
    {
        if (IsFat()) return(AsFat()->GetDataSize());
        return(AsSmall()->DataSize);
    }
    }

    friend struct COR_ILMETHOD;
    friend struct tagCOR_ILMETHOD_FAT;
    friend struct tagCOR_ILMETHOD_TINY;
    bool IsFat() const
    {
        return((AsSmall()->Kind & CorILMethod_Sect_FatFormat) != 0);
    }

    static const void* Align(const void* p)
    {
        return((void*) ((((UINT_PTR) p) + 3) & ~3));
    }

protected:
    const COR_ILMETHOD_SECT_FAT*   AsFat() const
    {
        return((COR_ILMETHOD_SECT_FAT*) this);
    }

    const COR_ILMETHOD_SECT_SMALL* AsSmall() const
    {
        return((COR_ILMETHOD_SECT_SMALL*) this);
    }

public:
    // The body is either a COR_ILMETHOD_SECT_SMALL or COR_ILMETHOD_SECT_FAT
    // (as indicated by the CorILMethod_Sect_FatFormat bit
    union {
        COR_ILMETHOD_SECT_EH_SMALL Small;
        COR_ILMETHOD_SECT_EH_FAT Fat;
        };
};


/***********************************/
// exported functions (implementation in Format\Format.cpp:
extern "C" {
IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* __stdcall SectEH_EHClause(void *pSectEH, unsigned idx, IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* buff);
        // compute the size of the section (best format)
        // codeSize is the size of the method
    // deprecated
unsigned __stdcall SectEH_SizeWithCode(unsigned ehCount, unsigned codeSize);

    // will return worse-case size and then Emit will return actual size
unsigned __stdcall SectEH_SizeWorst(unsigned ehCount);

    // will return exact size which will match the size returned by Emit
unsigned __stdcall SectEH_SizeExact(unsigned ehCount, IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clauses);

        // emit the section (best format);
unsigned __stdcall SectEH_Emit(unsigned size, unsigned ehCount,
                  IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clauses,
                  BOOL moreSections, BYTE* outBuff,
                  ULONG* ehTypeOffsets = 0);
} // extern "C"


struct COR_ILMETHOD_SECT_EH : public COR_ILMETHOD_SECT
{
    unsigned EHCount() const
    {
        return (unsigned)(IsFat() ? (Fat.GetDataSize() / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT)) :
                        (Small.DataSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL)));
    }

        // return one clause in its fat form.  Use 'buff' if needed
    const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* EHClause(unsigned idx, IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* buff) const
    {
        return SectEH_EHClause((void *)this, idx, buff);
    };
        // compute the size of the section (best format)
        // codeSize is the size of the method
    // deprecated
    unsigned static Size(unsigned ehCount, unsigned codeSize)
    {
        return SectEH_SizeWithCode(ehCount, codeSize);
    };

    // will return worse-case size and then Emit will return actual size
    unsigned static Size(unsigned ehCount)
    {
        return SectEH_SizeWorst(ehCount);
    };

    // will return exact size which will match the size returned by Emit
    unsigned static Size(unsigned ehCount, const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clauses)
    {
        return SectEH_SizeExact(ehCount, (IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)clauses);
    };

        // emit the section (best format);
    unsigned static Emit(unsigned size, unsigned ehCount,
                  const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clauses,
                  bool moreSections, BYTE* outBuff,
                  ULONG* ehTypeOffsets = 0)
    {
        return SectEH_Emit(size, ehCount,
                           (IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)clauses,
                           moreSections, outBuff, ehTypeOffsets);
    };
};


/***************************************************************************/
/* Used when the method is tiny (< 64 bytes), and there are no local vars */
typedef struct tagCOR_ILMETHOD_TINY : IMAGE_COR_ILMETHOD_TINY
{
    bool     IsTiny() const
    {
        return((Flags_CodeSize & (CorILMethod_FormatMask >> 1)) == CorILMethod_TinyFormat);
    }

    unsigned GetCodeSize() const
    {
        return(((unsigned) Flags_CodeSize) >> (CorILMethod_FormatShift-1));
    }

    unsigned GetMaxStack() const
    {
        return(8);
    }

    BYTE*    GetCode() const
    {
        return(((BYTE*) this) + sizeof(struct tagCOR_ILMETHOD_TINY));
    }

    DWORD    GetLocalVarSigTok() const
    {
        return(0);
    }

    COR_ILMETHOD_SECT* GetSect() const
    {
        return(0);
    }
} COR_ILMETHOD_TINY;


/************************************/
// This structure is the 'fat' layout, where no compression is attempted.
// Note that this structure can be added on at the end, thus making it extensible
typedef struct tagCOR_ILMETHOD_FAT : IMAGE_COR_ILMETHOD_FAT
{
        //Endian-safe wrappers
    unsigned GetSize() const {
        /* return Size; */
        BYTE* p = (BYTE*)this;
        return *(p+1) >> 4;
    }
    void SetSize(unsigned size) {
        /* Size = size; */
        BYTE* p = (BYTE*)this;
        *(p+1) = (BYTE)((*(p+1) & 0x0F) | (size << 4));
    }

    unsigned GetFlags() const {
        /* return Flags; */
        BYTE* p = (BYTE*)this;
        return ((unsigned)*(p+0)) | (( ((unsigned)*(p+1)) & 0x0F) << 8);
    }
    void SetFlags(unsigned flags) {
        /* flags = Flags; */
        BYTE* p = (BYTE*)this;
        *p = (BYTE)flags;
        *(p+1) = (BYTE)((*(p+1) & 0xF0) | ((flags >> 8) & 0x0F));
    }

    bool IsFat() const {
        /* return((IMAGE_COR_ILMETHOD_FAT::GetFlags() & CorILMethod_FormatMask) == CorILMethod_FatFormat); */
        return (*(BYTE*)this & CorILMethod_FormatMask) == CorILMethod_FatFormat;
    }

    unsigned GetMaxStack() const {
        /* return MaxStack; */
        return VAL16(*(USHORT*)((BYTE*)this+2));
    }
    void SetMaxStack(unsigned maxStack) {
        /* MaxStack = maxStack; */
        *(USHORT*)((BYTE*)this+2) = VAL16((USHORT)maxStack);
    }

    unsigned GetCodeSize() const
    {
        return VAL32(CodeSize);
    }

    void SetCodeSize(DWORD Size)
    {
        CodeSize = VAL32(Size);
    }

    mdToken  GetLocalVarSigTok() const
    {
        return VAL32(LocalVarSigTok);
    }

    void SetLocalVarSigTok(mdSignature tok)
    {
        LocalVarSigTok = VAL32(tok);
    }

    BYTE* GetCode() const {
        return(((BYTE*) this) + 4*GetSize());
    }

    bool More() const {
        // return (GetFlags() & CorILMethod_MoreSects) != 0;
        return (*(BYTE*)this & CorILMethod_MoreSects) != 0;
    }

    const COR_ILMETHOD_SECT* GetSect() const {
        if (!More()) return (0);
        return(((COR_ILMETHOD_SECT*) COR_ILMETHOD_SECT::Align(GetCode() + GetCodeSize())));
    }
} COR_ILMETHOD_FAT;


extern "C" {
/************************************/
// exported functions (impl. Format\Format.cpp)
unsigned __stdcall IlmethodSize(COR_ILMETHOD_FAT* header, BOOL MoreSections);
        // emit the header (bestFormat) return amount emitted
unsigned __stdcall IlmethodEmit(unsigned size, COR_ILMETHOD_FAT* header,
                  BOOL moreSections, BYTE* outBuff);
}

struct COR_ILMETHOD
{
        // a COR_ILMETHOD header should not be decoded by hand.  Instead us
        // COR_ILMETHOD_DECODER to decode it.
    friend class COR_ILMETHOD_DECODER;

        // compute the size of the header (best format)
    unsigned static Size(const COR_ILMETHOD_FAT* header, bool MoreSections)
    {
        return IlmethodSize((COR_ILMETHOD_FAT*)header,MoreSections);
    };
        // emit the header (bestFormat) return amount emitted
    unsigned static Emit(unsigned size, const COR_ILMETHOD_FAT* header,
                  bool moreSections, BYTE* outBuff)
    {
        return IlmethodEmit(size, (COR_ILMETHOD_FAT*)header, moreSections, outBuff);
    };

//private:
    union
    {
        COR_ILMETHOD_TINY       Tiny;
        COR_ILMETHOD_FAT        Fat;
    };
        // Code follows the Header, then immedately after the code comes
        // any sections (COR_ILMETHOD_SECT).
};

extern "C" {
/***************************************************************************/
/* COR_ILMETHOD_DECODER is the only way functions internal to the EE should
   fetch data from a COR_ILMETHOD.  This way any dependency on the file format
   (and the multiple ways of encoding the header) is centralized to the
   COR_ILMETHOD_DECODER constructor) */
    void __stdcall DecoderInit(void * pThis, COR_ILMETHOD* header);
    int  __stdcall DecoderGetOnDiskSize(void * pThis, COR_ILMETHOD* header);
} // extern "C"

class COR_ILMETHOD_DECODER : public COR_ILMETHOD_FAT
{
public:
    COR_ILMETHOD_DECODER() = default;

    // Typically the ONLY way you should access COR_ILMETHOD is through
    // this constructor so format changes are easier.
    COR_ILMETHOD_DECODER(const COR_ILMETHOD* header)
    {
        DecoderInit(this,(COR_ILMETHOD*)header);
    };

    // The above variant of the constructor can not do a 'complete' job, because
    // it can not look up the local variable signature meta-data token.
    // This method should be used when you have access to the Meta data API
    // If the construction fails, the 'Code' field is set to 0

    enum DecoderStatus {SUCCESS, FORMAT_ERROR, VERIFICATION_ERROR};

    // If we want the decoder to verify the that local signature is OK we
    // will pass a non-NULL value for wbStatus
    //
    // When using LazyInit we want ask that the local signature be verified
    // But if we fail verification we still need access to the 'Code' field
    // Because we may be able to demand SkipVerification and thus it was OK
    // to have had a verification error.

    COR_ILMETHOD_DECODER(COR_ILMETHOD* header,
                         void *pInternalImport,
                         DecoderStatus* wbStatus);

    unsigned EHCount() const
    {
        return (EH != 0) ? EH->EHCount() : 0;
    }

    unsigned GetHeaderSize() const
    {
        return GetCodeSize() + ((EH != 0) ? EH->DataSize() : 0);
    }

    // returns total size of method for use in copying
    int GetOnDiskSize(const COR_ILMETHOD* header)
    {
        return DecoderGetOnDiskSize(this,(COR_ILMETHOD*)header);
    }

    // Flags        these are available because we inherit COR_ILMETHOD_FAT
    // MaxStack
    // CodeSize
    const BYTE *    Code;
    PCCOR_SIGNATURE LocalVarSig;        // pointer to signature blob, or 0 if none
    DWORD           cbLocalVarSig;      // size of dignature blob, or 0 if none
    const COR_ILMETHOD_SECT_EH * EH;    // eh table if any  0 if none
    const COR_ILMETHOD_SECT *    Sect;  // additional sections  0 if none
};  // class COR_ILMETHOD_DECODER

#if defined(CORHLPR_TURNED_FPO_ON)
#pragma optimize("", on)		// Go back to command line default optimizations
#undef CORHLPR_TURNED_FPO_ON
#undef FPO_ON
#endif

#endif // __CORHLPR_H__
