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

#ifndef __CALLCONVBUILDER_H__
#define __CALLCONVBUILDER_H__

#include "corinfo.h"

// Attempts to parse the provided calling convention names to construct a
// calling convention with known modifiers.
//
// The Add* functions return false if the type is known but cannot be applied.
// Otherwise, they return true.
class CallConvBuilder final
{
public:
    enum CallConvModifiers
    {
        CALL_CONV_MOD_NONE = 0,
        CALL_CONV_MOD_SUPPRESSGCTRANSITION = 0x1,
        CALL_CONV_MOD_MEMBERFUNCTION = 0x2
    };

    struct State
    {
        CorInfoCallConvExtension CallConvBase;
        enum CallConvModifiers CallConvModifiers;
    };

private:
    State _state;

public:
    // The initial "unset" base calling convention.
    static const CorInfoCallConvExtension UnsetValue;

    CallConvBuilder();

    // Add a fully qualified type name to the calling convention computation.
    bool AddFullyQualifiedTypeName(
        _In_ size_t typeLength,
        _In_z_ LPCSTR typeName);

    // Add a simple type name to the calling convention computation.
    bool AddTypeName(
        _In_ size_t typeLength,
        _In_z_ LPCSTR typeName);

    // Get the currently computed calling convention value.
    CorInfoCallConvExtension GetCurrentCallConv() const;

    // Check if the modifier is set on the computed calling convention.
    bool IsCurrentCallConvModSet(_In_ CallConvModifiers mod) const;
};

namespace CallConv
{
    static CorInfoCallConvExtension GetDefaultUnmanagedCallingConvention()
    {
#ifdef TARGET_UNIX
        return CorInfoCallConvExtension::C;
#else // TARGET_UNIX
        return CorInfoCallConvExtension::Stdcall;
#endif // !TARGET_UNIX
    }

    //-------------------------------------------------------------------------
    // Gets the unmanaged calling convention by reading any modopts.
    //
    // Returns:
    //   S_OK - No errors
    //   COR_E_BADIMAGEFORMAT - Signature had an invalid format
    //   COR_E_INVALIDPROGRAM - Program is considered invalid (more
    //                          than one calling convention specified)
    //-------------------------------------------------------------------------
    HRESULT TryGetUnmanagedCallingConventionFromModOpt(
        _In_ CORINFO_MODULE_HANDLE pModule,
        _In_ PCCOR_SIGNATURE pSig,
        _In_ ULONG cSig,
        _Inout_ CallConvBuilder *builder,
        _Out_ UINT* errorResID);

    //-------------------------------------------------------------------------
    // Gets the calling convention from the UnmanagedCallConv attribute
    //
    // Returns:
    //   S_OK - No errors
    //   S_FALSE - UnmanagedCallConv or UnmanagedCallConv.CallConvs not specified
    //   COR_E_INVALIDPROGRAM - Program is considered invalid (more
    //                          than one calling convention specified)
    //-------------------------------------------------------------------------
    HRESULT TryGetCallingConventionFromUnmanagedCallConv(
        _In_ MethodDesc* pMD,
        _Inout_ CallConvBuilder* builder,
        _Out_opt_ UINT* errorResID);

    //-------------------------------------------------------------------------
    // Gets the unmanaged calling convention from the UnmanagedCallersOnly attribute.
    //
    // Returns:
    //   true  - No errors
    //   false - Not specified or invalid (more than one calling convention specified)
    //-------------------------------------------------------------------------
    bool TryGetCallingConventionFromUnmanagedCallersOnly(_In_ MethodDesc* pMD, _Out_ CorInfoCallConvExtension* callConv);
}

#endif // __CALLCONVBUILDER_H__
