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

#ifndef __dllimport_h__
#define __dllimport_h__

#include "util.hpp"

class ILStubHashBlob;
class NDirectStubParameters;
struct PInvokeStaticSigInfo;
class LoadLibErrorTracker;

// This structure groups together data that describe the signature for which a marshaling stub is being generated.
struct StubSigDesc
{
public:
    StubSigDesc(MethodDesc * pMD, PInvokeStaticSigInfo* pSigInfo = NULL);
    StubSigDesc(MethodDesc*  pMD, Signature sig, Module* m_pModule);
    StubSigDesc(MethodTable* pMT, Signature sig, Module* m_pModule);
    StubSigDesc(std::nullptr_t, Signature sig, Module* m_pModule);

    MethodDesc        *m_pMD;
    MethodTable       *m_pMT;
    Signature          m_sig;
    Module            *m_pModule;
    Module            *m_pLoaderModule;
    mdMethodDef        m_tkMethodDef;
    SigTypeContext     m_typeContext;

#ifdef _DEBUG
    LPCUTF8            m_pDebugName;
    LPCUTF8            m_pDebugClassName;

    void InitDebugNames()
    {
        LIMITED_METHOD_CONTRACT;

        if (m_pMD != NULL)
        {
            m_pDebugName = m_pMD->m_pszDebugMethodName;
            m_pDebugClassName = m_pMD->m_pszDebugClassName;
        }
        else
        {
            m_pDebugName = NULL;
            m_pDebugClassName = NULL;
        }
    }
#endif // _DEBUG
};

//=======================================================================
// Collects code and data pertaining to the NDirect interface.
//=======================================================================
class NDirect
{
    friend class NDirectMethodDesc;

public:
    //---------------------------------------------------------
    // Does a class or method have a NAT_L CustomAttribute?
    //
    // S_OK    = yes
    // S_FALSE = no
    // FAILED  = unknown because something failed.
    //---------------------------------------------------------
    static HRESULT HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs);

    static LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, NATIVE_LIBRARY_HANDLE hMod);
    static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError);
    static NATIVE_LIBRARY_HANDLE LoadLibraryByName(LPCWSTR name, Assembly *callingAssembly,
                                                   BOOL hasDllImportSearchPathFlags, DWORD dllImportSearchPathFlags,
                                                   BOOL throwOnError);
    static NATIVE_LIBRARY_HANDLE LoadNativeLibrary(NDirectMethodDesc * pMD, LoadLibErrorTracker *pErrorTracker);
    static void FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle);
    static INT_PTR GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError);

    static VOID NDirectLink(NDirectMethodDesc *pMD);

    // Either MD or signature & module must be given.
    static BOOL MarshalingRequired(
        _In_opt_ MethodDesc* pMD,
        _In_opt_ PCCOR_SIGNATURE pSig = NULL,
        _In_opt_ Module* pModule = NULL,
        _In_ bool unmanagedCallersOnlyRequiresMarshalling = true);
    static void PopulateNDirectMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, BOOL throwOnError = TRUE);

    static MethodDesc* CreateCLRToNativeILStub(
                    StubSigDesc*       pSigDesc,
                    CorNativeLinkType  nlType,
                    CorNativeLinkFlags nlFlags,
                    CorPinvokeMap      unmgdCallConv,
                    DWORD              dwStubFlags); // NDirectStubFlags

#ifdef FEATURE_COMINTEROP
    static MethodDesc* CreateFieldAccessILStub(
                    PCCOR_SIGNATURE    szMetaSig,
                    DWORD              cbMetaSigSize,
                    Module*            pModule,
                    mdFieldDef         fd,
                    DWORD              dwStubFlags, // NDirectStubFlags
                    FieldDesc*         pFD);
#endif // FEATURE_COMINTEROP

    static MethodDesc* CreateStructMarshalILStub(MethodTable* pMT);
    static PCODE GetEntryPointForStructMarshalStub(MethodTable* pMT);

    static MethodDesc* CreateCLRToNativeILStub(PInvokeStaticSigInfo* pSigInfo,
                             DWORD dwStubFlags,
                             MethodDesc* pMD);

    static MethodDesc*      GetILStubMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwNGenStubFlags);
    static MethodDesc*      GetStubMethodDesc(MethodDesc *pTargetMD, NDirectStubParameters* pParams, ILStubHashBlob* pHashParams, AllocMemTracker* pamTracker, bool& bILStubCreator, MethodDesc* pLastMD);
    static void             AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD);
    static void             RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams);
    static ILStubHashBlob*  CreateHashBlob(NDirectStubParameters* pParams);
    static PCODE            GetStubForILStub(NDirectMethodDesc* pNMD, MethodDesc** ppStubMD, DWORD dwStubFlags);
    static PCODE            GetStubForILStub(MethodDesc* pMD, MethodDesc** ppStubMD, DWORD dwStubFlags);

    inline static ILStubCache*     GetILStubCache(NDirectStubParameters* pParams);

private:
    NDirect() {LIMITED_METHOD_CONTRACT;};     // prevent "new"'s on this class
};

//----------------------------------------------------------------
// Flags passed to CreateNDirectStub that control stub generation
//----------------------------------------------------------------
enum NDirectStubFlags
{
    NDIRECTSTUB_FL_CONVSIGASVARARG          = 0x00000001,
    NDIRECTSTUB_FL_BESTFIT                  = 0x00000002,
    NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR    = 0x00000004,
    NDIRECTSTUB_FL_NGENEDSTUB               = 0x00000008,
    NDIRECTSTUB_FL_DELEGATE                 = 0x00000010,
    NDIRECTSTUB_FL_DOHRESULTSWAPPING        = 0x00000020,
    NDIRECTSTUB_FL_REVERSE_INTEROP          = 0x00000040,
#ifdef FEATURE_COMINTEROP
    NDIRECTSTUB_FL_COM                      = 0x00000080,
#endif // FEATURE_COMINTEROP
    NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING   = 0x00000100,
    NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL     = 0x00000200,
    NDIRECTSTUB_FL_STRUCT_MARSHAL           = 0x00000400,
    NDIRECTSTUB_FL_UNMANAGED_CALLI          = 0x00000800,
    NDIRECTSTUB_FL_TRIGGERCCTOR             = 0x00001000,
#ifdef FEATURE_COMINTEROP
    NDIRECTSTUB_FL_FIELDGETTER              = 0x00002000, // COM->CLR field getter
    NDIRECTSTUB_FL_FIELDSETTER              = 0x00004000, // COM->CLR field setter
    // unused                               = 0x00008000,
    // unused                               = 0x00010000,
    // unused                               = 0x00020000,
    // unused                               = 0x00080000,
    // unused                               = 0x00100000,
    // unused                               = 0x00200000,
    // unused                               = 0x00400000,
    // unused                               = 0x00800000,
#endif // FEATURE_COMINTEROP

    // internal flags -- these won't ever show up in an NDirectStubHashBlob
    NDIRECTSTUB_FL_FOR_NUMPARAMBYTES        = 0x10000000,   // do just enough to return the right value from Marshal.NumParamBytes

#ifdef FEATURE_COMINTEROP
    NDIRECTSTUB_FL_COMLATEBOUND             = 0x20000000,   // we use a generic stub for late bound calls
    NDIRECTSTUB_FL_COMEVENTCALL             = 0x40000000,   // we use a generic stub for event calls
#endif // FEATURE_COMINTEROP

    // Note: The upper half of the range is reserved for ILStubTypes enum
    NDIRECTSTUB_FL_MASK                     = 0x7FFFFFFF,
    NDIRECTSTUB_FL_INVALID                  = 0x80000000,
};

enum ILStubTypes
{
    ILSTUB_INVALID                       = 0x80000000,
#ifdef FEATURE_ARRAYSTUB_AS_IL
    ILSTUB_ARRAYOP_GET                   = 0x80000001,
    ILSTUB_ARRAYOP_SET                   = 0x80000002,
    ILSTUB_ARRAYOP_ADDRESS               = 0x80000003,
#endif
#ifdef FEATURE_MULTICASTSTUB_AS_IL
    ILSTUB_MULTICASTDELEGATE_INVOKE      = 0x80000004,
#endif
#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
    ILSTUB_UNBOXINGILSTUB                = 0x80000005,
    ILSTUB_INSTANTIATINGSTUB             = 0x80000006,
#endif
    ILSTUB_WRAPPERDELEGATE_INVOKE        = 0x80000007,
    ILSTUB_TAILCALL_STOREARGS            = 0x80000008,
    ILSTUB_TAILCALL_CALLTARGET           = 0x80000009,
    ILSTUB_TAILCALL_DISPATCH             = 0x8000000A,
};

#ifdef FEATURE_COMINTEROP
#define COM_ONLY(x) (x)
#else // FEATURE_COMINTEROP
#define COM_ONLY(x) false
#endif // FEATURE_COMINTEROP

inline bool SF_IsVarArgStub            (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_CONVSIGASVARARG)); }
inline bool SF_IsBestFit               (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_BESTFIT)); }
inline bool SF_IsThrowOnUnmappableChar (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR)); }
inline bool SF_IsNGENedStub            (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_NGENEDSTUB)); }
inline bool SF_IsDelegateStub          (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_DELEGATE)); }
inline bool SF_IsHRESULTSwapping       (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_DOHRESULTSWAPPING)); }
inline bool SF_IsReverseStub           (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)); }
inline bool SF_IsNGENedStubForProfiling(DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING)); }
inline bool SF_IsDebuggableStub        (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL)); }
inline bool SF_IsCALLIStub             (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_UNMANAGED_CALLI)); }
inline bool SF_IsStubWithCctorTrigger  (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_TRIGGERCCTOR)); }
inline bool SF_IsForNumParamBytes      (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_FOR_NUMPARAMBYTES)); }
inline bool SF_IsStructMarshalStub     (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_STRUCT_MARSHAL)); }

#ifdef FEATURE_ARRAYSTUB_AS_IL
inline bool SF_IsArrayOpStub           (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return ((dwStubFlags == ILSTUB_ARRAYOP_GET) ||
                                                                                              (dwStubFlags == ILSTUB_ARRAYOP_SET) ||
                                                                                              (dwStubFlags == ILSTUB_ARRAYOP_ADDRESS)); }
#endif

#ifdef FEATURE_MULTICASTSTUB_AS_IL
inline bool SF_IsMulticastDelegateStub  (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_MULTICASTDELEGATE_INVOKE); }
#endif

inline bool SF_IsWrapperDelegateStub    (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_WRAPPERDELEGATE_INVOKE); }
#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
inline bool SF_IsUnboxingILStub         (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_UNBOXINGILSTUB); }
inline bool SF_IsInstantiatingStub      (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_INSTANTIATINGSTUB); }
#endif
inline bool SF_IsTailCallStoreArgsStub  (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_STOREARGS); }
inline bool SF_IsTailCallCallTargetStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_CALLTARGET); }
inline bool SF_IsTailCallDispatcherStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_DISPATCH); }

inline bool SF_IsCOMStub               (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_COM)); }
inline bool SF_IsCOMLateBoundStub      (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_COMLATEBOUND)); }
inline bool SF_IsCOMEventCallStub      (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_COMEVENTCALL)); }
inline bool SF_IsFieldGetterStub       (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_FIELDGETTER)); }
inline bool SF_IsFieldSetterStub       (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_FIELDSETTER)); }

inline bool SF_IsSharedStub(DWORD dwStubFlags)
{
    WRAPPER_NO_CONTRACT;

    if (SF_IsTailCallStoreArgsStub(dwStubFlags) || SF_IsTailCallCallTargetStub(dwStubFlags) ||
        SF_IsTailCallDispatcherStub(dwStubFlags))
    {
        return false;
    }

    if (SF_IsFieldGetterStub(dwStubFlags) || SF_IsFieldSetterStub(dwStubFlags))
    {
        return false;
    }

    return true;
}

inline bool SF_IsForwardStub             (DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; return !SF_IsReverseStub(dwStubFlags); }

inline bool SF_IsForwardPInvokeStub      (DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; return (!SF_IsCOMStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags)); }
inline bool SF_IsReversePInvokeStub      (DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; return (!SF_IsCOMStub(dwStubFlags) && SF_IsReverseStub(dwStubFlags)); }

inline bool SF_IsForwardCOMStub          (DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; return (SF_IsCOMStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags)); }
inline bool SF_IsReverseCOMStub          (DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; return (SF_IsCOMStub(dwStubFlags) && SF_IsReverseStub(dwStubFlags)); }

inline bool SF_IsForwardDelegateStub     (DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; return (SF_IsDelegateStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags)); }
inline bool SF_IsReverseDelegateStub     (DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; return (SF_IsDelegateStub(dwStubFlags) && SF_IsReverseStub(dwStubFlags)); }

#undef COM_ONLY

inline void SF_ConsistencyCheck(DWORD dwStubFlags)
{
    LIMITED_METHOD_CONTRACT;

    // Late bound and event calls imply COM
    CONSISTENCY_CHECK(!(SF_IsCOMLateBoundStub(dwStubFlags) && !SF_IsCOMStub(dwStubFlags)));
    CONSISTENCY_CHECK(!(SF_IsCOMEventCallStub(dwStubFlags) && !SF_IsCOMStub(dwStubFlags)));

    // Field accessors imply reverse COM
    CONSISTENCY_CHECK(!(SF_IsFieldGetterStub(dwStubFlags) && !SF_IsReverseCOMStub(dwStubFlags)));
    CONSISTENCY_CHECK(!(SF_IsFieldSetterStub(dwStubFlags) && !SF_IsReverseCOMStub(dwStubFlags)));

    // Field accessors are always HRESULT swapping
    CONSISTENCY_CHECK(!(SF_IsFieldGetterStub(dwStubFlags) && !SF_IsHRESULTSwapping(dwStubFlags)));
    CONSISTENCY_CHECK(!(SF_IsFieldSetterStub(dwStubFlags) && !SF_IsHRESULTSwapping(dwStubFlags)));

    // Delegate stubs are not COM
    CONSISTENCY_CHECK(!(SF_IsDelegateStub(dwStubFlags) && SF_IsCOMStub(dwStubFlags)));

    // Struct marshal stubs are not COM or HRESULT swapping stubs
    CONSISTENCY_CHECK(!(SF_IsStructMarshalStub(dwStubFlags) && SF_IsCOMStub(dwStubFlags)));
    CONSISTENCY_CHECK(!(SF_IsStructMarshalStub(dwStubFlags) && SF_IsHRESULTSwapping(dwStubFlags)));
    CONSISTENCY_CHECK(!(SF_IsStructMarshalStub(dwStubFlags) && SF_IsReverseCOMStub(dwStubFlags)));
}

enum ETW_IL_STUB_FLAGS
{
    ETW_IL_STUB_FLAGS_REVERSE_INTEROP       = 0x00000001,
    ETW_IL_STUB_FLAGS_COM_INTEROP           = 0x00000002,
    ETW_IL_STUB_FLAGS_NGENED_STUB           = 0x00000004,
    ETW_IL_STUB_FLAGS_DELEGATE              = 0x00000008,
    ETW_IL_STUB_FLAGS_VARARG                = 0x00000010,
    ETW_IL_STUB_FLAGS_UNMANAGED_CALLI       = 0x00000020,
    ETW_IL_STUB_FLAGS_STRUCT_MARSHAL        = 0x00000040
};

//---------------------------------------------------------
// PInvoke has three flavors: DllImport M->U, Delegate M->U and Delegate U->M
// Each flavor uses rougly the same mechanism to marshal and place the call and so
// each flavor supports roughly the same switches. Those switches which can be
// statically determined via CAs (DllImport, UnmanagedFunctionPointer,
// BestFitMappingAttribute, etc) or via MetaSig are parsed and unified by this
// class. There are two flavors of constructor, one for NDirectMethodDescs and one
// for Delegates.
//---------------------------------------------------------
struct PInvokeStaticSigInfo
{
public:
    enum ThrowOnError { THROW_ON_ERROR = TRUE, NO_THROW_ON_ERROR = FALSE };

public:
    PInvokeStaticSigInfo() { LIMITED_METHOD_CONTRACT; }

    PInvokeStaticSigInfo(Signature sig, Module* pModule, ThrowOnError throwOnError = THROW_ON_ERROR);

    PInvokeStaticSigInfo(MethodDesc* pMdDelegate, ThrowOnError throwOnError = THROW_ON_ERROR);

    PInvokeStaticSigInfo(MethodDesc* pMD, LPCUTF8 *pLibName, LPCUTF8 *pEntryPointName, ThrowOnError throwOnError = THROW_ON_ERROR);

public:
    void ReportErrors();

private:
    void InitCallConv(CorPinvokeMap callConv, BOOL bIsVarArg);
    void DllImportInit(MethodDesc* pMD, LPCUTF8 *pLibName, LPCUTF8 *pEntryPointName);
    void PreInit(Module* pModule, MethodTable *pClass);
    void PreInit(MethodDesc* pMD);
    void SetError(WORD error) { if (!m_error) m_error = error; }
    void BestGuessNDirectDefaults(MethodDesc* pMD);

public:
    DWORD GetStubFlags()
    {
        WRAPPER_NO_CONTRACT;
        return (GetThrowOnUnmappableChar() ? NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR : 0) |
               (GetBestFitMapping() ? NDIRECTSTUB_FL_BESTFIT : 0) |
               (IsDelegateInterop() ? NDIRECTSTUB_FL_DELEGATE : 0);
    }
    Module* GetModule() { LIMITED_METHOD_CONTRACT; return m_pModule; }
    BOOL IsStatic() { LIMITED_METHOD_CONTRACT; return m_wFlags & PINVOKE_STATIC_SIGINFO_IS_STATIC; }
    void SetIsStatic (BOOL isStatic)
    {
        LIMITED_METHOD_CONTRACT;
        if (isStatic)
            m_wFlags |= PINVOKE_STATIC_SIGINFO_IS_STATIC;
        else
            m_wFlags &= ~PINVOKE_STATIC_SIGINFO_IS_STATIC;
    }
    BOOL GetThrowOnUnmappableChar() { LIMITED_METHOD_CONTRACT; return m_wFlags & PINVOKE_STATIC_SIGINFO_THROW_ON_UNMAPPABLE_CHAR; }
    void SetThrowOnUnmappableChar (BOOL throwOnUnmappableChar)
    {
        LIMITED_METHOD_CONTRACT;
        if (throwOnUnmappableChar)
            m_wFlags |= PINVOKE_STATIC_SIGINFO_THROW_ON_UNMAPPABLE_CHAR;
        else
            m_wFlags &= ~PINVOKE_STATIC_SIGINFO_THROW_ON_UNMAPPABLE_CHAR;
    }
    BOOL GetBestFitMapping() { LIMITED_METHOD_CONTRACT; return m_wFlags & PINVOKE_STATIC_SIGINFO_BEST_FIT; }
    void SetBestFitMapping (BOOL bestFit)
    {
        LIMITED_METHOD_CONTRACT;
        if (bestFit)
            m_wFlags |= PINVOKE_STATIC_SIGINFO_BEST_FIT;
        else
            m_wFlags &= ~PINVOKE_STATIC_SIGINFO_BEST_FIT;
    }
    BOOL IsDelegateInterop() { LIMITED_METHOD_CONTRACT; return m_wFlags & PINVOKE_STATIC_SIGINFO_IS_DELEGATE_INTEROP; }
    void SetIsDelegateInterop (BOOL delegateInterop)
    {
        LIMITED_METHOD_CONTRACT;
        if (delegateInterop)
            m_wFlags |= PINVOKE_STATIC_SIGINFO_IS_DELEGATE_INTEROP;
        else
            m_wFlags &= ~PINVOKE_STATIC_SIGINFO_IS_DELEGATE_INTEROP;
    }
    CorPinvokeMap GetCallConv() { LIMITED_METHOD_CONTRACT; return m_callConv; }
    Signature GetSignature() { LIMITED_METHOD_CONTRACT; return m_sig; }

private:
    Module* m_pModule;
    Signature m_sig;
    CorPinvokeMap m_callConv;
    WORD m_error;

    enum
    {
        PINVOKE_STATIC_SIGINFO_IS_STATIC = 0x0001,
        PINVOKE_STATIC_SIGINFO_THROW_ON_UNMAPPABLE_CHAR = 0x0002,
        PINVOKE_STATIC_SIGINFO_BEST_FIT = 0x0004,

        COR_NATIVE_LINK_TYPE_MASK = 0x0038,  // 0000 0000 0011 1000  <--- These 3 1's make the link type mask

        COR_NATIVE_LINK_FLAGS_MASK = 0x00C0, //0000 0000 1100 0000  <---- These 2 bits make up the link flags

        PINVOKE_STATIC_SIGINFO_IS_DELEGATE_INTEROP = 0x0100,

    };
    #define COR_NATIVE_LINK_TYPE_SHIFT 3 // Keep in synch with above mask
    #define COR_NATIVE_LINK_FLAGS_SHIFT 6  // Keep in synch with above mask
    WORD m_wFlags;

  public:
    CorNativeLinkType GetCharSet() { LIMITED_METHOD_CONTRACT; return (CorNativeLinkType)((m_wFlags & COR_NATIVE_LINK_TYPE_MASK) >> COR_NATIVE_LINK_TYPE_SHIFT); }
    CorNativeLinkFlags GetLinkFlags() { LIMITED_METHOD_CONTRACT; return (CorNativeLinkFlags)((m_wFlags & COR_NATIVE_LINK_FLAGS_MASK) >> COR_NATIVE_LINK_FLAGS_SHIFT); }
    void SetCharSet(CorNativeLinkType linktype)
    {
        LIMITED_METHOD_CONTRACT;
        _ASSERTE( linktype == (linktype & (COR_NATIVE_LINK_TYPE_MASK >> COR_NATIVE_LINK_TYPE_SHIFT)));
        // Clear out the old value first
        m_wFlags &= (~COR_NATIVE_LINK_TYPE_MASK);
        // Then set the given value
        m_wFlags |= (linktype << COR_NATIVE_LINK_TYPE_SHIFT);
    }
    void SetLinkFlags(CorNativeLinkFlags linkflags)
    {
        LIMITED_METHOD_CONTRACT;
        _ASSERTE( linkflags == (linkflags & (COR_NATIVE_LINK_FLAGS_MASK >> COR_NATIVE_LINK_FLAGS_SHIFT)));
        // Clear out the old value first
        m_wFlags &= (~COR_NATIVE_LINK_FLAGS_MASK);
        // Then set the given value
        m_wFlags |= (linkflags << COR_NATIVE_LINK_FLAGS_SHIFT);
    }
};


#include "stubgen.h"

#ifndef DACCESS_COMPILE
class NDirectStubLinker : public ILStubLinker
{
public:
    NDirectStubLinker(
                DWORD dwStubFlags,
                Module* pModule,
                const Signature &signature,
                SigTypeContext *pTypeContext,
                MethodDesc* pTargetMD,
                int  iLCIDParamIdx,
                BOOL fTargetHasThis,
                BOOL fStubHasThis);

    void    SetCallingConvention(CorPinvokeMap unmngCallConv, BOOL fIsVarArg);

    void    Begin(DWORD dwStubFlags);
    void    End(DWORD dwStubFlags);
    void    DoNDirect(ILCodeStream *pcsEmit, DWORD dwStubFlags, MethodDesc * pStubMD);
    void    EmitLogNativeArgument(ILCodeStream* pslILEmit, DWORD dwPinnedLocal);
    void    LoadCleanupWorkList(ILCodeStream* pcsEmit);
#ifdef PROFILING_SUPPORTED
    DWORD   EmitProfilerBeginTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags);
    void    EmitProfilerEndTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags, DWORD dwMethodDescLocalNum);
#endif
#ifdef VERIFY_HEAP
    void    EmitValidateLocal(ILCodeStream* pcsEmit, DWORD dwLocalNum, bool fIsByref, DWORD dwStubFlags);
    void    EmitObjectValidation(ILCodeStream* pcsEmit, DWORD dwStubFlags);
#endif // VERIFY_HEAP
    void    EmitLoadStubContext(ILCodeStream* pcsEmit, DWORD dwStubFlags);
    void    GenerateInteropParamException(ILCodeStream* pcsEmit);
    void    NeedsCleanupList();

#ifdef FEATURE_COMINTEROP
    DWORD   GetTargetInterfacePointerLocalNum();
    DWORD   GetTargetEntryPointLocalNum();
    void    EmitLoadRCWThis(ILCodeStream *pcsEmit, DWORD dwStubFlags);
#endif // FEATURE_COMINTEROP
    DWORD   GetCleanupWorkListLocalNum();
    DWORD   GetThreadLocalNum();
    DWORD   GetReturnValueLocalNum();
    void    SetCleanupNeeded();
    void    SetExceptionCleanupNeeded();
    BOOL    IsCleanupWorkListSetup();
    void    GetCleanupFinallyOffsets(ILStubEHClause * pClause);

    void    SetInteropParamExceptionInfo(UINT resID, UINT paramIdx);
    bool    HasInteropParamExceptionInfo();
    bool    TargetHasThis()
    {
        return m_targetHasThis == TRUE;
    }

    void ClearCode();

    enum
    {
        CLEANUP_INDEX_ARG0_MARSHAL     = 0x00000000,  // cleanup index of the first argument (marshal and retval unmarshal stream)
        CLEANUP_INDEX_RETVAL_UNMARSHAL = 0x3fffffff,  // cleanup index of the return value (retval unmarshal stream)
        CLEANUP_INDEX_ARG0_UNMARSHAL   = 0x40000000,  // cleanup index of the first argument (unmarshal stream)
        CLEANUP_INDEX_ALL_DONE         = 0x7ffffffe   // everything was successfully marshaled and unmarshaled, no exception thrown
    };

    enum ArgCleanupBranchKind
    {
        BranchIfMarshaled,
        BranchIfNotMarshaled
    };

    void    EmitSetArgMarshalIndex(ILCodeStream* pcsEmit, UINT uArgIdx);
    void    EmitCheckForArgCleanup(ILCodeStream* pcsEmit, UINT uArgIdx, ArgCleanupBranchKind branchKind, ILCodeLabel* pSkipCleanupLabel);

    int     GetLCIDParamIdx();

    ILCodeStream* GetSetupCodeStream();
    ILCodeStream* GetMarshalCodeStream();
    ILCodeStream* GetUnmarshalCodeStream();
    ILCodeStream* GetReturnUnmarshalCodeStream();
    ILCodeStream* GetDispatchCodeStream();
    ILCodeStream* GetCleanupCodeStream();
    ILCodeStream* GetExceptionCleanupCodeStream();

protected:
    BOOL            IsCleanupNeeded();
    BOOL            IsExceptionCleanupNeeded();
    void            InitCleanupCode();
    void            InitExceptionCleanupCode();



    ILCodeStream*   m_pcsSetup;
    ILCodeStream*   m_pcsMarshal;
    ILCodeStream*   m_pcsDispatch;
    ILCodeStream*   m_pcsRetUnmarshal;
    ILCodeStream*   m_pcsUnmarshal;
    ILCodeStream*   m_pcsExceptionCleanup;
    ILCodeStream*   m_pcsCleanup;


    ILCodeLabel*        m_pCleanupTryBeginLabel;
    ILCodeLabel*        m_pCleanupTryEndLabel;
    ILCodeLabel*        m_pCleanupFinallyBeginLabel;
    ILCodeLabel*        m_pCleanupFinallyEndLabel;
    ILCodeLabel*        m_pSkipExceptionCleanupLabel;

#ifdef FEATURE_COMINTEROP
    DWORD               m_dwTargetInterfacePointerLocalNum;
    DWORD               m_dwTargetEntryPointLocalNum;
#endif // FEATURE_COMINTEROP

    BOOL                m_fHasCleanupCode;
    BOOL                m_fHasExceptionCleanupCode;
    BOOL                m_fCleanupWorkListIsSetup;
    BOOL                m_targetHasThis;
    DWORD               m_dwThreadLocalNum;                 // managed-to-native only
    DWORD               m_dwArgMarshalIndexLocalNum;
    DWORD               m_dwCleanupWorkListLocalNum;
    DWORD               m_dwRetValLocalNum;


    UINT                m_ErrorResID;
    UINT                m_ErrorParamIdx;
    int                 m_iLCIDParamIdx;

    DWORD               m_dwStubFlags;
};
#endif // DACCESS_COMPILE

// This attempts to guess whether a target is an API call that uses SetLastError to communicate errors.
BOOL HeuristicDoesThisLooksLikeAnApiCall(LPBYTE pTarget);
BOOL HeuristicDoesThisLookLikeAGetLastErrorCall(LPBYTE pTarget);
DWORD STDMETHODCALLTYPE FalseGetLastError();

class NDirectStubParameters
{
public:

    NDirectStubParameters(Signature          sig,
                          SigTypeContext*    pTypeContext,
                          Module*            pModule,
                          Module*            pLoaderModule,
                          CorNativeLinkType  nlType,
                          CorNativeLinkFlags nlFlags,
                          CorPinvokeMap      unmgdCallConv,
                          DWORD              dwStubFlags,  // NDirectStubFlags
                          int                nParamTokens,
                          mdParamDef*        pParamTokenArray,
                          int                iLCIDArg,
                          MethodTable*       pMT
                          ) :
        m_sig(sig),
        m_pTypeContext(pTypeContext),
        m_pModule(pModule),
        m_pLoaderModule(pLoaderModule),
        m_pParamTokenArray(pParamTokenArray),
        m_unmgdCallConv(unmgdCallConv),
        m_nlType(nlType),
        m_nlFlags(nlFlags),
        m_dwStubFlags(dwStubFlags),
        m_iLCIDArg(iLCIDArg),
        m_nParamTokens(nParamTokens),
        m_pMT(pMT)
    {
        LIMITED_METHOD_CONTRACT;
    }

    Signature           m_sig;
    SigTypeContext*     m_pTypeContext;
    Module*             m_pModule;
    Module*             m_pLoaderModule;
    mdParamDef*         m_pParamTokenArray;
    CorPinvokeMap       m_unmgdCallConv;
    CorNativeLinkType   m_nlType;
    CorNativeLinkFlags  m_nlFlags;
    DWORD               m_dwStubFlags;
    int                 m_iLCIDArg;
    int                 m_nParamTokens;
    MethodTable*        m_pMT;
};

PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD);

MethodDesc *GetStubMethodDescFromInteropMethodDesc(MethodDesc* pMD, DWORD dwStubFlags);
PCODE JitILStub(MethodDesc* pStubMD);
MethodDesc *RestoreNGENedStub(MethodDesc* pStubMD);
PCODE GetStubForInteropMethod(MethodDesc* pMD, DWORD dwStubFlags = 0, MethodDesc **ppStubMD = NULL);

#ifdef FEATURE_COMINTEROP
// Resolve and return the predefined IL stub method
HRESULT FindPredefinedILStubMethod(MethodDesc *pTargetMD, DWORD dwStubFlags, MethodDesc **ppRetStubMD);
#endif // FEATURE_COMINTEROP

#ifndef DACCESS_COMPILE
void MarshalStructViaILStub(MethodDesc* pStubMD, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList = nullptr);
void MarshalStructViaILStubCode(PCODE pStubCode, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList = nullptr);
#endif // DACCESS_COMPILE

//
// Limit length of string field in IL stub ETW events so that the whole
// IL stub ETW events won't exceed 64KB
//
#define ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE      (1024)
#define ETW_IL_STUB_EVENT_CODE_STRING_FIELD_MAXSIZE (1024*32)

class SString;

//
// Truncates a SString by first converting it to unicode and truncate it
// if it is larger than size. "..." will be appened if it is truncated.
//
void TruncateUnicodeString(SString &string, COUNT_T bufSize);

//=======================================================================
// ILStubCreatorHelper
// The class is used as a helper class in CreateInteropILStub. It mainly
// puts two methods NDirect::GetStubMethodDesc and NDirect::RemoveILStubCacheEntry
// into a holder. See CreateInteropILStub for more information
//=======================================================================
class ILStubCreatorHelper
{
public:
    ILStubCreatorHelper(MethodDesc *pTargetMD,
                        NDirectStubParameters* pParams
                        ) :
        m_pTargetMD(pTargetMD),
        m_pParams(pParams),
        m_pStubMD(NULL),
        m_bILStubCreator(false)
    {
        STANDARD_VM_CONTRACT;
        m_pHashParams = NDirect::CreateHashBlob(m_pParams);
    }

    ~ILStubCreatorHelper()
    {
        CONTRACTL
        {
            THROWS;
            GC_TRIGGERS;
            MODE_ANY;
        }
        CONTRACTL_END;

        RemoveILStubCacheEntry();
    }

    inline void GetStubMethodDesc()
    {
        WRAPPER_NO_CONTRACT;

        m_pStubMD = NDirect::GetStubMethodDesc(m_pTargetMD, m_pParams, m_pHashParams, &m_amTracker, m_bILStubCreator, m_pStubMD);
    }

    inline void RemoveILStubCacheEntry()
    {
        WRAPPER_NO_CONTRACT;

        if (m_bILStubCreator)
        {
            NDirect::RemoveILStubCacheEntry(m_pParams, m_pHashParams);
            m_bILStubCreator = false;
        }
    }

    inline MethodDesc* GetStubMD()
    {
        LIMITED_METHOD_CONTRACT;
        return m_pStubMD;
    }

    inline void SuppressRelease()
    {
        WRAPPER_NO_CONTRACT;
        m_bILStubCreator = false;
        m_amTracker.SuppressRelease();
    }

    DEBUG_NOINLINE static void HolderEnter(ILStubCreatorHelper *pThis)
    {
        WRAPPER_NO_CONTRACT;
        ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
        pThis->GetStubMethodDesc();
    }

    DEBUG_NOINLINE static void HolderLeave(ILStubCreatorHelper *pThis)
    {
        WRAPPER_NO_CONTRACT;
        ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
        pThis->RemoveILStubCacheEntry();
    }

private:
    MethodDesc*                      m_pTargetMD;
    NDirectStubParameters*           m_pParams;
    NewArrayHolder<ILStubHashBlob>   m_pHashParams;
    AllocMemTracker*                 m_pAmTracker;
    MethodDesc*                      m_pStubMD;
    AllocMemTracker                  m_amTracker;
    bool                             m_bILStubCreator;     // Only the creator can remove the ILStub from the Cache
};  //ILStubCreatorHelper

typedef Wrapper<ILStubCreatorHelper*, ILStubCreatorHelper::HolderEnter, ILStubCreatorHelper::HolderLeave> ILStubCreatorHelperHolder;

#endif // __dllimport_h__
