diff options
Diffstat (limited to 'src/vm/mlinfo.cpp')
-rw-r--r-- | src/vm/mlinfo.cpp | 5395 |
1 files changed, 5395 insertions, 0 deletions
diff --git a/src/vm/mlinfo.cpp b/src/vm/mlinfo.cpp new file mode 100644 index 0000000000..ab2545296e --- /dev/null +++ b/src/vm/mlinfo.cpp @@ -0,0 +1,5395 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// File: mlinfo.cpp +// + +// + + +#include "common.h" +#include "mlinfo.h" +#include "dllimport.h" +#include "sigformat.h" +#include "eeconfig.h" +#include "eehash.h" +#include "../dlls/mscorrc/resource.h" +#include "mdaassistants.h" +#include "typeparse.h" +#include "comdelegate.h" +#include "olevariant.h" +#include "ilmarshalers.h" +#include "interoputil.h" + +#ifdef FEATURE_PREJIT + #include "dataimage.h" +#endif + +#ifdef FEATURE_COMINTEROP +#include "comcallablewrapper.h" +#include "runtimecallablewrapper.h" +#include "dispparammarshaler.h" +#include "winrttypenameconverter.h" +#endif // FEATURE_COMINTEROP + + +#ifndef lengthof + #define lengthof(rg) (sizeof(rg)/sizeof(rg[0])) +#endif + + +#ifdef FEATURE_COMINTEROP + DEFINE_ASM_QUAL_TYPE_NAME(ENUMERATOR_TO_ENUM_VARIANT_CM_NAME, g_EnumeratorToEnumClassName, "CustomMarshalers", VER_ASSEMBLYVERSION_STR, g_FXKeyToken); + + static const int ENUMERATOR_TO_ENUM_VARIANT_CM_NAME_LEN = lengthof(ENUMERATOR_TO_ENUM_VARIANT_CM_NAME); + static const char ENUMERATOR_TO_ENUM_VARIANT_CM_COOKIE[] = {""}; + static const int ENUMERATOR_TO_ENUM_VARIANT_CM_COOKIE_LEN = lengthof(ENUMERATOR_TO_ENUM_VARIANT_CM_COOKIE); + + DEFINE_ASM_QUAL_TYPE_NAME(COLOR_TRANSLATOR_ASM_QUAL_TYPE_NAME, g_ColorTranslatorClassName, g_DrawingAsmName, VER_ASSEMBLYVERSION_STR, g_FXKeyToken); + DEFINE_ASM_QUAL_TYPE_NAME(COLOR_ASM_QUAL_TYPE_NAME, g_ColorClassName, g_DrawingAsmName, VER_ASSEMBLYVERSION_STR, g_FXKeyToken); + + DEFINE_ASM_QUAL_TYPE_NAME(URI_ASM_QUAL_TYPE_NAME, g_SystemUriClassName, g_SystemRuntimeAsmName, VER_ASSEMBLYVERSION_STR, g_FXKeyToken); + + DEFINE_ASM_QUAL_TYPE_NAME(NCCEVENTARGS_ASM_QUAL_TYPE_NAME, g_NotifyCollectionChangedEventArgsName, g_ObjectModelAsmName, VER_ASSEMBLYVERSION_STR, g_FXKeyToken); +#ifdef FEATURE_CORECLR + DEFINE_ASM_QUAL_TYPE_NAME(NCCEVENTARGS_MARSHALER_ASM_QUAL_TYPE_NAME, g_NotifyCollectionChangedEventArgsMarshalerName, g_SystemRuntimeWindowsRuntimeAsmName, VER_ASSEMBLYVERSION_STR, g_ECMAKeyToken); +#else + DEFINE_ASM_QUAL_TYPE_NAME(NCCEVENTARGS_MARSHALER_ASM_QUAL_TYPE_NAME, g_NotifyCollectionChangedEventArgsMarshalerName, g_SystemAsmName, VER_ASSEMBLYVERSION_STR, g_ECMAKeyToken); +#endif + + + DEFINE_ASM_QUAL_TYPE_NAME(PCEVENTARGS_ASM_QUAL_TYPE_NAME, g_PropertyChangedEventArgsName, g_ObjectModelAsmName, VER_ASSEMBLYVERSION_STR, g_FXKeyToken); +#ifdef FEATURE_CORECLR + DEFINE_ASM_QUAL_TYPE_NAME(PCEVENTARGS_MARSHALER_ASM_QUAL_TYPE_NAME, g_PropertyChangedEventArgsMarshalerName, g_SystemRuntimeWindowsRuntimeAsmName, VER_ASSEMBLYVERSION_STR, g_ECMAKeyToken); +#else + DEFINE_ASM_QUAL_TYPE_NAME(PCEVENTARGS_MARSHALER_ASM_QUAL_TYPE_NAME, g_PropertyChangedEventArgsMarshalerName, g_SystemAsmName, VER_ASSEMBLYVERSION_STR, g_ECMAKeyToken); +#endif + + + #define OLECOLOR_TO_SYSTEMCOLOR_METH_NAME "FromOle" + #define SYSTEMCOLOR_TO_OLECOLOR_METH_NAME "ToOle" + + #define EVENTARGS_TO_WINRT_EVENTARGS_METH_NAME "ConvertToNative" + #define WINRT_EVENTARGS_TO_EVENTARGS_METH_NAME "ConvertToManaged" + + #define ORIGINALSTRING_PROPERTY_NAME "OriginalString" +#endif // FEATURE_COMINTEROP + + + +#define INITIAL_NUM_CMHELPER_HASHTABLE_BUCKETS 32 +#define INITIAL_NUM_CMINFO_HASHTABLE_BUCKETS 32 +#define DEBUG_CONTEXT_STR_LEN 2000 + + +//------------------------------------------------------------------------------------- +// Return the copy ctor for a VC class (if any exists) +//------------------------------------------------------------------------------------- +void FindCopyCtor(Module *pModule, MethodTable *pMT, MethodDesc **pMDOut) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; // CompareTypeTokens may trigger GC + MODE_ANY; + } + CONTRACTL_END; + + *pMDOut = NULL; + + HRESULT hr; + mdMethodDef tk; + mdTypeDef cl = pMT->GetCl(); + TypeHandle th = TypeHandle(pMT); + SigTypeContext typeContext(th); + + IMDInternalImport *pInternalImport = pModule->GetMDImport(); + MDEnumHolder hEnumMethod(pInternalImport); + + // + // First try for the new syntax: <MarshalCopy> + // + IfFailThrow(pInternalImport->EnumInit(mdtMethodDef, cl, &hEnumMethod)); + + while (pInternalImport->EnumNext(&hEnumMethod, &tk)) + { + _ASSERTE(TypeFromToken(tk) == mdtMethodDef); + DWORD dwMemberAttrs; + IfFailThrow(pInternalImport->GetMethodDefProps(tk, &dwMemberAttrs)); + + if (IsMdSpecialName(dwMemberAttrs)) + { + ULONG cSig; + PCCOR_SIGNATURE pSig; + LPCSTR pName; + IfFailThrow(pInternalImport->GetNameAndSigOfMethodDef(tk, &pSig, &cSig, &pName)); + + const char *pBaseName = "<MarshalCopy>"; + int ncBaseName = (int)strlen(pBaseName); + int nc = (int)strlen(pName); + if (nc >= ncBaseName && 0 == strcmp(pName + nc - ncBaseName, pBaseName)) + { + MetaSig msig(pSig, cSig, pModule, &typeContext); + + // Looking for the prototype void <MarshalCopy>(Ptr VC, Ptr VC); + if (msig.NumFixedArgs() == 2) + { + if (msig.GetReturnType() == ELEMENT_TYPE_VOID) + { + if (msig.NextArg() == ELEMENT_TYPE_PTR) + { + SigPointer sp1 = msig.GetArgProps(); + IfFailThrow(sp1.GetElemType(NULL)); + CorElementType eType; + IfFailThrow(sp1.GetElemType(&eType)); + if (eType == ELEMENT_TYPE_VALUETYPE) + { + mdToken tk1; + IfFailThrow(sp1.GetToken(&tk1)); + hr = CompareTypeTokensNT(tk1, cl, pModule, pModule); + if (FAILED(hr)) + { + pInternalImport->EnumClose(&hEnumMethod); + ThrowHR(hr); + } + + if (hr == S_OK) + { + if (msig.NextArg() == ELEMENT_TYPE_PTR) + { + SigPointer sp2 = msig.GetArgProps(); + IfFailThrow(sp2.GetElemType(NULL)); + IfFailThrow(sp2.GetElemType(&eType)); + if (eType == ELEMENT_TYPE_VALUETYPE) + { + mdToken tk2; + IfFailThrow(sp2.GetToken(&tk2)); + + hr = (tk2 == tk1) ? S_OK : CompareTypeTokensNT(tk2, cl, pModule, pModule); + if (hr == S_OK) + { + *pMDOut = pModule->LookupMethodDef(tk); + return; + } + } + } + } + } + } + } + } + } + } + } + + // + // Next try the old syntax: global .__ctor + // + IfFailThrow(pInternalImport->EnumGlobalFunctionsInit(&hEnumMethod)); + + while (pInternalImport->EnumNext(&hEnumMethod, &tk)) + { + _ASSERTE(TypeFromToken(tk) == mdtMethodDef); + DWORD dwMemberAttrs; + IfFailThrow(pInternalImport->GetMethodDefProps(tk, &dwMemberAttrs)); + + if (IsMdSpecialName(dwMemberAttrs)) + { + ULONG cSig; + PCCOR_SIGNATURE pSig; + LPCSTR pName; + IfFailThrow(pInternalImport->GetNameAndSigOfMethodDef(tk, &pSig, &cSig, &pName)); + + const char *pBaseName = ".__ctor"; + int ncBaseName = (int)strlen(pBaseName); + int nc = (int)strlen(pName); + if (nc >= ncBaseName && 0 == strcmp(pName + nc - ncBaseName, pBaseName)) + { + + MetaSig msig(pSig, cSig, pModule, &typeContext); + + // Looking for the prototype Ptr VC __ctor(Ptr VC, ByRef VC); + if (msig.NumFixedArgs() == 2) + { + if (msig.GetReturnType() == ELEMENT_TYPE_PTR) + { + SigPointer spret = msig.GetReturnProps(); + IfFailThrow(spret.GetElemType(NULL)); + CorElementType eType; + IfFailThrow(spret.GetElemType(&eType)); + if (eType == ELEMENT_TYPE_VALUETYPE) + { + mdToken tk0; + IfFailThrow(spret.GetToken(&tk0)); + hr = CompareTypeTokensNT(tk0, cl, pModule, pModule); + if (FAILED(hr)) + { + pInternalImport->EnumClose(&hEnumMethod); + ThrowHR(hr); + } + + if (hr == S_OK) + { + if (msig.NextArg() == ELEMENT_TYPE_PTR) + { + SigPointer sp1 = msig.GetArgProps(); + IfFailThrow(sp1.GetElemType(NULL)); + IfFailThrow(sp1.GetElemType(&eType)); + if (eType == ELEMENT_TYPE_VALUETYPE) + { + mdToken tk1; + IfFailThrow(sp1.GetToken(&tk1)); + hr = (tk1 == tk0) ? S_OK : CompareTypeTokensNT(tk1, cl, pModule, pModule); + if (FAILED(hr)) + { + pInternalImport->EnumClose(&hEnumMethod); + ThrowHR(hr); + } + + if (hr == S_OK) + { + if (msig.NextArg() == ELEMENT_TYPE_PTR && + msig.GetArgProps().HasCustomModifier(pModule, "Microsoft.VisualC.IsCXXReferenceModifier", ELEMENT_TYPE_CMOD_OPT)) + { + SigPointer sp2 = msig.GetArgProps(); + IfFailThrow(sp2.GetElemType(NULL)); + IfFailThrow(sp2.GetElemType(&eType)); + if (eType == ELEMENT_TYPE_VALUETYPE) + { + mdToken tk2; + IfFailThrow(sp2.GetToken(&tk2)); + + hr = (tk2 == tk0) ? S_OK : CompareTypeTokensNT(tk2, cl, pModule, pModule); + if (hr == S_OK) + { + *pMDOut = pModule->LookupMethodDef(tk); + return; + } + } + } + } + } + } + } + } + } + } + } + } + } +} + + +//------------------------------------------------------------------------------------- +// Return the destructor for a VC class (if any exists) +//------------------------------------------------------------------------------------- +void FindDtor(Module *pModule, MethodTable *pMT, MethodDesc **pMDOut) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; // CompareTypeTokens may trigger GC + MODE_ANY; + } + CONTRACTL_END; + + *pMDOut = NULL; + + HRESULT hr; + mdMethodDef tk; + mdTypeDef cl = pMT->GetCl(); + TypeHandle th = TypeHandle(pMT); + SigTypeContext typeContext(th); + + IMDInternalImport *pInternalImport = pModule->GetMDImport(); + MDEnumHolder hEnumMethod(pInternalImport); + + // + // First try for the new syntax: <MarshalDestroy> + // + IfFailThrow(pInternalImport->EnumInit(mdtMethodDef, cl, &hEnumMethod)); + + while (pInternalImport->EnumNext(&hEnumMethod, &tk)) + { + _ASSERTE(TypeFromToken(tk) == mdtMethodDef); + DWORD dwMemberAttrs; + IfFailThrow(pInternalImport->GetMethodDefProps(tk, &dwMemberAttrs)); + + if (IsMdSpecialName(dwMemberAttrs)) + { + ULONG cSig; + PCCOR_SIGNATURE pSig; + LPCSTR pName; + IfFailThrow(pInternalImport->GetNameAndSigOfMethodDef(tk, &pSig, &cSig, &pName)); + + const char *pBaseName = "<MarshalDestroy>"; + int ncBaseName = (int)strlen(pBaseName); + int nc = (int)strlen(pName); + if (nc >= ncBaseName && 0 == strcmp(pName + nc - ncBaseName, pBaseName)) + { + MetaSig msig(pSig, cSig, pModule, &typeContext); + + // Looking for the prototype void <MarshalDestroy>(Ptr VC); + if (msig.NumFixedArgs() == 1) + { + if (msig.GetReturnType() == ELEMENT_TYPE_VOID) + { + if (msig.NextArg() == ELEMENT_TYPE_PTR) + { + SigPointer sp1 = msig.GetArgProps(); + IfFailThrow(sp1.GetElemType(NULL)); + CorElementType eType; + IfFailThrow(sp1.GetElemType(&eType)); + if (eType == ELEMENT_TYPE_VALUETYPE) + { + mdToken tk1; + IfFailThrow(sp1.GetToken(&tk1)); + + hr = CompareTypeTokensNT(tk1, cl, pModule, pModule); + IfFailThrow(hr); + + if (hr == S_OK) + { + *pMDOut = pModule->LookupMethodDef(tk); + return; + } + } + } + } + } + } + } + } + + + // + // Next try the old syntax: global .__dtor + // + IfFailThrow(pInternalImport->EnumGlobalFunctionsInit(&hEnumMethod)); + + while (pInternalImport->EnumNext(&hEnumMethod, &tk)) + { + _ASSERTE(TypeFromToken(tk) == mdtMethodDef); + ULONG cSig; + PCCOR_SIGNATURE pSig; + LPCSTR pName; + IfFailThrow(pInternalImport->GetNameAndSigOfMethodDef(tk, &pSig, &cSig, &pName)); + + const char *pBaseName = ".__dtor"; + int ncBaseName = (int)strlen(pBaseName); + int nc = (int)strlen(pName); + if (nc >= ncBaseName && 0 == strcmp(pName + nc - ncBaseName, pBaseName)) + { + MetaSig msig(pSig, cSig, pModule, &typeContext); + + // Looking for the prototype void __dtor(Ptr VC); + if (msig.NumFixedArgs() == 1) + { + if (msig.GetReturnType() == ELEMENT_TYPE_VOID) + { + if (msig.NextArg() == ELEMENT_TYPE_PTR) + { + SigPointer sp1 = msig.GetArgProps(); + IfFailThrow(sp1.GetElemType(NULL)); + CorElementType eType; + IfFailThrow(sp1.GetElemType(&eType)); + if (eType == ELEMENT_TYPE_VALUETYPE) + { + mdToken tk1; + IfFailThrow(sp1.GetToken(&tk1)); + hr = CompareTypeTokensNT(tk1, cl, pModule, pModule); + if (FAILED(hr)) + { + pInternalImport->EnumClose(&hEnumMethod); + ThrowHR(hr); + } + + if (hr == S_OK) + { + *pMDOut = pModule->LookupMethodDef(tk); + return; + } + } + } + } + } + } + } +} + +//========================================================================== +// Set's up the custom marshaler information. +//========================================================================== +CustomMarshalerHelper *SetupCustomMarshalerHelper(LPCUTF8 strMarshalerTypeName, DWORD cMarshalerTypeNameBytes, LPCUTF8 strCookie, DWORD cCookieStrBytes, Assembly *pAssembly, TypeHandle hndManagedType) +{ +#ifndef CROSSGEN_COMPILE + CONTRACT (CustomMarshalerHelper*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pAssembly)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + EEMarshalingData *pMarshalingData = NULL; + + // Retrieve the marshalling data for the current app domain. + if (pAssembly->IsDomainNeutral()) + { + // If the assembly is shared, then it should only reference other domain neutral assemblies. + // This assumption MUST be true for the current custom marshaling scheme to work. + // This implies that the type of the managed parameter must be a shared type. + _ASSERTE(hndManagedType.GetAssembly()->IsDomainNeutral()); + + // The assembly is shared so we need to use the system domain's marshaling data. + pMarshalingData = SystemDomain::System()->GetMarshalingData(); + } + else + { + // The assembly is not shared so we use the current app domain's marshaling data. + pMarshalingData = GetThread()->GetDomain()->GetMarshalingData(); + } + + // Retrieve the custom marshaler helper from the EE marshaling data. + RETURN pMarshalingData->GetCustomMarshalerHelper(pAssembly, hndManagedType, strMarshalerTypeName, cMarshalerTypeNameBytes, strCookie, cCookieStrBytes); +#else + _ASSERTE(false); + RETURN NULL; +#endif +} + +//========================================================================== +// Return: S_OK if there is valid data to compress +// S_FALSE if at end of data block +// E_FAIL if corrupt data found +//========================================================================== +HRESULT CheckForCompressedData(PCCOR_SIGNATURE pvNativeTypeStart, PCCOR_SIGNATURE pvNativeType, ULONG cbNativeType) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (pvNativeTypeStart + cbNativeType == pvNativeType) + { // end of data block + return S_FALSE; + } + + ULONG ulDummy; + BYTE const *pbDummy; + return CPackedLen::SafeGetLength((BYTE const *)pvNativeType, + (BYTE const *)pvNativeTypeStart + cbNativeType, + &ulDummy, + &pbDummy); +} + +//========================================================================== +// Parse and validate the NATIVE_TYPE_ metadata. +// Note! NATIVE_TYPE_ metadata is optional. If it's not present, this +// routine sets NativeTypeParamInfo->m_NativeType to NATIVE_TYPE_DEFAULT. +//========================================================================== +BOOL ParseNativeTypeInfo(NativeTypeParamInfo* pParamInfo, PCCOR_SIGNATURE pvNativeType, ULONG cbNativeType); + +BOOL ParseNativeTypeInfo(mdToken token, + IMDInternalImport* pScope, + NativeTypeParamInfo* pParamInfo) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + PCCOR_SIGNATURE pvNativeType; + ULONG cbNativeType; + + if (token == mdParamDefNil || pScope->GetFieldMarshal(token, &pvNativeType, &cbNativeType) != S_OK) + return TRUE; + + return ParseNativeTypeInfo(pParamInfo, pvNativeType, cbNativeType); +} + +BOOL ParseNativeTypeInfo(NativeTypeParamInfo* pParamInfo, + PCCOR_SIGNATURE pvNativeType, + ULONG cbNativeType) +{ + LIMITED_METHOD_CONTRACT; + HRESULT hr; + + PCCOR_SIGNATURE pvNativeTypeStart = pvNativeType; + PCCOR_SIGNATURE pvNativeTypeEnd = pvNativeType + cbNativeType; + + if (cbNativeType == 0) + return FALSE; // Zero-length NATIVE_TYPE block + + pParamInfo->m_NativeType = (CorNativeType)*(pvNativeType++); + ULONG strLen = 0; + + // Retrieve any extra information associated with the native type. + switch (pParamInfo->m_NativeType) + { +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_INTF: + case NATIVE_TYPE_IUNKNOWN: + case NATIVE_TYPE_IDISPATCH: + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return TRUE; + + pParamInfo->m_IidParamIndex = (int)CorSigUncompressData(pvNativeType); + break; +#endif + + case NATIVE_TYPE_FIXEDARRAY: + + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return FALSE; + + pParamInfo->m_Additive = CorSigUncompressData(pvNativeType); + + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return TRUE; + + pParamInfo->m_ArrayElementType = (CorNativeType)CorSigUncompressData(pvNativeType); + break; + + case NATIVE_TYPE_FIXEDSYSSTRING: + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return FALSE; + + pParamInfo->m_Additive = CorSigUncompressData(pvNativeType); + break; + +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_SAFEARRAY: + // Check for the safe array element type. + hr = CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType); + if (FAILED(hr)) + return FALSE; + + if (hr == S_OK) + pParamInfo->m_SafeArrayElementVT = (VARTYPE) (CorSigUncompressData(/*modifies*/pvNativeType)); + + // Extract the name of the record type's. + if (S_OK == CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + { + hr = CPackedLen::SafeGetData((BYTE const *)pvNativeType, + (BYTE const *)pvNativeTypeEnd, + &strLen, + (BYTE const **)&pvNativeType); + if (FAILED(hr)) + { + return FALSE; + } + + pParamInfo->m_strSafeArrayUserDefTypeName = (LPUTF8)pvNativeType; + pParamInfo->m_cSafeArrayUserDefTypeNameBytes = strLen; + _ASSERTE((ULONG)(pvNativeType + strLen - pvNativeTypeStart) == cbNativeType); + } + break; + +#endif // FEATURE_COMINTEROP + + case NATIVE_TYPE_ARRAY: + hr = CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType); + if (FAILED(hr)) + return FALSE; + + if (hr == S_OK) + pParamInfo->m_ArrayElementType = (CorNativeType) (CorSigUncompressData(/*modifies*/pvNativeType)); + + // Check for "sizeis" param index + hr = CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType); + if (FAILED(hr)) + return FALSE; + + if (hr == S_OK) + { + pParamInfo->m_SizeIsSpecified = TRUE; + pParamInfo->m_CountParamIdx = (UINT16)(CorSigUncompressData(/*modifies*/pvNativeType)); + + // If an "sizeis" param index is present, the defaults for multiplier and additive change + pParamInfo->m_Multiplier = 1; + pParamInfo->m_Additive = 0; + + // Check for "sizeis" additive + hr = CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType); + if (FAILED(hr)) + return FALSE; + + if (hr == S_OK) + { + // Extract the additive. + pParamInfo->m_Additive = (DWORD)CorSigUncompressData(/*modifies*/pvNativeType); + + // Check to see if the flags field is present. + hr = CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType); + if (FAILED(hr)) + return FALSE; + + if (hr == S_OK) + { + // If the param index specified flag isn't set then we need to reset the + // multiplier to 0 to indicate no size param index was specified. + NativeTypeArrayFlags flags = (NativeTypeArrayFlags)CorSigUncompressData(/*modifies*/pvNativeType);; + if (!(flags & ntaSizeParamIndexSpecified)) + pParamInfo->m_Multiplier = 0; + } + } + } + + break; + + case NATIVE_TYPE_CUSTOMMARSHALER: + // Skip the typelib guid. + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return FALSE; + + if (FAILED(CPackedLen::SafeGetData(pvNativeType, pvNativeTypeEnd, &strLen, (void const **)&pvNativeType))) + return FALSE; + + pvNativeType += strLen; + _ASSERTE((ULONG)(pvNativeType - pvNativeTypeStart) < cbNativeType); + + // Skip the name of the native type. + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return FALSE; + + if (FAILED(CPackedLen::SafeGetData(pvNativeType, pvNativeTypeEnd, &strLen, (void const **)&pvNativeType))) + return FALSE; + + pvNativeType += strLen; + _ASSERTE((ULONG)(pvNativeType - pvNativeTypeStart) < cbNativeType); + + // Extract the name of the custom marshaler. + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return FALSE; + + if (FAILED(CPackedLen::SafeGetData(pvNativeType, pvNativeTypeEnd, &strLen, (void const **)&pvNativeType))) + return FALSE; + + pParamInfo->m_strCMMarshalerTypeName = (LPUTF8)pvNativeType; + pParamInfo->m_cCMMarshalerTypeNameBytes = strLen; + pvNativeType += strLen; + _ASSERTE((ULONG)(pvNativeType - pvNativeTypeStart) < cbNativeType); + + // Extract the cookie string. + if (S_OK != CheckForCompressedData(pvNativeTypeStart, pvNativeType, cbNativeType)) + return FALSE; + + if (FAILED(CPackedLen::SafeGetData(pvNativeType, pvNativeTypeEnd, &strLen, (void const **)&pvNativeType))) + return FALSE; + + pParamInfo->m_strCMCookie = (LPUTF8)pvNativeType; + pParamInfo->m_cCMCookieStrBytes = strLen; + _ASSERTE((ULONG)(pvNativeType + strLen - pvNativeTypeStart) == cbNativeType); + break; + + default: + break; + } + + return TRUE; +} + +//========================================================================== +// Determines whether *pManagedElemType is really normalized (i.e. differs +// from what sigPtr points to modulo generic instantiation). If it is the +// case, all types that have been normalized away are checked for valid +// managed/unmanaged type combination, and *pNativeType is updated to contain +// the native type of the primitive type field inside. On error (a generic +// type is encountered or managed/unmanaged type mismatch) or non-default +// native type of the primitive type inside, *pManagedElemType is un-normalized +// so that the calling code can deal with the situation in its own way. +//========================================================================== +void VerifyAndAdjustNormalizedType( + Module * pModule, + SigPointer sigPtr, + const SigTypeContext * pTypeContext, + CorElementType * pManagedElemType, + CorNativeType * pNativeType) +{ + CorElementType sigElemType = sigPtr.PeekElemTypeClosed(pModule, pTypeContext); + + if (*pManagedElemType != sigElemType) + { + // Normalized element type differs from closed element type, which means that + // normalization has occurred. + _ASSERTE(sigElemType == ELEMENT_TYPE_VALUETYPE); + + // Now we know that this is a normalized value type - we have to verify the removed + // value type(s) and get to the true primitive type inside. + TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, + pTypeContext, + ClassLoader::LoadTypes, + CLASS_LOAD_UNRESTORED, + TRUE); + _ASSERTE(!th.IsNull() && !th.IsTypeDesc()); + + CorNativeType ntype = *pNativeType; + + if (!th.AsMethodTable()->IsTruePrimitive() && + !th.IsEnum()) + { + // This is a trivial (yet non-primitive) value type that has been normalized. + // Loop until we eventually hit the primitive type or enum inside. + do + { + if (th.HasInstantiation()) + { + // generic structures are either not marshalable or special-cased - the caller needs to know either way + *pManagedElemType = sigElemType; + return; + } + + // verify the native type of the value type (must be default or Struct) + if (!(ntype == NATIVE_TYPE_DEFAULT || ntype == NATIVE_TYPE_STRUCT)) + { + *pManagedElemType = sigElemType; + return; + } + + MethodTable *pMT = th.GetMethodTable(); + _ASSERTE(pMT != NULL && pMT->IsValueType() && pMT->GetNumInstanceFields() == 1); + + // get the only instance field + PTR_FieldDesc fieldDesc = pMT->GetApproxFieldDescListRaw(); + + // retrieve the MarshalAs of the field + NativeTypeParamInfo paramInfo; + if (!ParseNativeTypeInfo(fieldDesc->GetMemberDef(), th.GetModule()->GetMDImport(), ¶mInfo)) + { + *pManagedElemType = sigElemType; + return; + } + + ntype = paramInfo.m_NativeType; + + th = fieldDesc->GetApproxFieldTypeHandleThrowing(); + } + while (!th.IsTypeDesc() && + !th.AsMethodTable()->IsTruePrimitive() && + !th.IsEnum()); + + // now ntype contains the native type of *pManagedElemType + if (ntype == NATIVE_TYPE_DEFAULT) + { + // Let's update the caller's native type with default type only. + // Updating with a non-default native type that is not allowed + // for the given managed type would result in confusing exception + // messages. + *pNativeType = ntype; + } + else + { + *pManagedElemType = sigElemType; + } + } + } +} + +VOID ThrowInteropParamException(UINT resID, UINT paramIdx) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + SString paramString; + if (paramIdx == 0) + paramString.Set(W("return value")); + else + paramString.Printf(W("parameter #%u"), paramIdx); + + SString errorString(W("Unknown error.")); + errorString.LoadResource(CCompRC::Error, resID); + + COMPlusThrow(kMarshalDirectiveException, IDS_EE_BADMARSHAL_ERROR_MSG, paramString.GetUnicode(), errorString.GetUnicode()); +} + +//=============================================================== +// Collects paraminfo's in an indexed array so that: +// +// aParams[0] == param token for return value +// aParams[1] == param token for argument #1... +// aParams[numargs] == param token for argument #n... +// +// If no param token exists, the corresponding array element +// is set to mdParamDefNil. +// +// Inputs: +// pInternalImport -- ifc for metadata api +// md -- token of method. If token is mdMethodNil, +// all aParam elements will be set to mdParamDefNil. +// numargs -- # of arguments in mdMethod +// aParams -- uninitialized array with numargs+1 elements. +// on exit, will be filled with param tokens. +//=============================================================== +VOID CollateParamTokens(IMDInternalImport *pInternalImport, mdMethodDef md, ULONG numargs, mdParamDef *aParams) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + for (ULONG i = 0; i < numargs + 1; i++) + aParams[i] = mdParamDefNil; + + if (md != mdMethodDefNil) + { + MDEnumHolder hEnumParams(pInternalImport); + HRESULT hr = pInternalImport->EnumInit(mdtParamDef, md, &hEnumParams); + if (FAILED(hr)) + { + // no param info: nothing left to do here + } + else + { + mdParamDef CurrParam = mdParamDefNil; + while (pInternalImport->EnumNext(&hEnumParams, &CurrParam)) + { + USHORT usSequence; + DWORD dwAttr; + LPCSTR szParamName_Ignore; + if (SUCCEEDED(pInternalImport->GetParamDefProps(CurrParam, &usSequence, &dwAttr, &szParamName_Ignore))) + { + if (usSequence > numargs) + { // Invalid argument index + ThrowHR(COR_E_BADIMAGEFORMAT); + } + if (aParams[usSequence] != mdParamDefNil) + { // Duplicit argument index + ThrowHR(COR_E_BADIMAGEFORMAT); + } + aParams[usSequence] = CurrParam; + } + } + } + } +} + + +#ifdef FEATURE_COMINTEROP + +void *EventArgsMarshalingInfo::operator new(size_t size, LoaderHeap *pHeap) +{ + CONTRACT (void*) + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pHeap)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + void* mem = pHeap->AllocMem(S_SIZE_T(size)); + + RETURN mem; +} + + +void EventArgsMarshalingInfo::operator delete(void *pMem) +{ + LIMITED_METHOD_CONTRACT; + // Instances of this class are always allocated on the loader heap so + // the delete operator has nothing to do. +} + +EventArgsMarshalingInfo::EventArgsMarshalingInfo() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + // Create on-demand as we don't want to create the factories in NGEN time + m_pNCCEventArgsFactory = NULL; + m_pPCEventArgsFactory = NULL; + + // Load the System.Collections.Specialized.NotifyCollectionChangedEventArgs class. + SString qualifiedNCCEventArgsTypeName(SString::Utf8, NCCEVENTARGS_ASM_QUAL_TYPE_NAME); + m_hndSystemNCCEventArgsType = TypeName::GetTypeFromAsmQualifiedName(qualifiedNCCEventArgsTypeName.GetUnicode(), FALSE); + _ASSERTE(!m_hndSystemNCCEventArgsType.IsNull() && "Cannot load System.Collections.Specialized.NotifyCollectionChangedEventArgs!"); + + // Load the System.ComponentModel.PropertyChangedEventArgs class. + SString qualifiedPCEventArgsTypeName(SString::Utf8, PCEVENTARGS_ASM_QUAL_TYPE_NAME); + m_hndSystemPCEventArgsType = TypeName::GetTypeFromAsmQualifiedName(qualifiedPCEventArgsTypeName.GetUnicode(), FALSE); + _ASSERTE(!m_hndSystemPCEventArgsType.IsNull() && "Cannot load System.ComponentModel.PropertyChangedEventArgs!"); + + // Load the NCCEventArgs marshaler class. + SString qualifiedNCCEventArgsMarshalerTypeName(SString::Utf8, NCCEVENTARGS_MARSHALER_ASM_QUAL_TYPE_NAME); + TypeHandle hndNCCEventArgsMarshalerType = TypeName::GetTypeFromAsmQualifiedName(qualifiedNCCEventArgsMarshalerTypeName.GetUnicode(), FALSE); + + // Retrieve the method to convert a .NET NCCEventArgs to a WinRT NCCEventArgs. + m_pSystemNCCEventArgsToWinRTNCCEventArgsMD = MemberLoader::FindMethodByName(hndNCCEventArgsMarshalerType.GetMethodTable(), EVENTARGS_TO_WINRT_EVENTARGS_METH_NAME); + _ASSERTE(m_pSystemNCCEventArgsToWinRTNCCEventArgsMD && "Unable to find the marshaler method to convert a .NET NCCEventArgs to a WinRT NCCEventArgs!"); + + // Retrieve the method to convert a WinRT NCCEventArgs to a .NET NCCEventArgs. + m_pWinRTNCCEventArgsToSystemNCCEventArgsMD = MemberLoader::FindMethodByName(hndNCCEventArgsMarshalerType.GetMethodTable(), WINRT_EVENTARGS_TO_EVENTARGS_METH_NAME); + _ASSERTE(m_pWinRTNCCEventArgsToSystemNCCEventArgsMD && "Unable to find the marshaler method to convert a WinRT NCCEventArgs to a .NET NCCEventArgs!"); + + // Load the PCEventArgs marshaler class. + SString qualifiedPCEventArgsMarshalerTypeName(SString::Utf8, PCEVENTARGS_MARSHALER_ASM_QUAL_TYPE_NAME); + TypeHandle hndPCEventArgsMarshalerType = TypeName::GetTypeFromAsmQualifiedName(qualifiedPCEventArgsMarshalerTypeName.GetUnicode(), FALSE); + + // Retrieve the method to convert a .NET PCEventArgs to a WinRT PCEventArgs. + m_pSystemPCEventArgsToWinRTPCEventArgsMD = MemberLoader::FindMethodByName(hndPCEventArgsMarshalerType.GetMethodTable(), EVENTARGS_TO_WINRT_EVENTARGS_METH_NAME); + _ASSERTE(m_pSystemPCEventArgsToWinRTPCEventArgsMD && "Unable to find the marshaler method to convert a .NET PCEventArgs to a WinRT PCEventArgs!"); + + // Retrieve the method to convert a WinRT PCEventArgs to a .NET PCEventArgs. + m_pWinRTPCEventArgsToSystemPCEventArgsMD = MemberLoader::FindMethodByName(hndPCEventArgsMarshalerType.GetMethodTable(), WINRT_EVENTARGS_TO_EVENTARGS_METH_NAME); + _ASSERTE(m_pWinRTPCEventArgsToSystemPCEventArgsMD && "Unable to find the marshaler method to convert a WinRT PCEventArgs to a .NET PCEventArgs!"); +} + +EventArgsMarshalingInfo::~EventArgsMarshalingInfo() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + if (m_pNCCEventArgsFactory) + { + SafeRelease(m_pNCCEventArgsFactory); + m_pNCCEventArgsFactory = NULL; + } + + if (m_pPCEventArgsFactory) + { + SafeRelease(m_pPCEventArgsFactory); + m_pPCEventArgsFactory = NULL; + } +} + +void *UriMarshalingInfo::operator new(size_t size, LoaderHeap *pHeap) +{ + CONTRACT (void*) + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pHeap)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + void* mem = pHeap->AllocMem(S_SIZE_T(size)); + + RETURN mem; +} + + +void UriMarshalingInfo::operator delete(void *pMem) +{ + LIMITED_METHOD_CONTRACT; + // Instances of this class are always allocated on the loader heap so + // the delete operator has nothing to do. +} + +UriMarshalingInfo::UriMarshalingInfo() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + // Create on-demand as we don't want to create the factories in NGEN time + m_pUriFactory = NULL; + + // Load the System.Uri class. + SString qualifiedUriTypeName(SString::Utf8, URI_ASM_QUAL_TYPE_NAME); + m_hndSystemUriType = TypeName::GetTypeFromAsmQualifiedName(qualifiedUriTypeName.GetUnicode(), FALSE); + _ASSERTE(!m_hndSystemUriType.IsNull() && "Cannot load System.Uri!"); + + m_SystemUriOriginalStringGetterMD = MemberLoader::FindPropertyMethod(m_hndSystemUriType.GetMethodTable(), ORIGINALSTRING_PROPERTY_NAME, PropertyGet); + _ASSERTE(m_SystemUriOriginalStringGetterMD && "Unable to find the System.Uri.get_OriginalString()!"); + _ASSERTE(!m_SystemUriOriginalStringGetterMD->IsStatic() && "System.Uri.get_OriginalString() is static!"); + + // Windows.Foundation.Uri..ctor(string) and System.Uri..ctor(string) + MethodTable* pSystemUriMT = m_hndSystemUriType.AsMethodTable(); + m_SystemUriCtorMD = MemberLoader::FindConstructor(pSystemUriMT, &gsig_IM_Str_RetVoid); + _ASSERTE(m_SystemUriCtorMD && "Unable to find the constructor on System.Uri that takes a string!"); + _ASSERTE(m_SystemUriCtorMD->IsClassConstructorOrCtor() && !m_SystemUriCtorMD->IsStatic() && "The method retrieved from System.Uri is not a constructor!"); +} + +UriMarshalingInfo::~UriMarshalingInfo() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; +#ifndef CROSSGEN_COMPILE + if (m_pUriFactory) + { + SafeRelease(m_pUriFactory); + m_pUriFactory = NULL; + } +#endif +} + +OleColorMarshalingInfo::OleColorMarshalingInfo() : + m_OleColorToSystemColorMD(NULL), + m_SystemColorToOleColorMD(NULL) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + SString qualifiedColorTranslatorTypeName(SString::Utf8, COLOR_TRANSLATOR_ASM_QUAL_TYPE_NAME); + + // Load the color translator class. + TypeHandle hndColorTranslatorType = TypeName::GetTypeFromAsmQualifiedName(qualifiedColorTranslatorTypeName.GetUnicode(), FALSE); + + + SString qualifiedColorTypeName(SString::Utf8, COLOR_ASM_QUAL_TYPE_NAME); + // Load the color class. + m_hndColorType = TypeName::GetTypeFromAsmQualifiedName(qualifiedColorTypeName.GetUnicode(), FALSE); + + // Retrieve the method to convert an OLE_COLOR to a System.Drawing.Color. + m_OleColorToSystemColorMD = MemberLoader::FindMethodByName(hndColorTranslatorType.GetMethodTable(), OLECOLOR_TO_SYSTEMCOLOR_METH_NAME); + _ASSERTE(m_OleColorToSystemColorMD && "Unable to find the translator method to convert an OLE_COLOR to a System.Drawing.Color!"); + _ASSERTE(m_OleColorToSystemColorMD->IsStatic() && "The translator method to convert an OLE_COLOR to a System.Drawing.Color must be static!"); + + // Retrieve the method to convert a System.Drawing.Color to an OLE_COLOR. + m_SystemColorToOleColorMD = MemberLoader::FindMethodByName(hndColorTranslatorType.GetMethodTable(), SYSTEMCOLOR_TO_OLECOLOR_METH_NAME); + _ASSERTE(m_SystemColorToOleColorMD && "Unable to find the translator method to convert a System.Drawing.Color to an OLE_COLOR!"); + _ASSERTE(m_SystemColorToOleColorMD->IsStatic() && "The translator method to convert a System.Drawing.Color to an OLE_COLOR must be static!"); +} + + +void *OleColorMarshalingInfo::operator new(size_t size, LoaderHeap *pHeap) +{ + CONTRACT (void*) + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pHeap)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + void* mem = pHeap->AllocMem(S_SIZE_T(size)); + + RETURN mem; +} + + +void OleColorMarshalingInfo::operator delete(void *pMem) +{ + LIMITED_METHOD_CONTRACT; + // Instances of this class are always allocated on the loader heap so + // the delete operator has nothing to do. +} + +#endif // FEATURE_COMINTEROP + +EEMarshalingData::EEMarshalingData(BaseDomain *pDomain, LoaderHeap *pHeap, CrstBase *pCrst) : + m_pHeap(pHeap), + m_pDomain(pDomain) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + LockOwner lock = {pCrst, IsOwnerOfCrst}; +#ifndef CROSSGEN_COMPILE + m_CMHelperHashtable.Init(INITIAL_NUM_CMHELPER_HASHTABLE_BUCKETS, &lock); + m_SharedCMHelperToCMInfoMap.Init(INITIAL_NUM_CMINFO_HASHTABLE_BUCKETS, &lock); +#endif // CROSSGEN_COMPILE +} + + +EEMarshalingData::~EEMarshalingData() +{ + WRAPPER_NO_CONTRACT; + + CustomMarshalerInfo *pCMInfo; + + // <TODO>@TODO(DM): Remove the linked list of CMInfo's and instead hang the OBJECTHANDLE + // contained inside the CMInfo off the AppDomain directly. The AppDomain can have + // a list of tasks to do when it gets teared down and we could leverage that + // to release the object handles.</TODO> + + // Walk through the linked list and delete all the custom marshaler info's. + while ((pCMInfo = m_pCMInfoList.RemoveHead()) != NULL) + delete pCMInfo; + +#ifdef FEATURE_COMINTEROP + if (m_pOleColorInfo) + { + delete m_pOleColorInfo; + m_pOleColorInfo = NULL; + } + + if (m_pUriInfo) + { + delete m_pUriInfo; + m_pUriInfo = NULL; + } + + if (m_pEventArgsInfo) + { + delete m_pEventArgsInfo; + m_pEventArgsInfo = NULL; + } +#endif +} + + +void *EEMarshalingData::operator new(size_t size, LoaderHeap *pHeap) +{ + CONTRACT (void*) + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pHeap)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + void* mem = pHeap->AllocMem(S_SIZE_T(sizeof(EEMarshalingData))); + + RETURN mem; +} + + +void EEMarshalingData::operator delete(void *pMem) +{ + LIMITED_METHOD_CONTRACT; + // Instances of this class are always allocated on the loader heap so + // the delete operator has nothing to do. +} + +#ifndef CROSSGEN_COMPILE + +CustomMarshalerHelper *EEMarshalingData::GetCustomMarshalerHelper(Assembly *pAssembly, TypeHandle hndManagedType, LPCUTF8 strMarshalerTypeName, DWORD cMarshalerTypeNameBytes, LPCUTF8 strCookie, DWORD cCookieStrBytes) +{ + CONTRACT (CustomMarshalerHelper*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pAssembly)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + CustomMarshalerHelper *pCMHelper = NULL; + CustomMarshalerHelper* pNewCMHelper = NULL; + NewHolder<CustomMarshalerInfo> pNewCMInfo(NULL); + + BOOL bSharedHelper = pAssembly->IsDomainNeutral(); + TypeHandle hndCustomMarshalerType; + + // Create the key that will be used to lookup in the hashtable. + EECMHelperHashtableKey Key(cMarshalerTypeNameBytes, strMarshalerTypeName, cCookieStrBytes, strCookie, hndManagedType.GetInstantiation(), bSharedHelper); + + // Lookup the custom marshaler helper in the hashtable. + if (m_CMHelperHashtable.GetValue(&Key, (HashDatum*)&pCMHelper)) + RETURN pCMHelper; + + { + GCX_COOP(); + + // Validate the arguments. + _ASSERTE(strMarshalerTypeName && strCookie && !hndManagedType.IsNull()); + + // Append a NULL terminator to the marshaler type name. + SString strCMMarshalerTypeName(SString::Utf8, strMarshalerTypeName, cMarshalerTypeNameBytes); + + // Load the custom marshaler class. + BOOL fNameIsAsmQualified = FALSE; + hndCustomMarshalerType = TypeName::GetTypeUsingCASearchRules(strCMMarshalerTypeName.GetUTF8NoConvert(), pAssembly, &fNameIsAsmQualified); + + if (hndCustomMarshalerType.IsGenericTypeDefinition()) + { + // Instantiate generic custom marshalers using the instantiation of the type being marshaled. + hndCustomMarshalerType = hndCustomMarshalerType.Instantiate(hndManagedType.GetInstantiation()); + } + + // Set the assembly to null to indicate that the custom marshaler name is assembly + // qualified. + if (fNameIsAsmQualified) + pAssembly = NULL; + + + if (bSharedHelper) + { + // Create the custom marshaler helper in the specified heap. + pNewCMHelper = new (m_pHeap) SharedCustomMarshalerHelper(pAssembly, hndManagedType, strMarshalerTypeName, cMarshalerTypeNameBytes, strCookie, cCookieStrBytes); + } + else + { + // Create the custom marshaler info in the specified heap. + pNewCMInfo = new (m_pHeap) CustomMarshalerInfo(m_pDomain, hndCustomMarshalerType, hndManagedType, strCookie, cCookieStrBytes); + + // Create the custom marshaler helper in the specified heap. + pNewCMHelper = new (m_pHeap) NonSharedCustomMarshalerHelper(pNewCMInfo); + } + } + + // Take the app domain lock before we insert the custom marshaler info into the hashtable. + { + BaseDomain::LockHolder lh(m_pDomain); + + // Verify that the custom marshaler helper has not already been added by another thread. + if (m_CMHelperHashtable.GetValue(&Key, (HashDatum*)&pCMHelper)) + { + RETURN pCMHelper; + } + + // Add the custom marshaler helper to the hash table. + m_CMHelperHashtable.InsertValue(&Key, pNewCMHelper, FALSE); + + // If we create the CM info, then add it to the linked list. + if (pNewCMInfo) + { + m_pCMInfoList.InsertHead(pNewCMInfo); + pNewCMInfo.SuppressRelease(); + } + + // Release the lock and return the custom marshaler info. + } + + RETURN pNewCMHelper; +} + +CustomMarshalerInfo *EEMarshalingData::GetCustomMarshalerInfo(SharedCustomMarshalerHelper *pSharedCMHelper) +{ + CONTRACT (CustomMarshalerInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + CustomMarshalerInfo *pCMInfo = NULL; + NewHolder<CustomMarshalerInfo> pNewCMInfo(NULL); + TypeHandle hndCustomMarshalerType; + + // Lookup the custom marshaler helper in the hashtable. + if (m_SharedCMHelperToCMInfoMap.GetValue(pSharedCMHelper, (HashDatum*)&pCMInfo)) + RETURN pCMInfo; + + // Append a NULL terminator to the marshaler type name. + CQuickArray<char> strCMMarshalerTypeName; + DWORD strLen = pSharedCMHelper->GetMarshalerTypeNameByteCount(); + strCMMarshalerTypeName.ReSizeThrows(pSharedCMHelper->GetMarshalerTypeNameByteCount() + 1); + memcpy(strCMMarshalerTypeName.Ptr(), pSharedCMHelper->GetMarshalerTypeName(), strLen); + strCMMarshalerTypeName[strLen] = 0; + + // Load the custom marshaler class. + hndCustomMarshalerType = TypeName::GetTypeUsingCASearchRules(strCMMarshalerTypeName.Ptr(), pSharedCMHelper->GetAssembly()); + if (hndCustomMarshalerType.IsGenericTypeDefinition()) + { + // Instantiate generic custom marshalers using the instantiation of the type being marshaled. + hndCustomMarshalerType = hndCustomMarshalerType.Instantiate(pSharedCMHelper->GetManagedType().GetInstantiation()); + } + + // Create the custom marshaler info in the specified heap. + pNewCMInfo = new (m_pHeap) CustomMarshalerInfo(m_pDomain, + hndCustomMarshalerType, + pSharedCMHelper->GetManagedType(), + pSharedCMHelper->GetCookieString(), + pSharedCMHelper->GetCookieStringByteCount()); + + { + // Take the app domain lock before we insert the custom marshaler info into the hashtable. + BaseDomain::LockHolder lh(m_pDomain); + + // Verify that the custom marshaler info has not already been added by another thread. + if (m_SharedCMHelperToCMInfoMap.GetValue(pSharedCMHelper, (HashDatum*)&pCMInfo)) + { + RETURN pCMInfo; + } + + // Add the custom marshaler helper to the hash table. + m_SharedCMHelperToCMInfoMap.InsertValue(pSharedCMHelper, pNewCMInfo, FALSE); + + // Add the custom marshaler into the linked list. + m_pCMInfoList.InsertHead(pNewCMInfo); + + // Release the lock and return the custom marshaler info. + } + + pNewCMInfo.SuppressRelease(); + RETURN pNewCMInfo; +} +#endif // CROSSGEN_COMPILE + +#ifdef FEATURE_COMINTEROP +UriMarshalingInfo *EEMarshalingData::GetUriMarshalingInfo() +{ + CONTRACT (UriMarshalingInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + if (m_pUriInfo == NULL) + { + UriMarshalingInfo *pUriInfo = new (m_pHeap) UriMarshalingInfo(); + + if (InterlockedCompareExchangeT(&m_pUriInfo, pUriInfo, NULL) != NULL) + { + // Another thread beat us to it. Delete on UriMarshalingInfo is an empty operation + // which is OK, since the possible leak is rare, small, and constant. This is the same + // pattern as in code:GetCustomMarshalerInfo. + delete pUriInfo; + } + } + +#ifdef _DEBUG + BaseDomain *pUriDomain = m_pUriInfo->GetSystemUriType().GetDomain(); + if (pUriDomain != m_pDomain) + { + // Make sure that Uri marshaling data is initialized in its own (shared) domain as well. + // This allows us to perform quick checks in code:EEMarshalingData.IsUriHelperMethod. + (void) pUriDomain->GetMarshalingData()->GetUriMarshalingInfo(); + } +#endif // _DEBUG + + RETURN m_pUriInfo; +} + +EventArgsMarshalingInfo *EEMarshalingData::GetEventArgsMarshalingInfo() +{ + CONTRACT (EventArgsMarshalingInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + if (m_pEventArgsInfo == NULL) + { + EventArgsMarshalingInfo *pEventArgsInfo = new (m_pHeap) EventArgsMarshalingInfo(); + + if (InterlockedCompareExchangeT(&m_pEventArgsInfo, pEventArgsInfo, NULL) != NULL) + { + // Another thread beat us to it. Delete on EventArgsMarshalingInfo is an empty operation + // which is OK, since the possible leak is rare, small, and constant. This is the same + // pattern as in code:GetCustomMarshalerInfo. + delete pEventArgsInfo; + } + } + +#ifdef _DEBUG + BaseDomain *pEventArgsDomain = m_pEventArgsInfo->GetSystemNCCEventArgsType().GetDomain(); + if (pEventArgsDomain != m_pDomain) + { + // Make sure that EventArgs marshaling data is initialized in its own (shared) domain as well. + // This allows us to perform quick checks in code:EEMarshalingData.IsEventArgsHelperMethod. + (void) pEventArgsDomain->GetMarshalingData()->GetEventArgsMarshalingInfo(); + } +#endif // _DEBUG + + RETURN m_pEventArgsInfo; +} + +OleColorMarshalingInfo *EEMarshalingData::GetOleColorMarshalingInfo() +{ + CONTRACT (OleColorMarshalingInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + if (m_pOleColorInfo == NULL) + { + OleColorMarshalingInfo *pOleColorInfo = new (m_pHeap) OleColorMarshalingInfo(); + + if (InterlockedCompareExchangeT(&m_pOleColorInfo, pOleColorInfo, NULL) != NULL) + { + // Another thread beat us to it. Delete on OleColorMarshalingInfo is an empty operation + // which is OK, since the possible leak is rare, small, and constant. This is the same + // pattern as in code:GetCustomMarshalerInfo. + delete pOleColorInfo; + } + } + +#ifdef _DEBUG + BaseDomain *pColorDomain = m_pOleColorInfo->GetColorType().GetDomain(); + if (pColorDomain != m_pDomain) + { + // Make sure that Color marshaling data is initialized in its own (shared) domain as well. + // This allows us to perform quick checks in code:EEMarshalingData.IsOleColorHelperMethod. + (void) pColorDomain->GetMarshalingData()->GetOleColorMarshalingInfo(); + } +#endif // _DEBUG + + RETURN m_pOleColorInfo; +} +#endif // FEATURE_COMINTEROP + +//========================================================================== +// Constructs MarshalInfo. +//========================================================================== +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +MarshalInfo::MarshalInfo(Module* pModule, + SigPointer sig, + const SigTypeContext *pTypeContext, + mdToken token, + MarshalScenario ms, + CorNativeLinkType nlType, + CorNativeLinkFlags nlFlags, + BOOL isParam, + UINT paramidx, // parameter # for use in error messages (ignored if not parameter) + UINT numArgs, // number of arguments + BOOL BestFit, + BOOL ThrowOnUnmappableChar, + BOOL fEmitsIL, + MethodDesc* pMD, + BOOL fLoadCustomMarshal +#ifdef _DEBUG + , + LPCUTF8 pDebugName, + LPCUTF8 pDebugClassName, + UINT argidx // 0 for return value, -1 for field +#endif +) +{ + STANDARD_VM_CONTRACT; + + HRESULT hr; + NativeTypeParamInfo ParamInfo; + + // we expect a 1-based paramidx, but we like to use a 0-based paramidx + m_paramidx = paramidx - 1; + + // if no one overwrites this with a better message, we'll still at least say something + m_resID = IDS_EE_BADMARSHAL_GENERIC; + + // flag for uninitialized type + m_type = MARSHAL_TYPE_UNKNOWN; + + CorNativeType nativeType = NATIVE_TYPE_DEFAULT; + Assembly *pAssembly = pModule->GetAssembly(); +#ifndef FEATURE_CORECLR + BOOL fNeedsCopyCtor = FALSE; +#endif // !FEATURE_CORECLR + m_BestFit = BestFit; + m_ThrowOnUnmappableChar = ThrowOnUnmappableChar; + m_ms = ms; + m_fAnsi = (ms == MARSHAL_SCENARIO_NDIRECT) && (nlType == nltAnsi); + m_managedArgSize = 0; + m_nativeArgSize = 0; + m_pCMHelper = NULL; + m_CMVt = VT_EMPTY; + m_args.m_pMarshalInfo = this; + m_args.m_pMT = NULL; + m_pModule = pModule; + CorElementType mtype = ELEMENT_TYPE_END; + CorElementType corElemType = ELEMENT_TYPE_END; + m_pMT = NULL; + m_pMD = pMD; + +#ifdef FEATURE_COMINTEROP + m_fDispItf = FALSE; + m_fInspItf = FALSE; + m_fErrorNativeType = FALSE; + m_hiddenLengthParamIndex = (UINT16)-1; + m_dwHiddenLengthManagedHomeLocal= 0xFFFFFFFF; + m_dwHiddenLengthNativeHomeLocal = 0xFFFFFFFF; + + m_pDefaultItfMT = NULL; +#endif // FEATURE_COMINTEROP + + +#ifdef _DEBUG + + CHAR achDbgContext[DEBUG_CONTEXT_STR_LEN] = ""; + if (!pDebugName) + { + strncpy_s(achDbgContext, COUNTOF(achDbgContext), "<Unknown>", _TRUNCATE); + } + else + { + strncat_s(achDbgContext, COUNTOF(achDbgContext), pDebugClassName, _TRUNCATE); + strncat_s(achDbgContext, COUNTOF(achDbgContext), NAMESPACE_SEPARATOR_STR, _TRUNCATE); + strncat_s(achDbgContext, COUNTOF(achDbgContext), pDebugName, _TRUNCATE); + strncat_s(achDbgContext, COUNTOF(achDbgContext), " ", _TRUNCATE); + switch (argidx) + { + case (UINT)-1: + strncat_s(achDbgContext, COUNTOF(achDbgContext), "field", _TRUNCATE); + break; + case 0: + strncat_s(achDbgContext, COUNTOF(achDbgContext), "return value", _TRUNCATE); + break; + default: + { + char buf[30]; + sprintf_s(buf, COUNTOF(buf), "param #%lu", (ULONG)argidx); + strncat_s(achDbgContext, COUNTOF(achDbgContext), buf, _TRUNCATE); + } + } + } + + m_strDebugMethName = pDebugName; + m_strDebugClassName = pDebugClassName; + m_iArg = argidx; + + m_in = m_out = FALSE; + m_byref = TRUE; +#endif + + + + // Retrieve the native type for the current parameter. + if (!ParseNativeTypeInfo(token, pModule->GetMDImport(), &ParamInfo)) + { + IfFailGoto(E_FAIL, lFail); + } + + nativeType = ParamInfo.m_NativeType; + corElemType = sig.PeekElemTypeNormalized(pModule, pTypeContext); + mtype = corElemType; + +#ifdef FEATURE_COMINTEROP + if (IsWinRTScenario() && nativeType != NATIVE_TYPE_DEFAULT) + { + // Do not allow any MarshalAs in WinRT scenarios - marshaling is fully described by the parameter type. + m_type = MARSHAL_TYPE_UNKNOWN; + m_resID = IDS_EE_BADMARSHAL_WINRT_MARSHAL_AS; + IfFailGoto(E_FAIL, lFail); + } +#endif // FEATURE_COMINTEROP + + // Make sure SizeParamIndex < numArgs when marshalling native arrays + if (nativeType == NATIVE_TYPE_ARRAY && ParamInfo.m_SizeIsSpecified) + { + if (ParamInfo.m_Multiplier > 0 && ParamInfo.m_CountParamIdx >= numArgs) + { + // Do not throw exception here. + // We'll use EmitOrThrowInteropException to throw exception in non-COM interop + // and emit exception throwing code directly in STUB in COM interop + m_type = MARSHAL_TYPE_UNKNOWN; + m_resID = IDS_EE_SIZECONTROLOUTOFRANGE; + IfFailGoto(E_FAIL, lFail); + } + } + + // Parse ET_BYREF signature + if (mtype == ELEMENT_TYPE_BYREF) + { + m_byref = TRUE; + SigPointer sigtmp = sig; + IfFailGoto(sig.GetElemType(NULL), lFail); + mtype = sig.PeekElemTypeNormalized(pModule, pTypeContext); + +#ifndef FEATURE_CORECLR // no copy ctor support in CoreCLR + // Check for Copy Constructor Modifier - peek closed elem type here to prevent ELEMENT_TYPE_VALUETYPE + // turning into a primitive. + if (sig.PeekElemTypeClosed(pModule, pTypeContext) == ELEMENT_TYPE_VALUETYPE) + { + // Skip ET_BYREF + IfFailGoto(sigtmp.GetByte(NULL), lFail); + + if (sigtmp.HasCustomModifier(pModule, "Microsoft.VisualC.NeedsCopyConstructorModifier", ELEMENT_TYPE_CMOD_REQD) || + sigtmp.HasCustomModifier(pModule, "System.Runtime.CompilerServices.IsCopyConstructed", ELEMENT_TYPE_CMOD_REQD) ) + { + mtype = ELEMENT_TYPE_VALUETYPE; + fNeedsCopyCtor = TRUE; + m_byref = FALSE; + } + } +#endif // !FEATURE_CORECLR + } + else + { + m_byref = FALSE; + } + + // Check for valid ET_PTR signature + if (mtype == ELEMENT_TYPE_PTR) + { +#ifdef FEATURE_COMINTEROP + // WinRT does not support ET_PTR + if (IsWinRTScenario()) + { + m_type = MARSHAL_TYPE_UNKNOWN; + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } +#endif // FEATURE_COMINTEROP + + SigPointer sigtmp = sig; + IfFailGoto(sigtmp.GetElemType(NULL), lFail); + + // Peek closed elem type here to prevent ELEMENT_TYPE_VALUETYPE turning into a primitive. + CorElementType mtype2 = sigtmp.PeekElemTypeClosed(pModule, pTypeContext); + + if (mtype2 == ELEMENT_TYPE_VALUETYPE) + { + + TypeHandle th = sigtmp.GetTypeHandleThrowing(pModule, pTypeContext); + _ASSERTE(!th.IsNull()); + + // We want to leave out enums as they surely don't have copy constructors + // plus they are not marked as blittable. + if (!th.IsEnum()) + { + // It should be blittable + if (!th.IsBlittable()) + { + m_resID = IDS_EE_BADMARSHAL_PTRNONBLITTABLE; + IfFailGoto(E_FAIL, lFail); + } + +#ifndef FEATURE_CORECLR + // Check for Copy Constructor Modifier + if (sigtmp.HasCustomModifier(pModule, "Microsoft.VisualC.NeedsCopyConstructorModifier", ELEMENT_TYPE_CMOD_REQD) || + sigtmp.HasCustomModifier(pModule, "System.Runtime.CompilerServices.IsCopyConstructed", ELEMENT_TYPE_CMOD_REQD) ) + { + mtype = mtype2; + + // Keep the sig pointer in sync with mtype (skip ELEMENT_TYPE_PTR) because for the rest + // of this method we are pretending that the parameter is a value type passed by-value. + IfFailGoto(sig.GetElemType(NULL), lFail); + + fNeedsCopyCtor = TRUE; + m_byref = FALSE; + } +#endif // !FEATURE_CORECLR + } + } + else + { + if (!(mtype2 != ELEMENT_TYPE_CLASS && + mtype2 != ELEMENT_TYPE_STRING && + mtype2 != ELEMENT_TYPE_OBJECT && + mtype2 != ELEMENT_TYPE_SZARRAY)) + { + m_resID = IDS_EE_BADMARSHAL_PTRSUBTYPE; + IfFailGoto(E_FAIL, lFail); + } + } + } + + + // System primitive types (System.Int32, et.al.) will be marshaled as expected + // because the mtype CorElementType is normalized (e.g. ELEMENT_TYPE_I4). +#ifdef _TARGET_X86_ + // We however need to detect if such a normalization occurred for non-system + // trivial value types, because we hold CorNativeType belonging to the original + // "un-normalized" signature type. It has to be verified that all the value types + // that have been normalized away have default marshaling or MarshalAs(Struct). + // In addition, the nativeType must be updated with the type of the real primitive inside. + // + VerifyAndAdjustNormalizedType(pModule, sig, pTypeContext, &mtype, &nativeType); +#endif // _TARGET_X86_ + + + if (nativeType == NATIVE_TYPE_CUSTOMMARSHALER) + { + switch (mtype) + { + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_OBJECT: + m_CMVt = VT_UNKNOWN; + break; + + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_ARRAY: + m_CMVt = VT_I4; + break; + + default: + m_resID = IDS_EE_BADMARSHAL_CUSTOMMARSHALER; + IfFailGoto(E_FAIL, lFail); + } + + // Set m_type to MARSHAL_TYPE_UNKNOWN in case SetupCustomMarshalerHelper throws. + m_type = MARSHAL_TYPE_UNKNOWN; + + if (fLoadCustomMarshal) + { + // Set up the custom marshaler info. + TypeHandle hndManagedType = sig.GetTypeHandleThrowing(pModule, pTypeContext); + + if (!fEmitsIL) + { + m_pCMHelper = SetupCustomMarshalerHelper(ParamInfo.m_strCMMarshalerTypeName, + ParamInfo.m_cCMMarshalerTypeNameBytes, + ParamInfo.m_strCMCookie, + ParamInfo.m_cCMCookieStrBytes, + pAssembly, + hndManagedType); + } + else + { + m_pCMHelper = NULL; + MethodDesc* pMDforModule = pMD; + if (pMD->IsILStub()) + { + pMDforModule = pMD->AsDynamicMethodDesc()->GetILStubResolver()->GetStubTargetMethodDesc(); + } + m_args.rcm.m_pMD = pMDforModule; + m_args.rcm.m_paramToken = token; + m_args.rcm.m_hndManagedType = hndManagedType.AsPtr(); + CONSISTENCY_CHECK(pModule == pMDforModule->GetModule()); + } + } + + // Specify which custom marshaler to use. + m_type = MARSHAL_TYPE_REFERENCECUSTOMMARSHALER; + + goto lExit; + } + + switch (mtype) + { + case ELEMENT_TYPE_BOOLEAN: + switch (nativeType) + { + case NATIVE_TYPE_BOOLEAN: + m_type = MARSHAL_TYPE_WINBOOL; + break; + +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_VARIANTBOOL: + m_type = MARSHAL_TYPE_VTBOOL; + break; +#endif // FEATURE_COMINTEROP + + case NATIVE_TYPE_U1: + case NATIVE_TYPE_I1: + m_type = MARSHAL_TYPE_CBOOL; + break; + + case NATIVE_TYPE_DEFAULT: +#ifdef FEATURE_COMINTEROP + if (m_ms == MARSHAL_SCENARIO_COMINTEROP) + { + // 2-byte COM VARIANT_BOOL + m_type = MARSHAL_TYPE_VTBOOL; + } + else if (m_ms == MARSHAL_SCENARIO_WINRT) + { + // 1-byte WinRT bool + m_type = MARSHAL_TYPE_CBOOL; + } + else +#endif // FEATURE_COMINTEROP + { + // 4-byte Windows BOOL + _ASSERTE(m_ms == MARSHAL_SCENARIO_NDIRECT); + m_type = MARSHAL_TYPE_WINBOOL; + } + break; + + default: + m_resID = IDS_EE_BADMARSHAL_BOOLEAN; + IfFailGoto(E_FAIL, lFail); + } + break; + + case ELEMENT_TYPE_CHAR: + switch (nativeType) + { + case NATIVE_TYPE_I1: //fallthru + case NATIVE_TYPE_U1: + m_type = MARSHAL_TYPE_ANSICHAR; + break; + + case NATIVE_TYPE_I2: //fallthru + case NATIVE_TYPE_U2: + m_type = MARSHAL_TYPE_GENERIC_U2; + break; + + case NATIVE_TYPE_DEFAULT: + m_type = ( (m_ms == MARSHAL_SCENARIO_NDIRECT && m_fAnsi) ? MARSHAL_TYPE_ANSICHAR : MARSHAL_TYPE_GENERIC_U2 ); + break; + + default: + m_resID = IDS_EE_BADMARSHAL_CHAR; + IfFailGoto(E_FAIL, lFail); + + } + break; + + case ELEMENT_TYPE_I1: + if (!(nativeType == NATIVE_TYPE_I1 || nativeType == NATIVE_TYPE_U1 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I1; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_1; + break; + + case ELEMENT_TYPE_U1: + if (!(nativeType == NATIVE_TYPE_U1 || nativeType == NATIVE_TYPE_I1 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I1; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_U1; + break; + + case ELEMENT_TYPE_I2: + if (!(nativeType == NATIVE_TYPE_I2 || nativeType == NATIVE_TYPE_U2 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I2; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_2; + break; + + case ELEMENT_TYPE_U2: + if (!(nativeType == NATIVE_TYPE_U2 || nativeType == NATIVE_TYPE_I2 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I2; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_U2; + break; + + case ELEMENT_TYPE_I4: + switch (nativeType) + { + case NATIVE_TYPE_I4: + case NATIVE_TYPE_U4: + case NATIVE_TYPE_DEFAULT: + break; + +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_ERROR: + m_fErrorNativeType = TRUE; + break; +#endif // FEATURE_COMINTEROP + + default: + m_resID = IDS_EE_BADMARSHAL_I4; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_4; + break; + + case ELEMENT_TYPE_U4: + switch (nativeType) + { + case NATIVE_TYPE_I4: + case NATIVE_TYPE_U4: + case NATIVE_TYPE_DEFAULT: + break; + +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_ERROR: + m_fErrorNativeType = TRUE; + break; +#endif // FEATURE_COMINTEROP + + default: + m_resID = IDS_EE_BADMARSHAL_I4; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_4; + break; + + case ELEMENT_TYPE_I8: + if (!(nativeType == NATIVE_TYPE_I8 || nativeType == NATIVE_TYPE_U8 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I8; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_8; + break; + + case ELEMENT_TYPE_U8: + if (!(nativeType == NATIVE_TYPE_U8 || nativeType == NATIVE_TYPE_I8 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I8; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_GENERIC_8; + break; + + case ELEMENT_TYPE_I: +#ifdef FEATURE_COMINTEROP + if (IsWinRTScenario()) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } +#endif // FEATURE_COMINTEROP + + if (!(nativeType == NATIVE_TYPE_INT || nativeType == NATIVE_TYPE_UINT || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I; + IfFailGoto(E_FAIL, lFail); + } + m_type = (sizeof(LPVOID) == 4 ? MARSHAL_TYPE_GENERIC_4 : MARSHAL_TYPE_GENERIC_8); + break; + + case ELEMENT_TYPE_U: +#ifdef FEATURE_COMINTEROP + if (IsWinRTScenario()) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } +#endif // FEATURE_COMINTEROP + + if (!(nativeType == NATIVE_TYPE_UINT || nativeType == NATIVE_TYPE_INT || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_I; + IfFailGoto(E_FAIL, lFail); + } + m_type = (sizeof(LPVOID) == 4 ? MARSHAL_TYPE_GENERIC_4 : MARSHAL_TYPE_GENERIC_8); + break; + + + case ELEMENT_TYPE_R4: + if (!(nativeType == NATIVE_TYPE_R4 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_R4; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_FLOAT; + break; + + case ELEMENT_TYPE_R8: + if (!(nativeType == NATIVE_TYPE_R8 || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_R8; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_DOUBLE; + break; + + case ELEMENT_TYPE_PTR: +#ifdef FEATURE_COMINTEROP + _ASSERTE(!IsWinRTScenario()); // we checked for this earlier +#endif // FEATURE_COMINTEROP + + if (nativeType != NATIVE_TYPE_DEFAULT) + { + m_resID = IDS_EE_BADMARSHAL_PTR; + IfFailGoto(E_FAIL, lFail); + } + m_type = ( (sizeof(void*)==4) ? MARSHAL_TYPE_GENERIC_4 : MARSHAL_TYPE_GENERIC_8 ); + break; + + case ELEMENT_TYPE_FNPTR: +#ifdef FEATURE_COMINTEROP + if (IsWinRTScenario()) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } +#endif // FEATURE_COMINTEROP + + if (!(nativeType == NATIVE_TYPE_FUNC || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_FNPTR; + IfFailGoto(E_FAIL, lFail); + } + m_type = ( (sizeof(void*)==4) ? MARSHAL_TYPE_GENERIC_4 : MARSHAL_TYPE_GENERIC_8 ); + break; + + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VAR: + { + TypeHandle sigTH = sig.GetTypeHandleThrowing(pModule, pTypeContext); + + // Disallow marshaling generic types except for WinRT interfaces. + if (sigTH.HasInstantiation()) + { +#ifdef FEATURE_COMINTEROP + if (!sigTH.SupportsGenericInterop(TypeHandle::Interop_NativeToManaged)) +#endif // FEATURE_COMINTEROP + { + m_resID = IDS_EE_BADMARSHAL_GENERICS_RESTRICTION; + IfFailGoto(E_FAIL, lFail); + } + } + + m_pMT = sigTH.GetMethodTable(); + if (m_pMT == NULL) + IfFailGoto(COR_E_TYPELOAD, lFail); + +#ifdef FEATURE_COMINTEROP + MethodTable* pDefaultMT = NULL; + + // Look for marshaling of WinRT runtime classes + if ((m_pMT->IsProjectedFromWinRT() || m_pMT->IsExportedToWinRT()) && !m_pMT->HasExplicitGuid()) + { + // The type loader guarantees that there are no WinRT interfaces without explicit GUID + _ASSERTE(!m_pMT->IsInterface()); + + // Make sure that this is really a legal runtime class and not a custom attribute or delegate + if (!m_pMT->IsLegalNonArrayWinRTType() || m_pMT->IsDelegate()) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } + + // This class must have a default interface that describes how it is marshaled + pDefaultMT = m_pMT->GetDefaultWinRTInterface(); + if (pDefaultMT == NULL) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_MISSING_GUID; + IfFailGoto(E_FAIL, lFail); + } + } + + if (nativeType == NATIVE_TYPE_INTF) + { + // whatever... + if (sig.IsStringType(pModule, pTypeContext)) + { + m_resID = IDS_EE_BADMARSHALPARAM_STRING; + IfFailGoto(E_FAIL, lFail); + } + + if (m_ms == MARSHAL_SCENARIO_WINRT && COMDelegate::IsDelegate(m_pMT)) + { + // In WinRT scenarios delegates must be WinRT delegates + if (!m_pMT->IsProjectedFromWinRT() && !WinRTTypeNameConverter::IsRedirectedType(m_pMT)) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_DELEGATE; + IfFailGoto(E_FAIL, lFail); + } + } + + m_type = MARSHAL_TYPE_INTERFACE; + } + else if (pDefaultMT != NULL && nativeType == NATIVE_TYPE_DEFAULT) + { + // Pretend this is really marshaling as the default interface type + + // Validate it's a WinRT interface with GUID + if (!pDefaultMT->IsInterface() || + (!pDefaultMT->IsProjectedFromWinRT() && !pDefaultMT->IsExportedToWinRT()) || + !pDefaultMT->HasExplicitGuid()) + { + // This might also be a redirected interface - which is also allowed + if (!pDefaultMT->IsWinRTRedirectedInterface(TypeHandle::Interop_NativeToManaged)) + { + m_resID = IDS_EE_BADMARSHAL_DEFAULTIFACE_NOT_WINRT_IFACE; + IfFailGoto(E_FAIL, lFail); + } + } + + // Validate that it's one of the component interfaces of the class in the signature + if (!m_pMT->ImplementsEquivalentInterface(pDefaultMT)) + { + m_resID = IDS_EE_BADMARSHAL_DEFAULTIFACE_NOT_SUBTYPE; + IfFailGoto(E_FAIL, lFail); + } + + // Make sure it's not an unexpected generic case (not clear we can actually get here in practice due + // to the above Implements check) + if (pDefaultMT->HasInstantiation() && !pDefaultMT->SupportsGenericInterop(TypeHandle::Interop_NativeToManaged)) + { + m_resID = IDS_EE_BADMARSHAL_GENERICS_RESTRICTION; + IfFailGoto(E_FAIL, lFail); + } + + // Store the marshal data just as if we were marshaling as this default interface type + m_type = MARSHAL_TYPE_INTERFACE; + m_pDefaultItfMT = pDefaultMT; + } + else +#endif // FEATURE_COMINTEROP + { + bool builder = false; + if (sig.IsStringTypeThrowing(pModule, pTypeContext) + || ((builder = true), 0) + || sig.IsClassThrowing(pModule, g_StringBufferClassName, pTypeContext) + ) + { + switch ( nativeType ) + { + case NATIVE_TYPE_LPWSTR: + m_type = builder ? MARSHAL_TYPE_LPWSTR_BUFFER : MARSHAL_TYPE_LPWSTR; + break; + + case NATIVE_TYPE_LPSTR: + m_type = builder ? MARSHAL_TYPE_LPSTR_BUFFER : MARSHAL_TYPE_LPSTR; + break; + + case NATIVE_TYPE_LPUTF8STR: + m_type = builder ? MARSHAL_TYPE_UTF8_BUFFER : MARSHAL_TYPE_LPUTF8STR; + break; + + case NATIVE_TYPE_LPTSTR: + { +#ifdef FEATURE_COMINTEROP + if (m_ms != MARSHAL_SCENARIO_NDIRECT) + { + _ASSERTE(m_ms == MARSHAL_SCENARIO_COMINTEROP); + // We disallow NATIVE_TYPE_LPTSTR for COM. + IfFailGoto(E_FAIL, lFail); + } +#endif // FEATURE_COMINTEROP + // We no longer support Win9x so LPTSTR always maps to a Unicode string. + m_type = builder ? MARSHAL_TYPE_LPWSTR_BUFFER : MARSHAL_TYPE_LPWSTR; + break; + } + +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_BSTR: + if (builder) + { + m_resID = IDS_EE_BADMARSHALPARAM_STRINGBUILDER; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_BSTR; + break; + + case NATIVE_TYPE_ANSIBSTR: + if (builder) + { + m_resID = IDS_EE_BADMARSHALPARAM_STRINGBUILDER; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_ANSIBSTR; + break; + + case NATIVE_TYPE_TBSTR: + { + if (builder) + { + m_resID = IDS_EE_BADMARSHALPARAM_STRINGBUILDER; + IfFailGoto(E_FAIL, lFail); + } + + // We no longer support Win9x so TBSTR always maps to a normal (unicode) BSTR. + m_type = MARSHAL_TYPE_BSTR; + break; + } + + case NATIVE_TYPE_BYVALSTR: + { + if (builder) + { + m_resID = IDS_EE_BADMARSHALPARAM_STRINGBUILDER; + IfFailGoto(E_FAIL, lFail); + } + m_type = m_fAnsi ? MARSHAL_TYPE_VBBYVALSTR : MARSHAL_TYPE_VBBYVALSTRW; + break; + } + + case NATIVE_TYPE_HSTRING: + { + if (builder) + { + m_resID = IDS_EE_BADMARSHALPARAM_STRINGBUILDER; + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_HSTRING; + break; + } +#endif // FEATURE_COMINTEROP + + case NATIVE_TYPE_DEFAULT: + { +#ifdef FEATURE_COMINTEROP + if (m_ms == MARSHAL_SCENARIO_WINRT) + { + if (builder) + { + m_resID = IDS_EE_BADMARSHALPARAM_STRINGBUILDER; + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_HSTRING; + } + else if (m_ms != MARSHAL_SCENARIO_NDIRECT) + { + _ASSERTE(m_ms == MARSHAL_SCENARIO_COMINTEROP); + m_type = builder ? MARSHAL_TYPE_LPWSTR_BUFFER : MARSHAL_TYPE_BSTR; + } + else +#endif // FEATURE_COMINTEROP + if (m_fAnsi) + { + m_type = builder ? MARSHAL_TYPE_LPSTR_BUFFER : MARSHAL_TYPE_LPSTR; + } + else + { + m_type = builder ? MARSHAL_TYPE_LPWSTR_BUFFER : MARSHAL_TYPE_LPWSTR; + } + break; + } + + default: + m_resID = builder ? IDS_EE_BADMARSHALPARAM_STRINGBUILDER : IDS_EE_BADMARSHALPARAM_STRING; + IfFailGoto(E_FAIL, lFail); + break; + } + } +#ifdef FEATURE_COMINTEROP + else if (sig.IsClassThrowing(pModule, g_CollectionsEnumeratorClassName, pTypeContext) && + nativeType == NATIVE_TYPE_DEFAULT) + { + m_CMVt = VT_UNKNOWN; + m_type = MARSHAL_TYPE_REFERENCECUSTOMMARSHALER; + + if (fLoadCustomMarshal) + { + if (!fEmitsIL) + { + m_pCMHelper = SetupCustomMarshalerHelper(ENUMERATOR_TO_ENUM_VARIANT_CM_NAME, + ENUMERATOR_TO_ENUM_VARIANT_CM_NAME_LEN, + ENUMERATOR_TO_ENUM_VARIANT_CM_COOKIE, + ENUMERATOR_TO_ENUM_VARIANT_CM_COOKIE_LEN, + pAssembly, sigTH); + } + else + { + m_pCMHelper = NULL; + MethodDesc* pMDforModule = pMD; + if (pMD->IsILStub()) + { + pMDforModule = pMD->AsDynamicMethodDesc()->GetILStubResolver()->GetStubTargetMethodDesc(); + } + m_args.rcm.m_pMD = pMDforModule; + m_args.rcm.m_paramToken = token; + m_args.rcm.m_hndManagedType = sigTH.AsPtr(); + CONSISTENCY_CHECK(pModule == pMDforModule->GetModule()); + } + } + } +#endif // FEATURE_COMINTEROP + else if (sigTH.CanCastTo(TypeHandle(MscorlibBinder::GetClass(CLASS__SAFE_HANDLE)))) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + m_resID = IDS_EE_BADMARSHAL_SAFEHANDLE; + IfFailGoto(E_FAIL, lFail); + } + m_args.m_pMT = m_pMT; + m_type = MARSHAL_TYPE_SAFEHANDLE; + } + else if (sigTH.CanCastTo(TypeHandle(MscorlibBinder::GetClass(CLASS__CRITICAL_HANDLE)))) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + m_resID = IDS_EE_BADMARSHAL_CRITICALHANDLE; + IfFailGoto(E_FAIL, lFail); + } + m_args.m_pMT = m_pMT; + m_type = MARSHAL_TYPE_CRITICALHANDLE; + } + else if (sig.IsClassThrowing(pModule, g_ReflectionMethodInterfaceName, pTypeContext)) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_RUNTIMEMETHODINFO; + } +#ifdef FEATURE_COMINTEROP + else if (m_pMT->IsInterface()) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT || + nativeType == NATIVE_TYPE_INTF)) + { + m_resID = IDS_EE_BADMARSHAL_INTERFACE; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_INTERFACE; + + if (m_ms == MARSHAL_SCENARIO_WINRT) + { + // all interfaces marshaled in WinRT scenarios are IInspectable-based + m_fInspItf = TRUE; + } + } + // Check for Windows.Foundation.HResult <-> Exception + else if (m_ms == MARSHAL_SCENARIO_WINRT && MscorlibBinder::IsClass(m_pMT, CLASS__EXCEPTION)) + { + m_args.m_pMT = m_pMT; + m_type = MARSHAL_TYPE_EXCEPTION; + } +#endif // FEATURE_COMINTEROP + else if (COMDelegate::IsDelegate(m_pMT)) + { + m_args.m_pMT = m_pMT; +#ifdef FEATURE_COMINTEROP + if (m_ms == MARSHAL_SCENARIO_WINRT) + { + // Delegates must be imported from WinRT and marshaled as Interface + if (!m_pMT->IsProjectedFromWinRT() && !WinRTTypeNameConverter::IsRedirectedType(m_pMT)) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_DELEGATE; + IfFailGoto(E_FAIL, lFail); + } + } +#endif // FEATURE_COMINTEROP + + switch (nativeType) + { + case NATIVE_TYPE_FUNC: + m_type = MARSHAL_TYPE_DELEGATE; + break; + + case NATIVE_TYPE_DEFAULT: +#ifdef FEATURE_COMINTEROP + if (m_ms != MARSHAL_SCENARIO_NDIRECT) + { + _ASSERTE(m_ms == MARSHAL_SCENARIO_COMINTEROP || m_ms == MARSHAL_SCENARIO_WINRT); + m_type = MARSHAL_TYPE_INTERFACE; + } + else +#endif // FEATURE_COMINTEROP + m_type = MARSHAL_TYPE_DELEGATE; + + break; + + default: + m_resID = IDS_EE_BADMARSHAL_DELEGATE; + IfFailGoto(E_FAIL, lFail); + break; + } + } + else if (m_pMT->IsBlittable()) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_LPSTRUCT)) + { + m_resID = IDS_EE_BADMARSHAL_CLASS; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_BLITTABLEPTR; + m_args.m_pMT = m_pMT; + } + else if (m_pMT->HasLayout()) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_LPSTRUCT)) + { + m_resID = IDS_EE_BADMARSHAL_CLASS; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_LAYOUTCLASSPTR; + m_args.m_pMT = m_pMT; + } + else if (sig.IsClassThrowing(pModule, g_ReflectionModuleName, pTypeContext)) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_RUNTIMEMODULE; + } + else if (sig.IsClassThrowing(pModule, g_ReflectionAssemblyName, pTypeContext)) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_RUNTIMEASSEMBLY; + } +#ifdef FEATURE_COMINTEROP + else if (m_ms == MARSHAL_SCENARIO_WINRT && sig.IsClassThrowing(pModule, g_SystemUriClassName, pTypeContext)) + { + m_type = MARSHAL_TYPE_URI; + } + else if (m_ms == MARSHAL_SCENARIO_WINRT && sig.IsClassThrowing(pModule, g_NotifyCollectionChangedEventArgsName, pTypeContext)) + { + m_type = MARSHAL_TYPE_NCCEVENTARGS; + } + else if (m_ms == MARSHAL_SCENARIO_WINRT && sig.IsClassThrowing(pModule, g_PropertyChangedEventArgsName, pTypeContext)) + { + m_type = MARSHAL_TYPE_PCEVENTARGS; + } +#endif // FEATURE_COMINTEROP + else if (m_pMT->IsObjectClass()) + { + switch(nativeType) + { +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_DEFAULT: + if (ms == MARSHAL_SCENARIO_WINRT) + { + m_fInspItf = TRUE; + m_type = MARSHAL_TYPE_INTERFACE; + break; + } + // fall through + case NATIVE_TYPE_STRUCT: + m_type = MARSHAL_TYPE_OBJECT; + break; + + case NATIVE_TYPE_INTF: + case NATIVE_TYPE_IUNKNOWN: + m_type = MARSHAL_TYPE_INTERFACE; + break; + + case NATIVE_TYPE_IDISPATCH: + m_fDispItf = TRUE; + m_type = MARSHAL_TYPE_INTERFACE; + break; + + case NATIVE_TYPE_IINSPECTABLE: + m_fInspItf = TRUE; + m_type = MARSHAL_TYPE_INTERFACE; + break; +#else + case NATIVE_TYPE_DEFAULT: + case NATIVE_TYPE_STRUCT: + m_resID = IDS_EE_OBJECT_TO_VARIANT_NOT_SUPPORTED; + IfFailGoto(E_FAIL, lFail); + + case NATIVE_TYPE_INTF: + case NATIVE_TYPE_IUNKNOWN: + case NATIVE_TYPE_IDISPATCH: + m_resID = IDS_EE_OBJECT_TO_ITF_NOT_SUPPORTED; + IfFailGoto(E_FAIL, lFail); +#endif // FEATURE_COMINTEROP + + case NATIVE_TYPE_ASANY: + m_type = m_fAnsi ? MARSHAL_TYPE_ASANYA : MARSHAL_TYPE_ASANYW; + break; + + default: + m_resID = IDS_EE_BADMARSHAL_OBJECT; + IfFailGoto(E_FAIL, lFail); + } + } + +#ifdef FEATURE_COMINTEROP + else if (sig.IsClassThrowing(pModule, g_ArrayClassName, pTypeContext)) + { + if (IsWinRTScenario()) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } + + switch(nativeType) + { + case NATIVE_TYPE_DEFAULT: + case NATIVE_TYPE_INTF: + m_type = MARSHAL_TYPE_INTERFACE; + break; + + case NATIVE_TYPE_SAFEARRAY: + { + TypeHandle thElement = TypeHandle(g_pObjectClass); + + if (ParamInfo.m_SafeArrayElementVT != VT_EMPTY) + { + if (ParamInfo.m_cSafeArrayUserDefTypeNameBytes > 0) + { + // Load the type. Use an SString for the string since we need to NULL terminate the string + // that comes from the metadata. + StackScratchBuffer utf8Name; + SString safeArrayUserDefTypeName(SString::Utf8, ParamInfo.m_strSafeArrayUserDefTypeName, ParamInfo.m_cSafeArrayUserDefTypeNameBytes); + thElement = TypeName::GetTypeUsingCASearchRules(safeArrayUserDefTypeName.GetUTF8(utf8Name), pAssembly); + } + } + else + { + // Compat: If no safe array VT was specified, default to VT_VARIANT. + ParamInfo.m_SafeArrayElementVT = VT_VARIANT; + } + + IfFailGoto(HandleArrayElemType(&ParamInfo, 0, thElement, -1, FALSE, isParam, pAssembly), lFail); + break; + } + + default: + m_resID = IDS_EE_BADMARSHAL_SYSARRAY; + IfFailGoto(E_FAIL, lFail); + + } + } + + else if (m_pMT->IsArray()) + { + _ASSERTE(!"This invalid signature should never be hit!"); + IfFailGoto(E_FAIL, lFail); + } + else if ((m_ms == MARSHAL_SCENARIO_WINRT) && sig.IsClassThrowing(pModule, g_TypeClassName, pTypeContext)) + { + m_type = MARSHAL_TYPE_SYSTEMTYPE; + } +#endif // FEATURE_COMINTEROP + else if (!m_pMT->IsValueType()) + { +#ifdef FEATURE_COMINTEROP + if (IsWinRTScenario() && !m_pMT->IsLegalNonArrayWinRTType()) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } +#endif // FEATURE_COMINTEROP + + if (!(nativeType == NATIVE_TYPE_INTF || nativeType == NATIVE_TYPE_DEFAULT)) + { + m_resID = IDS_EE_BADMARSHAL_NOLAYOUT; + IfFailGoto(E_FAIL, lFail); + } +#ifdef FEATURE_COMINTEROP + // default marshalling is interface + m_type = MARSHAL_TYPE_INTERFACE; +#else // FEATURE_COMINTEROP + m_resID = IDS_EE_OBJECT_TO_ITF_NOT_SUPPORTED; + IfFailGoto(E_FAIL, lFail); +#endif // FEATURE_COMINTEROP + } + + else + { + _ASSERTE(m_pMT->IsValueType()); + goto lValueClass; + } + } + break; + } + + + case ELEMENT_TYPE_VALUETYPE: + lValueClass: + { + if (sig.IsClassThrowing(pModule, g_DecimalClassName, pTypeContext)) + { + switch (nativeType) + { + case NATIVE_TYPE_DEFAULT: + case NATIVE_TYPE_STRUCT: + m_type = MARSHAL_TYPE_DECIMAL; + break; + + case NATIVE_TYPE_LPSTRUCT: + m_type = MARSHAL_TYPE_DECIMAL_PTR; + break; + + case NATIVE_TYPE_CURRENCY: + m_type = MARSHAL_TYPE_CURRENCY; + break; + + default: + m_resID = IDS_EE_BADMARSHALPARAM_DECIMAL; + IfFailGoto(E_FAIL, lFail); + } + } + else if (sig.IsClassThrowing(pModule, g_GuidClassName, pTypeContext)) + { + switch (nativeType) + { + case NATIVE_TYPE_DEFAULT: + case NATIVE_TYPE_STRUCT: + m_type = MARSHAL_TYPE_GUID; + break; + + case NATIVE_TYPE_LPSTRUCT: + m_type = MARSHAL_TYPE_GUID_PTR; + break; + + default: + m_resID = IDS_EE_BADMARSHAL_GUID; + IfFailGoto(E_FAIL, lFail); + } + } +#ifdef FEATURE_COMINTEROP + else if (sig.IsClassThrowing(pModule, g_DateTimeOffsetClassName, pTypeContext)) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_STRUCT)) + { + m_resID = IDS_EE_BADMARSHAL_DATETIMEOFFSET; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_DATETIME; + m_pMT = MscorlibBinder::GetClass(CLASS__DATE_TIME_OFFSET); + } +#endif // FEATURE_COMINTEROP + else if (sig.IsClassThrowing(pModule, g_DateClassName, pTypeContext)) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_STRUCT)) + { + m_resID = IDS_EE_BADMARSHAL_DATETIME; + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_DATE; + } + else if (sig.IsClassThrowing(pModule, "System.Runtime.InteropServices.ArrayWithOffset", pTypeContext)) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT)) + { + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_ARRAYWITHOFFSET; + } + else if (sig.IsClassThrowing(pModule, "System.Runtime.InteropServices.HandleRef", pTypeContext)) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT)) + { + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_HANDLEREF; + } + else if (sig.IsClassThrowing(pModule, "System.ArgIterator", pTypeContext)) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT)) + { + IfFailGoto(E_FAIL, lFail); + } + m_type = MARSHAL_TYPE_ARGITERATOR; + } +#ifdef FEATURE_COMINTEROP + else if (sig.IsClassThrowing(pModule, g_ColorClassName, pTypeContext)) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT)) + { + IfFailGoto(E_FAIL, lFail); + } + + // This is only supported for COM interop. + if (m_ms != MARSHAL_SCENARIO_COMINTEROP) + { + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_OLECOLOR; + } +#endif // FEATURE_COMINTEROP + else if (sig.IsClassThrowing(pModule, g_RuntimeTypeHandleClassName, pTypeContext)) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_RUNTIMETYPEHANDLE; + } + else if (sig.IsClassThrowing(pModule, g_RuntimeFieldHandleClassName, pTypeContext)) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_RUNTIMEFIELDHANDLE; + } + else if (sig.IsClassThrowing(pModule, g_RuntimeMethodHandleClassName, pTypeContext)) + { + if (nativeType != NATIVE_TYPE_DEFAULT) + { + IfFailGoto(E_FAIL, lFail); + } + + m_type = MARSHAL_TYPE_RUNTIMEMETHODHANDLE; + } + else + { + m_pMT = sig.GetTypeHandleThrowing(pModule, pTypeContext).GetMethodTable(); + if (m_pMT == NULL) + break; + +#ifdef FEATURE_COMINTEROP + // Handle Nullable<T> and KeyValuePair<K, V> for WinRT + if (m_ms == MARSHAL_SCENARIO_WINRT) + { + if (m_pMT->HasSameTypeDefAs(g_pNullableClass)) + { + m_type = MARSHAL_TYPE_NULLABLE; + m_args.m_pMT = m_pMT; + break; + } + + if (m_pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__KEYVALUEPAIRGENERIC))) + { + m_type = MARSHAL_TYPE_KEYVALUEPAIR; + m_args.m_pMT = m_pMT; + break; + } + + if (!m_pMT->IsLegalNonArrayWinRTType()) + { + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + IfFailGoto(E_FAIL, lFail); + } + } +#endif // FEATURE_COMINTEROP + + if (m_pMT->HasInstantiation()) + { + m_resID = IDS_EE_BADMARSHAL_GENERICS_RESTRICTION; + IfFailGoto(E_FAIL, lFail); + } + + UINT managedSize = m_pMT->GetAlignedNumInstanceFieldBytes(); + UINT nativeSize = m_pMT->GetNativeSize(); + + if ( nativeSize > 0xfff0 || + managedSize > 0xfff0) + { + m_resID = IDS_EE_STRUCTTOOCOMPLEX; + IfFailGoto(E_FAIL, lFail); + } + + if (m_pMT->IsBlittable()) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_STRUCT)) + { + m_resID = IDS_EE_BADMARSHAL_VALUETYPE; + IfFailGoto(E_FAIL, lFail); + } + + if (m_byref && !isParam) + { + // Override the prohibition on byref returns so that IJW works + m_byref = FALSE; + m_type = ((sizeof(void*) == 4) ? MARSHAL_TYPE_GENERIC_4 : MARSHAL_TYPE_GENERIC_8); + } + else + { +#ifndef FEATURE_CORECLR + if (fNeedsCopyCtor) + { + if (m_ms == MARSHAL_SCENARIO_WINRT) + { + // our WinRT-optimized GetCOMIPFromRCW helpers don't support copy + // constructor stubs so make sure that this marshaler will not be used + m_resID = IDS_EE_BADMARSHAL_WINRT_COPYCTOR; + IfFailGoto(E_FAIL, lFail); + } + + MethodDesc *pCopyCtor; + MethodDesc *pDtor; + FindCopyCtor(pModule, m_pMT, &pCopyCtor); + FindDtor(pModule, m_pMT, &pDtor); + + m_args.mm.m_pMT = m_pMT; + m_args.mm.m_pCopyCtor = pCopyCtor; + m_args.mm.m_pDtor = pDtor; + m_type = MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR; + } + else +#endif // !FEATURE_CORECLR +#ifdef _TARGET_X86_ + // JIT64 is not aware of normalized value types and this optimization + // (returning small value types by value in registers) is already done in JIT64. + if ( !m_byref // Permit register-sized structs as return values + && !isParam + && CorIsPrimitiveType(m_pMT->GetInternalCorElementType()) + && !IsUnmanagedValueTypeReturnedByRef(nativeSize) + && managedSize <= sizeof(void*) + && nativeSize <= sizeof(void*)) + { + m_type = MARSHAL_TYPE_GENERIC_4; + m_args.m_pMT = m_pMT; + } + else +#endif // _TARGET_X86_ + { + m_args.m_pMT = m_pMT; + m_type = MARSHAL_TYPE_BLITTABLEVALUECLASS; + } + } + } + else if (m_pMT->HasLayout()) + { + if (!(nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_STRUCT)) + { + m_resID = IDS_EE_BADMARSHAL_VALUETYPE; + IfFailGoto(E_FAIL, lFail); + } + + m_args.m_pMT = m_pMT; + m_type = MARSHAL_TYPE_VALUECLASS; + } + } + break; + } + + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_ARRAY: + { + // Get class info from array. + TypeHandle arrayTypeHnd = sig.GetTypeHandleThrowing(pModule, pTypeContext); + _ASSERTE(!arrayTypeHnd.IsNull()); + + ArrayTypeDesc* asArray = arrayTypeHnd.AsArray(); + if (asArray == NULL) + IfFailGoto(E_FAIL, lFail); + + TypeHandle thElement = asArray->GetTypeParam(); + +#ifdef FEATURE_COMINTEROP + if (m_ms != MARSHAL_SCENARIO_WINRT) +#endif // FEATURE_COMINTEROP + { + if (thElement.HasInstantiation()) + { + m_resID = IDS_EE_BADMARSHAL_GENERICS_RESTRICTION; + IfFailGoto(E_FAIL, lFail); + } + } + + unsigned ofs = 0; + if (arrayTypeHnd.GetMethodTable()) + { + ofs = ArrayBase::GetDataPtrOffset(arrayTypeHnd.GetMethodTable()); + if (ofs > 0xffff) + { + ofs = 0; // can't represent it, so pass on magic value (which causes fallback to regular ML code) + } + } + + // Handle retrieving the information for the array type. + IfFailGoto(HandleArrayElemType(&ParamInfo, (UINT16)ofs, thElement, asArray->GetRank(), mtype == ELEMENT_TYPE_SZARRAY, isParam, pAssembly), lFail); + break; + } + + default: + m_resID = IDS_EE_BADMARSHAL_BADMANAGED; + } + +lExit: +#ifdef FEATURE_COMINTEROP +#ifdef FEATURE_CORECLR +//Field scenario is not blocked here because we don't want to block loading structs that +//have the types which we are blocking, but never pass it to Interop. + + if (AppX::IsAppXProcess() && ms != MarshalInfo::MARSHAL_SCENARIO_FIELD) + { + bool set_error = false; + switch (m_type) + { + case MARSHAL_TYPE_ANSIBSTR: + m_resID = IDS_EE_BADMARSHAL_TYPE_ANSIBSTR; + set_error = true; + break; + case MARSHAL_TYPE_VBBYVALSTR: + case MARSHAL_TYPE_VBBYVALSTRW: + m_resID = IDS_EE_BADMARSHAL_TYPE_VBBYVALSTR; + set_error = true; + break; + case MARSHAL_TYPE_REFERENCECUSTOMMARSHALER: + m_resID = IDS_EE_BADMARSHAL_TYPE_REFERENCECUSTOMMARSHALER; + set_error = true; + break; + case MARSHAL_TYPE_ASANYA: + case MARSHAL_TYPE_ASANYW: + m_resID = IDS_EE_BADMARSHAL_TYPE_ASANYA; + set_error = true; + break; + case MARSHAL_TYPE_INTERFACE: + if (m_fDispItf) + { + m_resID = IDS_EE_BADMARSHAL_TYPE_IDISPATCH; + set_error = true; + } + break; + } + + if (set_error) + COMPlusThrow(kPlatformNotSupportedException, m_resID); + + } +#endif // FEATURE_CORECLR + + if (IsWinRTScenario() && !IsSupportedForWinRT(m_type)) + { + // the marshaler we came up with is not supported in WinRT scenarios + m_type = MARSHAL_TYPE_UNKNOWN; + m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE; + goto lReallyExit; + } +#endif // FEATURE_COMINTEROP + + if (m_byref && !isParam) + { + // byref returns don't work: the thing pointed to lives on + // a stack that disappears! + m_type = MARSHAL_TYPE_UNKNOWN; + goto lReallyExit; + } + + //--------------------------------------------------------------------- + // Now, figure out the IN/OUT status. + // Also set the m_fOleVarArgCandidate here to save perf of invoking Metadata API + //--------------------------------------------------------------------- + m_fOleVarArgCandidate = FALSE; + if (m_type != MARSHAL_TYPE_UNKNOWN && IsInOnly(m_type) && !m_byref) + { + // If we got here, the parameter is something like an "int" where + // [in] is the only semantically valid choice. Since there is no + // possible way to interpret an [out] for such a type, we will ignore + // the metadata and force the bits to "in". We could have defined + // it as an error instead but this is less likely to cause problems + // with metadata autogenerated from typelibs and poorly + // defined C headers. + // + m_in = TRUE; + m_out = FALSE; + } + else + { + + // Capture and save away "In/Out" bits. If none is present, set both to FALSE (they will be properly defaulted downstream) + if (token == mdParamDefNil) + { + m_in = FALSE; + m_out = FALSE; + } + else if (TypeFromToken(token) != mdtParamDef) + { + _ASSERTE(TypeFromToken(token) == mdtFieldDef); + + // Field setters are always In, the flags are ignored for return values of getters + m_in = TRUE; + m_out = FALSE; + } + else + { + IMDInternalImport *pInternalImport = pModule->GetMDImport(); + USHORT usSequence; + DWORD dwAttr; + LPCSTR szParamName_Ignore; + + if (FAILED(pInternalImport->GetParamDefProps(token, &usSequence, &dwAttr, &szParamName_Ignore))) + { + m_in = FALSE; + m_out = FALSE; + } + else + { + m_in = IsPdIn(dwAttr) != 0; + m_out = IsPdOut(dwAttr) != 0; +#ifdef FEATURE_COMINTEROP + // set m_fOleVarArgCandidate. The rule is same as the one defined in vm\tlbexp.cpp + if(paramidx == numArgs && // arg is the last arg of the method + !(dwAttr & PARAMFLAG_FOPT) && // arg is not a optional arg + !IsNilToken(token) && // token is not a nil token + (m_type == MARSHAL_TYPE_SAFEARRAY) && // arg is marshaled as SafeArray + (m_arrayElementType == VT_VARIANT)) // the element of the safearray is VARIANT + { + // check if it has default value + MDDefaultValue defaultValue; + if (SUCCEEDED(pInternalImport->GetDefaultValue(token, &defaultValue)) && defaultValue.m_bType == ELEMENT_TYPE_VOID) + { + // check if it has params attribute + if (pInternalImport->GetCustomAttributeByName(token, INTEROP_PARAMARRAY_TYPE, 0,0) == S_OK) + m_fOleVarArgCandidate = TRUE; + } + } +#endif + } + } + + // If neither IN nor OUT are true, this signals the URT to use the default + // rules. + if (!m_in && !m_out) + { + if (m_byref || + (mtype == ELEMENT_TYPE_CLASS + && !(sig.IsStringType(pModule, pTypeContext)) + && sig.IsClass(pModule, g_StringBufferClassName, pTypeContext))) + { + m_in = TRUE; + m_out = TRUE; + } + else + { + m_in = TRUE; + m_out = FALSE; + } + + } + } + +lReallyExit: + +#ifdef _DEBUG + DumpMarshalInfo(pModule, sig, pTypeContext, token, ms, nlType, nlFlags); +#endif + return; + + + lFail: + // We got here because of an illegal ELEMENT_TYPE/NATIVE_TYPE combo. + m_type = MARSHAL_TYPE_UNKNOWN; + //_ASSERTE(!"Invalid ELEMENT_TYPE/NATIVE_TYPE combination"); + goto lExit; +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +VOID MarshalInfo::EmitOrThrowInteropParamException(NDirectStubLinker* psl, BOOL fMngToNative, UINT resID, UINT paramIdx) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + +#ifdef FEATURE_COMINTEROP + // If this is not forward COM interop, throw the exception right away. We rely on this + // for example in code:ComPreStubWorker when we fire the InvalidMemberDeclaration MDA. + if ((m_ms == MARSHAL_SCENARIO_COMINTEROP || m_ms == MARSHAL_SCENARIO_WINRT) && fMngToNative) + { + psl->SetInteropParamExceptionInfo(resID, paramIdx); + return; + } +#endif // FEATURE_COMINTEROP + + ThrowInteropParamException(resID, paramIdx); +} + + +HRESULT MarshalInfo::HandleArrayElemType(NativeTypeParamInfo *pParamInfo, UINT16 optbaseoffset, TypeHandle thElement, int iRank, BOOL fNoLowerBounds, BOOL isParam, Assembly *pAssembly) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pParamInfo)); + } + CONTRACTL_END; + + ArrayMarshalInfo arrayMarshalInfo(amiRuntime); + + + // + // Store rank and bound information. + // + + m_iArrayRank = iRank; + m_nolowerbounds = fNoLowerBounds; + + + // + // Determine which type of marshaler to use. + // + +#ifdef FEATURE_COMINTEROP + if (m_ms == MARSHAL_SCENARIO_WINRT) + { + m_type = MARSHAL_TYPE_HIDDENLENGTHARRAY; + } + else if (pParamInfo->m_NativeType == NATIVE_TYPE_SAFEARRAY) + { + m_type = MARSHAL_TYPE_SAFEARRAY; + } + else +#endif // FEATURE_COMINTEROP + if (pParamInfo->m_NativeType == NATIVE_TYPE_ARRAY) + { + m_type = MARSHAL_TYPE_NATIVEARRAY; + } + else if (pParamInfo->m_NativeType == NATIVE_TYPE_DEFAULT) + { +#ifdef FEATURE_COMINTEROP + if (m_ms != MARSHAL_SCENARIO_NDIRECT) + { + m_type = MARSHAL_TYPE_SAFEARRAY; + } + else +#endif // FEATURE_COMINTEROP + { + m_type = MARSHAL_TYPE_NATIVEARRAY; + } + } + else + { + m_resID = IDS_EE_BADMARSHAL_ARRAY; + return E_FAIL; + } + +#ifdef FEATURE_COMINTEROP + if (m_type == MARSHAL_TYPE_SAFEARRAY) + { + arrayMarshalInfo.InitForSafeArray(m_ms, thElement, pParamInfo->m_SafeArrayElementVT, m_fAnsi); + } + else if (m_type == MARSHAL_TYPE_HIDDENLENGTHARRAY) + { + arrayMarshalInfo.InitForHiddenLengthArray(thElement); + } + else +#endif // FEATURE_COMINTEROP + { + _ASSERTE(m_type == MARSHAL_TYPE_NATIVEARRAY); + arrayMarshalInfo.InitForNativeArray(m_ms, thElement, pParamInfo->m_ArrayElementType, m_fAnsi); + } + + // Make sure the marshalling information is valid. + if (!arrayMarshalInfo.IsValid()) + { + m_resID = arrayMarshalInfo.GetErrorResourceId(); + return E_FAIL; + } + + // Set the array type handle and VARTYPE to use for marshalling. + m_hndArrayElemType = arrayMarshalInfo.GetElementTypeHandle(); + m_arrayElementType = arrayMarshalInfo.GetElementVT(); + + if (m_type == MARSHAL_TYPE_NATIVEARRAY) + { + // Retrieve the extra information associated with the native array marshaling. + m_args.na.m_vt = m_arrayElementType; + m_args.na.m_pMT = !m_hndArrayElemType.IsTypeDesc() ? m_hndArrayElemType.AsMethodTable() : NULL; + m_args.na.m_optionalbaseoffset = optbaseoffset; + m_countParamIdx = pParamInfo->m_CountParamIdx; + m_multiplier = pParamInfo->m_Multiplier; + m_additive = pParamInfo->m_Additive; + } +#ifdef FEATURE_COMINTEROP + else if (m_type == MARSHAL_TYPE_HIDDENLENGTHARRAY) + { + m_args.na.m_optionalbaseoffset = optbaseoffset; + + m_args.na.m_vt = m_arrayElementType; + m_args.na.m_pMT = m_hndArrayElemType.AsMethodTable(); + m_args.na.m_cbElementSize = arrayMarshalInfo.GetElementSize(); + m_args.na.m_redirectedTypeIndex = arrayMarshalInfo.GetRedirectedTypeIndex(); + } +#endif // FEATURE_COMINTEROP + + return S_OK; +} + +ILMarshaler* CreateILMarshaler(MarshalInfo::MarshalType mtype, NDirectStubLinker* psl) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + ILMarshaler* pMarshaler = NULL; + switch (mtype) + { + +#define DEFINE_MARSHALER_TYPE(mt, mclass, fWinRTSupported) \ + case MarshalInfo::mt: \ + pMarshaler = new IL##mclass(); \ + break; +#include "mtypes.h" +#undef DEFINE_MARSHALER_TYPE + + default: + UNREACHABLE_MSG("unexpected MarshalType passed to CreateILMarshaler"); + } + + pMarshaler->SetNDirectStubLinker(psl); + return pMarshaler; +} + + + +DWORD CalculateArgumentMarshalFlags(BOOL byref, BOOL in, BOOL out, BOOL fMngToNative) +{ + LIMITED_METHOD_CONTRACT; + DWORD dwMarshalFlags = 0; + + if (byref) + { + dwMarshalFlags |= MARSHAL_FLAG_BYREF; + } + + if (in) + { + dwMarshalFlags |= MARSHAL_FLAG_IN; + } + + if (out) + { + dwMarshalFlags |= MARSHAL_FLAG_OUT; + } + + if (fMngToNative) + { + dwMarshalFlags |= MARSHAL_FLAG_CLR_TO_NATIVE; + } + + return dwMarshalFlags; +} + +DWORD CalculateReturnMarshalFlags(BOOL hrSwap, BOOL fMngToNative) +{ + LIMITED_METHOD_CONTRACT; + DWORD dwMarshalFlags = MARSHAL_FLAG_RETVAL; + + if (hrSwap) + { + dwMarshalFlags |= MARSHAL_FLAG_HRESULT_SWAP; + } + + if (fMngToNative) + { + dwMarshalFlags |= MARSHAL_FLAG_CLR_TO_NATIVE; + } + + return dwMarshalFlags; +} + +void MarshalInfo::GenerateArgumentIL(NDirectStubLinker* psl, + int argOffset, // the argument's index is m_paramidx + argOffset + UINT nativeStackOffset, // offset of the argument on the native stack + BOOL fMngToNative) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(psl)); + } + CONTRACTL_END; + + if (m_type == MARSHAL_TYPE_UNKNOWN) + { + EmitOrThrowInteropParamException(psl, fMngToNative, m_resID, m_paramidx + 1); // m_paramidx is 0-based, but the user wants to see a 1-based index + return; + } + + // set up m_corArgSize and m_nativeArgSize + SetupArgumentSizes(); + + MarshalerOverrideStatus amostat; + UINT resID = IDS_EE_BADMARSHAL_RESTRICTION; + amostat = (GetArgumentOverrideProc(m_type)) (psl, + m_byref, + m_in, + m_out, + fMngToNative, + &m_args, + &resID, + m_paramidx + argOffset, + nativeStackOffset); + + + if (amostat == OVERRIDDEN) + { + return; + } + + if (amostat == DISALLOWED) + { + EmitOrThrowInteropParamException(psl, fMngToNative, resID, m_paramidx + 1); // m_paramidx is 0-based, but the user wants to see a 1-based index + return; + } + + CONSISTENCY_CHECK(amostat == HANDLEASNORMAL); + + NewHolder<ILMarshaler> pMarshaler = CreateILMarshaler(m_type, psl); + DWORD dwMarshalFlags = CalculateArgumentMarshalFlags(m_byref, m_in, m_out, fMngToNative); + + if (!pMarshaler->SupportsArgumentMarshal(dwMarshalFlags, &resID)) + { + EmitOrThrowInteropParamException(psl, fMngToNative, resID, m_paramidx + 1); // m_paramidx is 0-based, but the user wants to see a 1-based index + return; + } + + ILCodeStream* pcsMarshal = psl->GetMarshalCodeStream(); + ILCodeStream* pcsUnmarshal = psl->GetUnmarshalCodeStream(); + ILCodeStream* pcsDispatch = psl->GetDispatchCodeStream(); + + pcsMarshal->EmitNOP("// argument { "); + pcsUnmarshal->EmitNOP("// argument { "); + + pMarshaler->EmitMarshalArgument(pcsMarshal, pcsUnmarshal, m_paramidx + argOffset, dwMarshalFlags, &m_args); + + // + // Increment a counter so that when the finally clause + // is run, we only run the cleanup that is needed. + // + if (pMarshaler->NeedsMarshalCleanupIndex()) + { + // we don't bother writing to the counter if marshaling does not need cleanup + psl->EmitSetArgMarshalIndex(pcsMarshal, NDirectStubLinker::CLEANUP_INDEX_ARG0_MARSHAL + m_paramidx + argOffset); + } + if (pMarshaler->NeedsUnmarshalCleanupIndex()) + { + // we don't bother writing to the counter if unmarshaling does not need exception cleanup + psl->EmitSetArgMarshalIndex(pcsUnmarshal, NDirectStubLinker::CLEANUP_INDEX_ARG0_UNMARSHAL + m_paramidx + argOffset); + } + + pcsMarshal->EmitNOP("// } argument"); + pcsUnmarshal->EmitNOP("// } argument"); + + pMarshaler->EmitSetupArgument(pcsDispatch); + if (m_paramidx == 0) + { + CorCallingConvention callConv = psl->GetStubTargetCallingConv(); + if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_UNMANAGED_CALLCONV_THISCALL) + { + // Make sure the 'this' argument to thiscall is of native int type; JIT asserts this. + pcsDispatch->EmitCONV_I(); + } + } +} + +void MarshalInfo::GenerateReturnIL(NDirectStubLinker* psl, + int argOffset, + BOOL fMngToNative, + BOOL fieldGetter, + BOOL retval) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(psl)); + } + CONTRACTL_END; + + MarshalerOverrideStatus amostat; + UINT resID = IDS_EE_BADMARSHAL_RESTRICTION; + + if (m_type == MARSHAL_TYPE_UNKNOWN) + { + amostat = HANDLEASNORMAL; + } + else + { + amostat = (GetReturnOverrideProc(m_type)) (psl, + fMngToNative, + retval, + &m_args, + &resID); + } + + if (amostat == DISALLOWED) + { + EmitOrThrowInteropParamException(psl, fMngToNative, resID, 0); + return; + } + + if (amostat == HANDLEASNORMAL) + { + // Historically we have always allowed reading fields that are marshaled as C arrays. + if (m_type == MARSHAL_TYPE_UNKNOWN || (!fieldGetter && m_type == MARSHAL_TYPE_NATIVEARRAY)) + { + EmitOrThrowInteropParamException(psl, fMngToNative, m_resID, 0); + return; + } + + NewHolder<ILMarshaler> pMarshaler = CreateILMarshaler(m_type, psl); + DWORD dwMarshalFlags = CalculateReturnMarshalFlags(retval, fMngToNative); + + if (!pMarshaler->SupportsReturnMarshal(dwMarshalFlags, &resID)) + { + EmitOrThrowInteropParamException(psl, fMngToNative, resID, 0); + return; + } + + ILCodeStream* pcsMarshal = psl->GetMarshalCodeStream(); + ILCodeStream* pcsUnmarshal = psl->GetReturnUnmarshalCodeStream(); + ILCodeStream* pcsDispatch = psl->GetDispatchCodeStream(); + + pcsMarshal->EmitNOP("// return { "); + pcsUnmarshal->EmitNOP("// return { "); + + UINT16 wNativeSize = GetNativeSize(m_type, m_ms); + + // The following statement behaviour has existed for a long time. By aligning the size of the return + // value up to stack slot size, we prevent EmitMarshalReturnValue from distinguishing between, say, 3-byte + // structure and 4-byte structure. The former is supposed to be returned by-ref using a secret argument + // (at least in MSVC compiled code) while the latter is returned in EAX. We are keeping the behavior for + // now for backward compatibility. + X86_ONLY(wNativeSize = StackElemSize(wNativeSize)); + + pMarshaler->EmitMarshalReturnValue(pcsMarshal, pcsUnmarshal, pcsDispatch, m_paramidx + argOffset, wNativeSize, dwMarshalFlags, &m_args); + + pcsMarshal->EmitNOP("// } return"); + pcsUnmarshal->EmitNOP("// } return"); + + return; + } +} + +void MarshalInfo::SetupArgumentSizes() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (m_byref) + { + m_managedArgSize = StackElemSize(sizeof(void*)); + m_nativeArgSize = StackElemSize(sizeof(void*)); + } + else + { + m_managedArgSize = StackElemSize(GetManagedSize(m_type, m_ms)); + m_nativeArgSize = StackElemSize(GetNativeSize(m_type, m_ms)); + } + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (m_managedArgSize > ENREGISTERED_PARAMTYPE_MAXSIZE) + m_managedArgSize = StackElemSize(sizeof(void*)); + + if (m_nativeArgSize > ENREGISTERED_PARAMTYPE_MAXSIZE) + m_nativeArgSize = StackElemSize(sizeof(void*)); +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE +} + +UINT16 MarshalInfo::GetManagedSize(MarshalType mtype, MarshalScenario ms) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + static const BYTE managedSizes[]= + { + #define DEFINE_MARSHALER_TYPE(mt, mclass, fWinRTSupported) IL##mclass::c_CLRSize, + #include "mtypes.h" + }; + + _ASSERTE((SIZE_T)mtype < COUNTOF(managedSizes)); + BYTE managedSize = managedSizes[mtype]; + + if (managedSize == VARIABLESIZE) + { + switch (mtype) + { + + case MARSHAL_TYPE_BLITTABLEVALUECLASS: + case MARSHAL_TYPE_VALUECLASS: +#ifdef FEATURE_COMINTEROP + case MARSHAL_TYPE_DATETIME: + case MARSHAL_TYPE_NULLABLE: + case MARSHAL_TYPE_KEYVALUEPAIR: +#endif // FEATURE_COMINTEROP + return (UINT16) m_pMT->GetAlignedNumInstanceFieldBytes(); + break; + + default: + _ASSERTE(0); + } + } + + return managedSize; +} + +UINT16 MarshalInfo::GetNativeSize(MarshalType mtype, MarshalScenario ms) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + static const BYTE nativeSizes[]= + { + #define DEFINE_MARSHALER_TYPE(mt, mclass, fWinRTSupported) IL##mclass::c_nativeSize, + #include "mtypes.h" + }; + + _ASSERTE((SIZE_T)mtype < COUNTOF(nativeSizes)); + BYTE nativeSize = nativeSizes[mtype]; + + if (nativeSize == VARIABLESIZE) + { + switch (mtype) + { + case MARSHAL_TYPE_BLITTABLEVALUECLASS: + case MARSHAL_TYPE_VALUECLASS: +#ifndef FEATURE_CORECLR + case MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR: +#endif // !FEATURE_CORECLR + return (UINT16) m_pMT->GetNativeSize(); + + default: + _ASSERTE(0); + } + } + + return nativeSize; +} + +bool MarshalInfo::IsInOnly(MarshalType mtype) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + static const bool ILMarshalerIsInOnly[] = + { + #define DEFINE_MARSHALER_TYPE(mt, mclass, fWinRTSupported) \ + (IL##mclass::c_fInOnly ? true : false), + + #include "mtypes.h" + }; + + return ILMarshalerIsInOnly[mtype]; +} + +bool MarshalInfo::IsSupportedForWinRT(MarshalType mtype) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + static const bool MarshalerSupportsWinRT[] = + { + #define DEFINE_MARSHALER_TYPE(mt, mclass, fWinRTSupported) \ + fWinRTSupported, + + #include "mtypes.h" + }; + + return MarshalerSupportsWinRT[mtype]; +} + +OVERRIDEPROC MarshalInfo::GetArgumentOverrideProc(MarshalType mtype) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + static const OVERRIDEPROC ILArgumentOverrideProcs[] = + { + #define DEFINE_MARSHALER_TYPE(mt, mclass, fWinRTSupported) IL##mclass::ArgumentOverride, + #include "mtypes.h" + }; + + _ASSERTE((SIZE_T)mtype < COUNTOF(ILArgumentOverrideProcs)); + return ILArgumentOverrideProcs[mtype]; +} + +RETURNOVERRIDEPROC MarshalInfo::GetReturnOverrideProc(MarshalType mtype) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + static const RETURNOVERRIDEPROC ILReturnOverrideProcs[] = + { + #define DEFINE_MARSHALER_TYPE(mt, mclass, fWinRTSupported) IL##mclass::ReturnOverride, + #include "mtypes.h" + }; + + _ASSERTE((SIZE_T)mtype < COUNTOF(ILReturnOverrideProcs)); + return ILReturnOverrideProcs[mtype]; +} + +void MarshalInfo::GetItfMarshalInfo(ItfMarshalInfo* pInfo) +{ + STANDARD_VM_CONTRACT; + + GetItfMarshalInfo(TypeHandle(m_pMT), +#ifdef FEATURE_COMINTEROP + TypeHandle(m_pDefaultItfMT), +#else // FEATURE_COMINTEROP + TypeHandle(), +#endif // FEATURE_COMINTEROP + m_fDispItf, + m_fInspItf, + m_ms, + pInfo); +} + +void MarshalInfo::GetItfMarshalInfo(TypeHandle th, TypeHandle thItf, BOOL fDispItf, BOOL fInspItf, MarshalScenario ms, ItfMarshalInfo *pInfo) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pInfo)); + PRECONDITION(!th.IsNull()); + PRECONDITION(!th.IsTypeDesc()); + } + CONTRACTL_END; + +#ifdef FEATURE_COMINTEROP + + // Initialize the output parameter. + pInfo->dwFlags = 0; + pInfo->thItf = TypeHandle(); + pInfo->thClass = TypeHandle(); + + if (!th.IsInterface()) + { + // If the parameter is not System.Object. + if (!th.IsObjectType()) + { + // Set the class method table. + pInfo->thClass = th; + + if (th.IsTypeDesc() || !th.AsMethodTable()->IsWinRTDelegate()) + { + // If this is not a WinRT delegate, retrieve the default interface method table. + TypeHandle hndDefItfClass; + DefaultInterfaceType DefItfType; + + if (!thItf.IsNull()) + { + hndDefItfClass = thItf; + DefItfType = DefaultInterfaceType_Explicit; + } + else if (th.IsProjectedFromWinRT() || th.IsExportedToWinRT()) + { + // WinRT classes use their WinRT default interface + hndDefItfClass = th.GetMethodTable()->GetDefaultWinRTInterface(); + DefItfType = DefaultInterfaceType_Explicit; + } + else + { + DefItfType = GetDefaultInterfaceForClassWrapper(th, &hndDefItfClass); + } + switch (DefItfType) + { + case DefaultInterfaceType_Explicit: + { + pInfo->thItf = hndDefItfClass; + switch (hndDefItfClass.GetComInterfaceType()) + { + case ifDispatch: + case ifDual: + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_DISP_ITF; + break; + + case ifInspectable: + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_INSP_ITF; + break; + } + break; + } + + case DefaultInterfaceType_AutoDual: + { + pInfo->thItf = hndDefItfClass; + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_DISP_ITF; + break; + } + + case DefaultInterfaceType_IUnknown: + case DefaultInterfaceType_BaseComClass: + { + break; + } + + case DefaultInterfaceType_AutoDispatch: + { + pInfo->thItf = hndDefItfClass; + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_DISP_ITF; + break; + } + + default: + { + _ASSERTE(!"Invalid default interface type!"); + break; + } + } + } + } + else + { + // The type will be marshalled as an IUnknown, IInspectable, or IDispatch pointer depending + // on the value of fDispItf and fInspItf + if (fDispItf) + { + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_DISP_ITF; + } + else if (fInspItf) + { + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_INSP_ITF; + } + + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_USE_BASIC_ITF; + } + } + else if (fInspItf) + { + // IInspectable-based interfaces are simple + pInfo->thItf = th; + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_INSP_ITF; + } + else + { + // Determine the interface this type will be marshalled as. + if (th.IsComClassInterface()) + pInfo->thItf = th.GetDefItfForComClassItf(); + else + pInfo->thItf = th; + + // Determine if we are dealing with an IDispatch, IInspectable, or IUnknown based interface. + switch (pInfo->thItf.GetComInterfaceType()) + { + case ifDispatch: + case ifDual: + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_DISP_ITF; + break; + + case ifInspectable: + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_INSP_ITF; + break; + } + + // Look to see if the interface has a coclass defined + pInfo->thClass = th.GetCoClassForInterface(); + if (!pInfo->thClass.IsNull()) + { + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_CLASS_IS_HINT; + } + } + + // store the pre-redirection interface type as thNativeItf + pInfo->thNativeItf = pInfo->thItf; + + if (ms == MARSHAL_SCENARIO_WINRT) + { + // Use the "class is hint" flag so GetObjectRefFromComIP doesn't verify that the + // WinRT object really supports IInspectable - note that we'll do the verification + // in UnmarshalObjectFromInterface for this exact pInfo->thItf. + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_CLASS_IS_HINT; + + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_WINRT_SCENARIO; + + // Perform interface redirection statically here. When the resulting ItfMarshalInfo + // is used for CLR->WinRT marshaling, this is necessary so we know which COM vtable + // to pass out (for instance IList could be marshaled out as IList or IBindableVector + // depending on the marshal scenario). In the WinRT->CLR direction, it's just an + // optimization which saves us from performing redirection at run-time. + + if (!pInfo->thItf.IsNull()) + { + MethodTable *pNewItfMT1; + MethodTable *pNewItfMT2; + switch (RCW::GetInterfacesForQI(pInfo->thItf.GetMethodTable(), &pNewItfMT1, &pNewItfMT2)) + { + case RCW::InterfaceRedirection_None: + case RCW::InterfaceRedirection_UnresolvedIEnumerable: + break; + + case RCW::InterfaceRedirection_IEnumerable_RetryOnFailure: + case RCW::InterfaceRedirection_IEnumerable: + case RCW::InterfaceRedirection_Other: + pInfo->thNativeItf = pNewItfMT1; + break; + + case RCW::InterfaceRedirection_Other_RetryOnFailure: + pInfo->thNativeItf = pNewItfMT2; + break; + } + } + + if (!pInfo->thNativeItf.IsNull()) + { + // The native interface is redirected WinRT interface - need to change the flags + _ASSERTE(pInfo->thNativeItf.AsMethodTable()->IsProjectedFromWinRT()); + + pInfo->dwFlags &= ~ItfMarshalInfo::ITF_MARSHAL_DISP_ITF; + pInfo->dwFlags |= ItfMarshalInfo::ITF_MARSHAL_INSP_ITF; + } + } + +#else // FEATURE_COMINTEROP + if (!th.IsInterface()) + pInfo->thClass = th; + else + pInfo->thItf = th; +#endif // FEATURE_COMINTEROP +} + +HRESULT MarshalInfo::TryGetItfMarshalInfo(TypeHandle th, BOOL fDispItf, BOOL fInspItf, ItfMarshalInfo *pInfo) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(!th.IsNull()); + PRECONDITION(CheckPointer(pInfo)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + EX_TRY + { + GetItfMarshalInfo(th, TypeHandle(), fDispItf, fInspItf, +#ifdef FEATURE_COMINTEROP + MARSHAL_SCENARIO_COMINTEROP, +#else // FEATURE_COMINTEROP + MARSHAL_SCENARIO_NDIRECT, +#endif // FEATURE_COMINTEROP + pInfo); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(RethrowTerminalExceptions); + + return hr; +} + +#ifdef _DEBUG +VOID MarshalInfo::DumpMarshalInfo(Module* pModule, SigPointer sig, const SigTypeContext *pTypeContext, mdToken token, + MarshalScenario ms, CorNativeLinkType nlType, CorNativeLinkFlags nlFlags) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + if (LoggingOn(LF_MARSHALER, LL_INFO10)) + { + SString logbuf; + StackScratchBuffer scratch; + + IMDInternalImport *pInternalImport = pModule->GetMDImport(); + + logbuf.AppendASCII("------------------------------------------------------------\n"); + LOG((LF_MARSHALER, LL_INFO10, logbuf.GetANSI(scratch))); + logbuf.Clear(); + + logbuf.AppendASCII("Managed type: "); + if (m_byref) + logbuf.AppendASCII("Byref "); + + TypeHandle th = sig.GetTypeHandleNT(pModule, pTypeContext); + if (th.IsNull()) + logbuf.AppendASCII("<error>"); + else + { + SigFormat sigfmt; + sigfmt.AddType(th); + logbuf.AppendUTF8(sigfmt.GetCString()); + } + + logbuf.AppendASCII("\n"); + LOG((LF_MARSHALER, LL_INFO10, logbuf.GetANSI(scratch))); + logbuf.Clear(); + + logbuf.AppendASCII("NativeType : "); + PCCOR_SIGNATURE pvNativeType; + ULONG cbNativeType; + if (token == mdParamDefNil + || pInternalImport->GetFieldMarshal(token, + &pvNativeType, + &cbNativeType) != S_OK) + { + logbuf.AppendASCII("<absent>"); + } + else + { + + while (cbNativeType--) + { + char num[100]; + sprintf_s(num, COUNTOF(num), "0x%lx ", (ULONG)*pvNativeType); + logbuf.AppendASCII(num); + switch (*(pvNativeType++)) + { +#define XXXXX(nt) case nt: logbuf.AppendASCII("(" #nt ")"); break; + + XXXXX(NATIVE_TYPE_BOOLEAN) + XXXXX(NATIVE_TYPE_I1) + + XXXXX(NATIVE_TYPE_U1) + XXXXX(NATIVE_TYPE_I2) + XXXXX(NATIVE_TYPE_U2) + XXXXX(NATIVE_TYPE_I4) + + XXXXX(NATIVE_TYPE_U4) + XXXXX(NATIVE_TYPE_I8) + XXXXX(NATIVE_TYPE_U8) + XXXXX(NATIVE_TYPE_R4) + + XXXXX(NATIVE_TYPE_R8) + + XXXXX(NATIVE_TYPE_LPSTR) + XXXXX(NATIVE_TYPE_LPWSTR) + XXXXX(NATIVE_TYPE_LPTSTR) + XXXXX(NATIVE_TYPE_FIXEDSYSSTRING) + + XXXXX(NATIVE_TYPE_STRUCT) + + XXXXX(NATIVE_TYPE_INT) + XXXXX(NATIVE_TYPE_FIXEDARRAY) + + XXXXX(NATIVE_TYPE_UINT) + + XXXXX(NATIVE_TYPE_FUNC) + + XXXXX(NATIVE_TYPE_ASANY) + + XXXXX(NATIVE_TYPE_ARRAY) + XXXXX(NATIVE_TYPE_LPSTRUCT) + + XXXXX(NATIVE_TYPE_IUNKNOWN) + +#ifdef FEATURE_COMINTEROP + XXXXX(NATIVE_TYPE_BSTR) + XXXXX(NATIVE_TYPE_TBSTR) + XXXXX(NATIVE_TYPE_ANSIBSTR) + XXXXX(NATIVE_TYPE_HSTRING) + XXXXX(NATIVE_TYPE_BYVALSTR) + + XXXXX(NATIVE_TYPE_VARIANTBOOL) + XXXXX(NATIVE_TYPE_SAFEARRAY) + + XXXXX(NATIVE_TYPE_IDISPATCH) + XXXXX(NATIVE_TYPE_INTF) +#endif // FEATURE_COMINTEROP + +#undef XXXXX + + + case NATIVE_TYPE_CUSTOMMARSHALER: + { + int strLen = 0; + logbuf.AppendASCII("(NATIVE_TYPE_CUSTOMMARSHALER)"); + + // Skip the typelib guid. + logbuf.AppendASCII(" "); + + strLen = CPackedLen::GetLength(pvNativeType, (void const **)&pvNativeType); + if (strLen) + { + BYTE* p = (BYTE*)logbuf.OpenANSIBuffer(strLen); + memcpyNoGCRefs(p, pvNativeType, strLen); + logbuf.CloseBuffer(); + logbuf.AppendASCII("\0"); + + pvNativeType += strLen; + cbNativeType -= strLen + 1; + + // Skip the name of the native type. + logbuf.AppendASCII(" "); + } + + + strLen = CPackedLen::GetLength(pvNativeType, (void const **)&pvNativeType); + if (strLen) + { + BYTE* p = (BYTE*)logbuf.OpenANSIBuffer(strLen); + memcpyNoGCRefs(p, pvNativeType, strLen); + logbuf.CloseBuffer(); + logbuf.AppendASCII("\0"); + + pvNativeType += strLen; + cbNativeType -= strLen + 1; + + // Extract the name of the custom marshaler. + logbuf.AppendASCII(" "); + } + + + strLen = CPackedLen::GetLength(pvNativeType, (void const **)&pvNativeType); + if (strLen) + { + BYTE* p = (BYTE*)logbuf.OpenANSIBuffer(strLen); + memcpyNoGCRefs(p, pvNativeType, strLen); + logbuf.CloseBuffer(); + logbuf.AppendASCII("\0"); + + pvNativeType += strLen; + cbNativeType -= strLen + 1; + + // Extract the cookie string. + logbuf.AppendASCII(" "); + } + + strLen = CPackedLen::GetLength(pvNativeType, (void const **)&pvNativeType); + if (strLen) + { + BYTE* p = (BYTE*)logbuf.OpenANSIBuffer(strLen); + memcpyNoGCRefs(p, pvNativeType, strLen); + logbuf.CloseBuffer(); + logbuf.AppendASCII("\0"); + + pvNativeType += strLen; + cbNativeType -= strLen + 1; + } + + break; + } + + default: + logbuf.AppendASCII("(?)"); + } + + logbuf.AppendASCII(" "); + } + } + logbuf.AppendASCII("\n"); + LOG((LF_MARSHALER, LL_INFO10, logbuf.GetANSI(scratch))); + logbuf.Clear(); + + logbuf.AppendASCII("MarshalType : "); + { + char num[100]; + sprintf_s(num, COUNTOF(num), "0x%lx ", (ULONG)m_type); + logbuf.AppendASCII(num); + } + switch (m_type) + { + #define DEFINE_MARSHALER_TYPE(mt, mc, fWinRTSupported) case mt: logbuf.AppendASCII( #mt " (IL" #mc ")"); break; + #include "mtypes.h" + + case MARSHAL_TYPE_UNKNOWN: + logbuf.AppendASCII("MARSHAL_TYPE_UNKNOWN (illegal combination)"); + break; + + default: + logbuf.AppendASCII("MARSHAL_TYPE_???"); + break; + } + + logbuf.AppendASCII("\n"); + + + logbuf.AppendASCII("Metadata In/Out : "); + if (TypeFromToken(token) != mdtParamDef || token == mdParamDefNil) + logbuf.AppendASCII("<absent>"); + + else + { + DWORD dwAttr = 0; + USHORT usSequence; + LPCSTR szParamName_Ignore; + if (FAILED(pInternalImport->GetParamDefProps(token, &usSequence, &dwAttr, &szParamName_Ignore))) + { + logbuf.AppendASCII("Invalid ParamDef record "); + } + else + { + if (IsPdIn(dwAttr)) + logbuf.AppendASCII("In "); + + if (IsPdOut(dwAttr)) + logbuf.AppendASCII("Out "); + } + } + + logbuf.AppendASCII("\n"); + + logbuf.AppendASCII("Effective In/Out : "); + if (m_in) + logbuf.AppendASCII("In "); + + if (m_out) + logbuf.AppendASCII("Out "); + + logbuf.AppendASCII("\n"); + + LOG((LF_MARSHALER, LL_INFO10, logbuf.GetANSI(scratch))); + logbuf.Clear(); + } +} // MarshalInfo::DumpMarshalInfo +#endif //_DEBUG + +#ifndef CROSSGEN_COMPILE +#ifdef FEATURE_COMINTEROP +DispParamMarshaler *MarshalInfo::GenerateDispParamMarshaler() +{ + CONTRACT (DispParamMarshaler*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + NewHolder<DispParamMarshaler> pDispParamMarshaler = NULL; + + switch (m_type) + { + case MARSHAL_TYPE_OLECOLOR: + pDispParamMarshaler = new DispParamOleColorMarshaler(); + break; + + case MARSHAL_TYPE_CURRENCY: + pDispParamMarshaler = new DispParamCurrencyMarshaler(); + break; + + case MARSHAL_TYPE_GENERIC_4: + if (m_fErrorNativeType) + pDispParamMarshaler = new DispParamErrorMarshaler(); + break; + + case MARSHAL_TYPE_INTERFACE: + { + ItfMarshalInfo itfInfo; + GetItfMarshalInfo(TypeHandle(m_pMT), TypeHandle(m_pDefaultItfMT), m_fDispItf, m_fInspItf, m_ms, &itfInfo); + pDispParamMarshaler = new DispParamInterfaceMarshaler( + itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_DISP_ITF, + itfInfo.thItf.GetMethodTable(), + itfInfo.thClass.GetMethodTable(), + itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_CLASS_IS_HINT); + break; + } + + case MARSHAL_TYPE_VALUECLASS: + case MARSHAL_TYPE_BLITTABLEVALUECLASS: + case MARSHAL_TYPE_BLITTABLEPTR: + case MARSHAL_TYPE_LAYOUTCLASSPTR: +#ifndef FEATURE_CORECLR + case MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR: +#endif + pDispParamMarshaler = new DispParamRecordMarshaler(m_pMT); + break; + +#ifdef FEATURE_CLASSIC_COMINTEROP + case MARSHAL_TYPE_SAFEARRAY: + pDispParamMarshaler = new DispParamArrayMarshaler(m_arrayElementType, m_hndArrayElemType.GetMethodTable()); + break; +#endif + + case MARSHAL_TYPE_DELEGATE: + pDispParamMarshaler = new DispParamDelegateMarshaler(m_pMT); + break; + + case MARSHAL_TYPE_REFERENCECUSTOMMARSHALER: + pDispParamMarshaler = new DispParamCustomMarshaler(m_pCMHelper, m_CMVt); + break; + } + + pDispParamMarshaler.SuppressRelease(); + RETURN pDispParamMarshaler; +} + + +DispatchWrapperType MarshalInfo::GetDispWrapperType() +{ + STANDARD_VM_CONTRACT; + + DispatchWrapperType WrapperType = (DispatchWrapperType)0; + + switch (m_type) + { + case MARSHAL_TYPE_CURRENCY: + WrapperType = DispatchWrapperType_Currency; + break; + + case MARSHAL_TYPE_BSTR: + WrapperType = DispatchWrapperType_BStr; + break; + + case MARSHAL_TYPE_GENERIC_4: + if (m_fErrorNativeType) + WrapperType = DispatchWrapperType_Error; + break; + + case MARSHAL_TYPE_INTERFACE: + { + ItfMarshalInfo itfInfo; + GetItfMarshalInfo(TypeHandle(m_pMT), TypeHandle(m_pDefaultItfMT), m_fDispItf, m_fInspItf, m_ms, &itfInfo); + WrapperType = !!(itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_DISP_ITF) ? DispatchWrapperType_Dispatch : DispatchWrapperType_Unknown; + break; + } + + case MARSHAL_TYPE_SAFEARRAY: + switch (m_arrayElementType) + { + case VT_CY: + WrapperType = (DispatchWrapperType)(DispatchWrapperType_SafeArray | DispatchWrapperType_Currency); + break; + case VT_UNKNOWN: + WrapperType = (DispatchWrapperType)(DispatchWrapperType_SafeArray | DispatchWrapperType_Unknown); + break; + case VT_DISPATCH: + WrapperType = (DispatchWrapperType)(DispatchWrapperType_SafeArray | DispatchWrapperType_Dispatch); + break; + case VT_ERROR: + WrapperType = (DispatchWrapperType)(DispatchWrapperType_SafeArray | DispatchWrapperType_Error); + break; + case VT_BSTR: + WrapperType = (DispatchWrapperType)(DispatchWrapperType_SafeArray | DispatchWrapperType_BStr); + break; + } + break; + } + + return WrapperType; +} + +#endif // FEATURE_COMINTEROP + + +VOID MarshalInfo::MarshalTypeToString(SString& strMarshalType, BOOL fSizeIsSpecified) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + LPCWSTR strRetVal; + + if (m_type == MARSHAL_TYPE_NATIVEARRAY) + { + SString strVarType; + VarTypeToString(m_arrayElementType, strVarType); + + if (!fSizeIsSpecified) + { + strMarshalType.Printf(W("native array of %s (size not specified by a parameter)"), + strVarType.GetUnicode()); + } + else + { + strMarshalType.Printf(W("native array of %s (size specified by parameter %i)"), + strVarType.GetUnicode(), m_countParamIdx); + } + + return; + } +#ifdef FEATURE_COMINTEROP + // Some MarshalTypes have extra information and require special handling + else if (m_type == MARSHAL_TYPE_INTERFACE) + { + ItfMarshalInfo itfInfo; + GetItfMarshalInfo(TypeHandle(m_pMT), TypeHandle(m_pDefaultItfMT), m_fDispItf, m_fInspItf, m_ms, &itfInfo); + + if (!itfInfo.thItf.IsNull()) + { + StackSString ssClassName; + itfInfo.thItf.GetMethodTable()->_GetFullyQualifiedNameForClass(ssClassName); + + if (!!(itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_DISP_ITF)) + { + strMarshalType.SetLiteral(W("IDispatch ")); + } + else if (!!(itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_INSP_ITF)) + { + strMarshalType.SetLiteral(W("IInspectable")); + } + else + { + strMarshalType.SetLiteral(W("IUnknown ")); + } + + if (itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_USE_BASIC_ITF) + { + strMarshalType.Append(W("(basic) ")); + } + + strMarshalType.Append(ssClassName); + return; + } + else + { + if (!!(itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_DISP_ITF)) + strRetVal = W("IDispatch"); + else if (!!(itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_INSP_ITF)) + strRetVal = W("IInspectable"); + else + strRetVal = W("IUnknown"); + } + } + else if (m_type == MARSHAL_TYPE_SAFEARRAY) + { + StackSString strVarType; + VarTypeToString(m_arrayElementType, strVarType); + + strMarshalType = SL(W("SafeArray of ")); + strMarshalType.Append(strVarType); + + return; + } +#endif // FEATURE_COMINTEROP + else if (m_type == MARSHAL_TYPE_REFERENCECUSTOMMARSHALER) + { + GCX_COOP(); + + OBJECTHANDLE objHandle = m_pCMHelper->GetCustomMarshalerInfo()->GetCustomMarshaler(); + { + OBJECTREF pObjRef = ObjectFromHandle(objHandle); + DefineFullyQualifiedNameForClassW(); + + strMarshalType.Printf(W("custom marshaler (%s)"), + GetFullyQualifiedNameForClassW(pObjRef->GetMethodTable())); + } + + return; + } + else + { + // All other MarshalTypes with no special handling + switch (m_type) + { + case MARSHAL_TYPE_GENERIC_1: + strRetVal = W("BYTE"); + break; + case MARSHAL_TYPE_GENERIC_U1: + strRetVal = W("unsigned BYTE"); + break; + case MARSHAL_TYPE_GENERIC_2: + strRetVal = W("WORD"); + break; + case MARSHAL_TYPE_GENERIC_U2: + strRetVal = W("unsigned WORD"); + break; + case MARSHAL_TYPE_GENERIC_4: + strRetVal = W("DWORD"); + break; + case MARSHAL_TYPE_GENERIC_8: + strRetVal = W("QUADWORD"); + break; + case MARSHAL_TYPE_WINBOOL: + strRetVal = W("Windows Bool"); + break; +#ifdef FEATURE_COMINTEROP + case MARSHAL_TYPE_VTBOOL: + strRetVal = W("VARIANT Bool"); + break; +#endif // FEATURE_COMINTEROP + case MARSHAL_TYPE_ANSICHAR: + strRetVal = W("Ansi character"); + break; + case MARSHAL_TYPE_CBOOL: + strRetVal = W("CBool"); + break; + case MARSHAL_TYPE_FLOAT: + strRetVal = W("float"); + break; + case MARSHAL_TYPE_DOUBLE: + strRetVal = W("double"); + break; + case MARSHAL_TYPE_CURRENCY: + strRetVal = W("CURRENCY"); + break; + case MARSHAL_TYPE_DECIMAL: + strRetVal = W("DECIMAL"); + break; + case MARSHAL_TYPE_DECIMAL_PTR: + strRetVal = W("DECIMAL pointer"); + break; + case MARSHAL_TYPE_GUID: + strRetVal = W("GUID"); + break; + case MARSHAL_TYPE_GUID_PTR: + strRetVal = W("GUID pointer"); + break; + case MARSHAL_TYPE_DATE: + strRetVal = W("DATE"); + break; +#ifdef FEATURE_COMINTEROP + case MARSHAL_TYPE_BSTR: + strRetVal = W("BSTR"); + break; +#endif // FEATURE_COMINTEROP + case MARSHAL_TYPE_LPWSTR: + strRetVal = W("LPWSTR"); + break; + case MARSHAL_TYPE_LPSTR: + strRetVal = W("LPSTR"); + break; + case MARSHAL_TYPE_LPUTF8STR: + strRetVal = W("LPUTF8STR"); + break; +#ifdef FEATURE_COMINTEROP + case MARSHAL_TYPE_ANSIBSTR: + strRetVal = W("AnsiBStr"); + break; +#endif // FEATURE_COMINTEROP + case MARSHAL_TYPE_LPWSTR_BUFFER: + strRetVal = W("LPWSTR buffer"); + break; + case MARSHAL_TYPE_LPSTR_BUFFER: + strRetVal = W("LPSTR buffer"); + break; + case MARSHAL_TYPE_UTF8_BUFFER: + strRetVal = W("UTF8 buffer"); + break; + case MARSHAL_TYPE_ASANYA: + strRetVal = W("AsAnyA"); + break; + case MARSHAL_TYPE_ASANYW: + strRetVal = W("AsAnyW"); + break; + case MARSHAL_TYPE_DELEGATE: + strRetVal = W("Delegate"); + break; + case MARSHAL_TYPE_BLITTABLEPTR: + strRetVal = W("blittable pointer"); + break; +#ifdef FEATURE_COMINTEROP + case MARSHAL_TYPE_VBBYVALSTR: + strRetVal = W("VBByValStr"); + break; + case MARSHAL_TYPE_VBBYVALSTRW: + strRetVal = W("VBByRefStr"); + break; +#endif // FEATURE_COMINTEROP + case MARSHAL_TYPE_LAYOUTCLASSPTR: + strRetVal = W("Layout class pointer"); + break; + case MARSHAL_TYPE_ARRAYWITHOFFSET: + strRetVal = W("ArrayWithOffset"); + break; + case MARSHAL_TYPE_BLITTABLEVALUECLASS: + strRetVal = W("blittable value class"); + break; + case MARSHAL_TYPE_VALUECLASS: + strRetVal = W("value class"); + break; + case MARSHAL_TYPE_ARGITERATOR: + strRetVal = W("ArgIterator"); + break; +#ifndef FEATURE_CORECLR + case MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR: + strRetVal = W("blittable value class with copy constructor"); + break; +#endif // FEATURE_CORECLR +#ifdef FEATURE_COMINTEROP + case MARSHAL_TYPE_OBJECT: + strRetVal = W("VARIANT"); + break; +#endif // FEATURE_COMINTEROP + case MARSHAL_TYPE_HANDLEREF: + strRetVal = W("HandleRef"); + break; +#ifdef FEATURE_COMINTEROP + case MARSHAL_TYPE_OLECOLOR: + strRetVal = W("OLE_COLOR"); + break; +#endif // FEATURE_COMINTEROP + case MARSHAL_TYPE_RUNTIMETYPEHANDLE: + strRetVal = W("RuntimeTypeHandle"); + break; + case MARSHAL_TYPE_RUNTIMEFIELDHANDLE: + strRetVal = W("RuntimeFieldHandle"); + break; + case MARSHAL_TYPE_RUNTIMEMETHODHANDLE: + strRetVal = W("RuntimeMethodHandle"); + break; + case MARSHAL_TYPE_RUNTIMEMETHODINFO: + strRetVal = W("RuntimeMethodInfo"); + break; + case MARSHAL_TYPE_RUNTIMEMODULE: + strRetVal = W("RuntimeModule"); + break; + case MARSHAL_TYPE_RUNTIMEASSEMBLY: + strRetVal = W("RuntimeAssembly"); + break; + default: + strRetVal = W("<UNKNOWN>"); + break; + } + } + + strMarshalType.Set(strRetVal); + return; +} + +VOID MarshalInfo::VarTypeToString(VARTYPE vt, SString& strVarType) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + + LPCWSTR strRetVal; + + switch(vt) + { + case VT_I2: + strRetVal = W("2-byte signed int"); + break; + case VT_I4: + strRetVal = W("4-byte signed int"); + break; + case VT_R4: + strRetVal = W("4-byte real"); + break; + case VT_R8: + strRetVal = W("8-byte real"); + break; + case VT_CY: + strRetVal = W("currency"); + break; + case VT_DATE: + strRetVal = W("date"); + break; + case VT_BSTR: + strRetVal = W("binary string"); + break; + case VT_DISPATCH: + strRetVal = W("IDispatch *"); + break; + case VT_ERROR: + strRetVal = W("Scode"); + break; + case VT_BOOL: + strRetVal = W("boolean"); + break; + case VT_VARIANT: + strRetVal = W("VARIANT *"); + break; + case VT_UNKNOWN: + strRetVal = W("IUnknown *"); + break; + case VT_DECIMAL: + strRetVal = W("16-byte fixed point"); + break; + case VT_RECORD: + strRetVal = W("user defined structure"); + break; + case VT_I1: + strRetVal = W("signed char"); + break; + case VT_UI1: + strRetVal = W("unsigned char"); + break; + case VT_UI2: + strRetVal = W("unsigned short"); + break; + case VT_UI4: + strRetVal = W("unsigned short"); + break; + case VT_INT: + strRetVal = W("signed int"); + break; + case VT_UINT: + strRetVal = W("unsigned int"); + break; + case VT_LPSTR: + strRetVal = W("LPSTR"); + break; + case VT_LPWSTR: + strRetVal = W("LPWSTR"); + break; + case VT_HRESULT: + strRetVal = W("HResult"); + break; + case VT_I8: + strRetVal = W("8-byte signed int"); + break; + case VT_NULL: + strRetVal = W("null"); + break; + case VT_UI8: + strRetVal = W("8-byte unsigned int"); + break; + case VT_VOID: + strRetVal = W("void"); + break; + case VTHACK_WINBOOL: + strRetVal = W("boolean"); + break; + case VTHACK_ANSICHAR: + strRetVal = W("char"); + break; + case VTHACK_CBOOL: + strRetVal = W("1-byte C bool"); + break; + default: + strRetVal = W("unknown"); + break; + } + + strVarType.Set(strRetVal); + return; +} + +#endif // CROSSGEN_COMPILE + +// Returns true if the marshaler represented by this instance requires COM to have been started. +bool MarshalInfo::MarshalerRequiresCOM() +{ + LIMITED_METHOD_CONTRACT; + +#ifdef FEATURE_COMINTEROP + switch (m_type) + { + case MARSHAL_TYPE_REFERENCECUSTOMMARSHALER: + + case MARSHAL_TYPE_BSTR: + case MARSHAL_TYPE_ANSIBSTR: + case MARSHAL_TYPE_OBJECT: + case MARSHAL_TYPE_OLECOLOR: + case MARSHAL_TYPE_SAFEARRAY: + case MARSHAL_TYPE_INTERFACE: + + case MARSHAL_TYPE_URI: + case MARSHAL_TYPE_KEYVALUEPAIR: + case MARSHAL_TYPE_NULLABLE: + case MARSHAL_TYPE_SYSTEMTYPE: + case MARSHAL_TYPE_EXCEPTION: + case MARSHAL_TYPE_HIDDENLENGTHARRAY: + case MARSHAL_TYPE_HSTRING: + case MARSHAL_TYPE_NCCEVENTARGS: + case MARSHAL_TYPE_PCEVENTARGS: + { + // some of these types do not strictly require COM for the actual marshaling + // but they tend to be used in COM context so we keep the logic we had in + // previous versions and return true here + return true; + } + + case MARSHAL_TYPE_LAYOUTCLASSPTR: + case MARSHAL_TYPE_VALUECLASS: + { + // pessimistic guess, but in line with previous versions + return true; + } + + case MARSHAL_TYPE_NATIVEARRAY: + { + return (m_arrayElementType == VT_UNKNOWN || + m_arrayElementType == VT_DISPATCH || + m_arrayElementType == VT_VARIANT); + } + } +#endif // FEATURE_COMINTEROP + + return false; +} + +#ifdef FEATURE_COMINTEROP +MarshalInfo::MarshalType MarshalInfo::GetHiddenLengthParamMarshalType() +{ + LIMITED_METHOD_CONTRACT; + return MARSHAL_TYPE_GENERIC_U4; +} + +CorElementType MarshalInfo::GetHiddenLengthParamElementType() +{ + LIMITED_METHOD_CONTRACT; + return ELEMENT_TYPE_U4; +} + +UINT16 MarshalInfo::GetHiddenLengthParamStackSize() +{ + LIMITED_METHOD_CONTRACT; + return StackElemSize(GetNativeSize(GetHiddenLengthParamMarshalType(), m_ms)); +} + +void MarshalInfo::MarshalHiddenLengthArgument(NDirectStubLinker *psl, BOOL managedToNative, BOOL isForReturnArray) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(psl)); + PRECONDITION(m_type == MARSHAL_TYPE_HIDDENLENGTHARRAY); + PRECONDITION(m_dwHiddenLengthManagedHomeLocal == 0xFFFFFFFF); + PRECONDITION(m_dwHiddenLengthNativeHomeLocal == 0xFFFFFFFF); + } + CONTRACTL_END; + + NewHolder<ILMarshaler> pHiddenLengthMarshaler = CreateILMarshaler(GetHiddenLengthParamMarshalType(), psl); + + + ILCodeStream *pcsMarshal = psl->GetMarshalCodeStream(); + ILCodeStream *pcsUnmarshal = psl->GetUnmarshalCodeStream(); + + pcsMarshal->EmitNOP("// hidden length argument { "); + pcsUnmarshal->EmitNOP("// hidden length argument { "); + + DWORD dwMarshalFlags = MARSHAL_FLAG_HIDDENLENPARAM; + if (isForReturnArray) + { + // This is a hidden length argument for an [out, retval] argument, so setup flags to match that + dwMarshalFlags |= CalculateArgumentMarshalFlags(TRUE, FALSE, TRUE, managedToNative); + } + else + { + // The length parameter needs to be an [in] parameter if the array itself is an [in] parameter. + // Additionally, in order to support the FillArray pattern: + // FillArray([in] UInt32 length, [out, size_is(length)] ElementType* value) + // + // We need to make sure that the length parameter is [in] if the array pointer is not byref, since + // this means that the caller is allocating the array. This includes array buffers which are [out] + // but not byref, since the [out] marshaling applies to the array contents but not the array pointer + // value itself. + BOOL marshalHiddenLengthIn = m_in || !m_byref; + dwMarshalFlags |= CalculateArgumentMarshalFlags(m_byref, marshalHiddenLengthIn, m_out, managedToNative); + } + pHiddenLengthMarshaler->EmitMarshalHiddenLengthArgument(pcsMarshal, + pcsUnmarshal, + this, + m_paramidx, + dwMarshalFlags, + HiddenLengthParamIndex(), + &m_args, + &m_dwHiddenLengthManagedHomeLocal, + &m_dwHiddenLengthNativeHomeLocal); + + pcsMarshal->EmitNOP("// } hidden length argument"); + pcsUnmarshal->EmitNOP("// } hidden length argument"); + + // Only emit into the dispatch stream for CLR -> Native cases - in the reverse, there is no argument + // to pass to the managed method. Instead, the length is encoded in the marshaled array. + if (managedToNative) + { + ILCodeStream* pcsDispatch = psl->GetDispatchCodeStream(); + pHiddenLengthMarshaler->EmitSetupArgument(pcsDispatch); + } +} + +#endif // FEATURE_COMINTEROP + +#define ReportInvalidArrayMarshalInfo(resId) \ + do \ + { \ + m_vtElement = VT_EMPTY; \ + m_errorResourceId = resId; \ + m_thElement = TypeHandle(); \ + goto LExit; \ + } \ + while (0) + +void ArrayMarshalInfo::InitForNativeArray(MarshalInfo::MarshalScenario ms, TypeHandle thElement, CorNativeType ntElement, BOOL isAnsi) +{ + WRAPPER_NO_CONTRACT; + InitElementInfo(NATIVE_TYPE_ARRAY, ms, thElement, ntElement, isAnsi); +} + +void ArrayMarshalInfo::InitForFixedArray(TypeHandle thElement, CorNativeType ntElement, BOOL isAnsi) +{ + WRAPPER_NO_CONTRACT; + InitElementInfo(NATIVE_TYPE_FIXEDARRAY, MarshalInfo::MARSHAL_SCENARIO_FIELD, thElement, ntElement, isAnsi); +} + +#ifdef FEATURE_COMINTEROP +void ArrayMarshalInfo::InitForSafeArray(MarshalInfo::MarshalScenario ms, TypeHandle thElement, VARTYPE vtElement, BOOL isAnsi) +{ + STANDARD_VM_CONTRACT; + + InitElementInfo(NATIVE_TYPE_SAFEARRAY, ms, thElement, NATIVE_TYPE_DEFAULT, isAnsi); + + if (IsValid() && vtElement != VT_EMPTY) + { + if (vtElement == VT_USERDEFINED) + { + // If the user explicitly sets the VARTYPE to VT_USERDEFINED, we simply ignore it + // since the exporter will take care of transforming the vt to VT_USERDEFINED and the + // marshallers needs the actual type. + } + else + { + m_flags = (ArrayMarshalInfoFlags)(m_flags | amiSafeArraySubTypeExplicitlySpecified); + m_vtElement = vtElement; + } + } +} + +void ArrayMarshalInfo::InitForHiddenLengthArray(TypeHandle thElement) +{ + STANDARD_VM_CONTRACT; + + // WinRT supports arrays of any WinRT-legal types + if (thElement.IsArray()) + { + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_NESTEDARRAY); + } + else if (thElement.IsTypeDesc() || !thElement.GetMethodTable()->IsLegalNonArrayWinRTType()) + { + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE); + } + + m_thElement = thElement; + + MethodTable *pMT = thElement.GetMethodTable(); + if (pMT->IsString()) + { + m_vtElement = VTHACK_HSTRING; + m_cbElementSize = sizeof(HSTRING); + } + else if (WinRTTypeNameConverter::ResolveRedirectedType(pMT, &m_redirectedTypeIndex)) + { + m_vtElement = VTHACK_REDIRECTEDTYPE; + + switch (m_redirectedTypeIndex) + { + case WinMDAdapter::RedirectedTypeIndex_System_DateTimeOffset: + m_cbElementSize = ILDateTimeMarshaler::c_nativeSize; + break; + + case WinMDAdapter::RedirectedTypeIndex_System_Type: + m_cbElementSize = ILSystemTypeMarshaler::c_nativeSize; + break; + + case WinMDAdapter::RedirectedTypeIndex_System_Exception: + m_cbElementSize = ILHResultExceptionMarshaler::c_nativeSize; + break; + + // WinRT delegates are IUnknown pointers + case WinMDAdapter::RedirectedTypeIndex_System_EventHandlerGeneric: + m_vtElement = VTHACK_INSPECTABLE; + m_cbElementSize = sizeof(IUnknown*); + break; + + case WinMDAdapter::RedirectedTypeIndex_System_Collections_Generic_KeyValuePair: + case WinMDAdapter::RedirectedTypeIndex_System_Nullable: + case WinMDAdapter::RedirectedTypeIndex_System_Uri: + case WinMDAdapter::RedirectedTypeIndex_System_Collections_Specialized_NotifyCollectionChangedEventArgs: + case WinMDAdapter::RedirectedTypeIndex_System_ComponentModel_PropertyChangedEventArgs: + { + m_cbElementSize = sizeof(IInspectable *); + break; + } + + default: + { + if (pMT->IsValueType()) + { + // other redirected structs are blittable and don't need special marshaling + m_vtElement = VTHACK_BLITTABLERECORD; + m_cbElementSize = pMT->GetNativeSize(); + } + else + { + // redirected interfaces should be treated as interface pointers + _ASSERTE(pMT->IsInterface()); + m_vtElement = VTHACK_INSPECTABLE; + m_cbElementSize = sizeof(IInspectable *); + } + break; + } + } + } + else if (pMT->IsBlittable() || pMT->IsTruePrimitive() || pMT->IsEnum()) + { + m_vtElement = VTHACK_BLITTABLERECORD; + + CorElementType elemType = pMT->GetInternalCorElementType(); + if (CorTypeInfo::IsPrimitiveType(elemType)) + { + // .NET and WinRT primitives have the same size + m_cbElementSize = CorTypeInfo::Size(elemType); + } + else + { + m_cbElementSize = pMT->GetNativeSize(); + } + } + else if (pMT->IsValueType()) + { + m_vtElement = VTHACK_NONBLITTABLERECORD; + m_cbElementSize = pMT->GetNativeSize(); + } + else + { + m_vtElement = VTHACK_INSPECTABLE; + m_cbElementSize = sizeof(IInspectable *); + } + +LExit:; +} +#endif // FEATURE_COMINTEROP + +void ArrayMarshalInfo::InitElementInfo(CorNativeType arrayNativeType, MarshalInfo::MarshalScenario ms, TypeHandle thElement, CorNativeType ntElement, BOOL isAnsi) +{ + CONTRACT_VOID + { + STANDARD_VM_CHECK; + PRECONDITION(!thElement.IsNull()); + POSTCONDITION(!IsValid() || !m_thElement.IsNull()); + } + CONTRACT_END; + + CorElementType etElement = ELEMENT_TYPE_END; + + // + // IMPORTANT: The error resource IDs used in this function must not contain any placeholders! + // + // Also please maintain the standard of using IDS_EE_BADMARSHAL_XXX when defining new error + // message resource IDs. + // + + if (thElement.IsArray()) + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_NESTEDARRAY); + + m_thElement = thElement; + + if (m_thElement.IsPointer()) + { + m_flags = (ArrayMarshalInfoFlags)(m_flags | amiIsPtr); + m_thElement = ((ParamTypeDesc*)m_thElement.AsTypeDesc())->GetModifiedType(); + } + + etElement = m_thElement.GetSignatureCorElementType(); + + if (IsAMIPtr(m_flags) && (etElement > ELEMENT_TYPE_R8)) + { + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_UNSUPPORTED_SIG); + } + + if (etElement == ELEMENT_TYPE_CHAR) + { + switch (ntElement) + { + case NATIVE_TYPE_I1: //fallthru + case NATIVE_TYPE_U1: + m_vtElement = VTHACK_ANSICHAR; + break; + + case NATIVE_TYPE_I2: //fallthru + case NATIVE_TYPE_U2: + m_vtElement = VT_UI2; + break; + + // Compat: If the native type doesn't make sense, we need to ignore it and not report an error. + case NATIVE_TYPE_DEFAULT: //fallthru + default: +#ifdef FEATURE_COMINTEROP + if (ms == MarshalInfo::MARSHAL_SCENARIO_COMINTEROP) + m_vtElement = VT_UI2; + else +#endif // FEATURE_COMINTEROP + m_vtElement = isAnsi ? VTHACK_ANSICHAR : VT_UI2; + } + } + else if (etElement == ELEMENT_TYPE_BOOLEAN) + { + switch (ntElement) + { + case NATIVE_TYPE_BOOLEAN: + m_vtElement = VTHACK_WINBOOL; + break; + +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_VARIANTBOOL: + m_vtElement = VT_BOOL; + break; +#endif // FEATURE_COMINTEROP + + case NATIVE_TYPE_I1 : + case NATIVE_TYPE_U1 : + m_vtElement = VTHACK_CBOOL; + break; + + // Compat: if the native type doesn't make sense, we need to ignore it and not report an error. + case NATIVE_TYPE_DEFAULT: //fallthru + default: +#ifdef FEATURE_COMINTEROP + if (ms == MarshalInfo::MARSHAL_SCENARIO_COMINTEROP || + arrayNativeType == NATIVE_TYPE_SAFEARRAY) + { + m_vtElement = VT_BOOL; + } + else +#endif // FEATURE_COMINTEROP + { + m_vtElement = VTHACK_WINBOOL; + } + break; + } + } + else if (etElement == ELEMENT_TYPE_I) + { + m_vtElement = static_cast<VARTYPE>((GetPointerSize() == 4) ? VT_I4 : VT_I8); + } + else if (etElement == ELEMENT_TYPE_U) + { + m_vtElement = static_cast<VARTYPE>((GetPointerSize() == 4) ? VT_UI4 : VT_UI8); + } + else if (etElement <= ELEMENT_TYPE_R8) + { + static const BYTE map [] = + { + VT_NULL, // ELEMENT_TYPE_END + VT_VOID, // ELEMENT_TYPE_VOID + VT_NULL, // ELEMENT_TYPE_BOOLEAN + VT_NULL, // ELEMENT_TYPE_CHAR + VT_I1, // ELEMENT_TYPE_I1 + VT_UI1, // ELEMENT_TYPE_U1 + VT_I2, // ELEMENT_TYPE_I2 + VT_UI2, // ELEMENT_TYPE_U2 + VT_I4, // ELEMENT_TYPE_I4 + VT_UI4, // ELEMENT_TYPE_U4 + VT_I8, // ELEMENT_TYPE_I8 + VT_UI8, // ELEMENT_TYPE_U8 + VT_R4, // ELEMENT_TYPE_R4 + VT_R8 // ELEMENT_TYPE_R8 + + }; + + _ASSERTE(map[etElement] != VT_NULL); + m_vtElement = map[etElement]; + } + else + { + if (m_thElement == TypeHandle(g_pStringClass)) + { + switch (ntElement) + { + case NATIVE_TYPE_DEFAULT: +#ifdef FEATURE_COMINTEROP + if (arrayNativeType == NATIVE_TYPE_SAFEARRAY || ms == MarshalInfo::MARSHAL_SCENARIO_COMINTEROP) + { + m_vtElement = VT_BSTR; + } + else +#endif // FEATURE_COMINTEROP + { + m_vtElement = static_cast<VARTYPE>(isAnsi ? VT_LPSTR : VT_LPWSTR); + } + break; +#ifdef FEATURE_COMINTEROP + case NATIVE_TYPE_BSTR: + m_vtElement = VT_BSTR; + break; +#endif // FEATURE_COMINTEROP + case NATIVE_TYPE_LPSTR: + m_vtElement = VT_LPSTR; + break; + case NATIVE_TYPE_LPWSTR: + m_vtElement = VT_LPWSTR; + break; + case NATIVE_TYPE_LPTSTR: + { +#ifdef FEATURE_COMINTEROP + if (ms == MarshalInfo::MARSHAL_SCENARIO_COMINTEROP || IsAMIExport(m_flags)) + { + // We disallow NATIVE_TYPE_LPTSTR for COM or if we are exporting. + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHALPARAM_NO_LPTSTR); + } + else +#endif // FEATURE_COMINTEROP + { + // We no longer support Win9x so LPTSTR always maps to a Unicode string. + m_vtElement = VT_LPWSTR; + } + break; + } + + default: + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_STRINGARRAY); + } + } + else if (m_thElement == TypeHandle(g_pObjectClass)) + { +#ifdef FEATURE_COMINTEROP + switch(ntElement) + { + case NATIVE_TYPE_DEFAULT: + if (ms == MarshalInfo::MARSHAL_SCENARIO_FIELD) + m_vtElement = VT_UNKNOWN; + else + m_vtElement = VT_VARIANT; + break; + + case NATIVE_TYPE_STRUCT: + m_vtElement = VT_VARIANT; + break; + + case NATIVE_TYPE_INTF: + case NATIVE_TYPE_IUNKNOWN: + m_vtElement = VT_UNKNOWN; + break; + + case NATIVE_TYPE_IDISPATCH: + m_vtElement = VT_DISPATCH; + break; + + default: + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_OBJECTARRAY); + } + +#else // FEATURE_COMINTEROP + switch (ntElement) + { + case NATIVE_TYPE_IUNKNOWN: + m_vtElement = VT_UNKNOWN; + break; + + default: + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_UNSUPPORTED_SIG); + } +#endif // FEATURE_COMINTEROP + } + else if (m_thElement.CanCastTo(TypeHandle(MscorlibBinder::GetClass(CLASS__SAFE_HANDLE)))) + { + // Array's of SAFEHANDLEs are not supported. + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_SAFEHANDLEARRAY); + } + else if (m_thElement.CanCastTo(TypeHandle(MscorlibBinder::GetClass(CLASS__CRITICAL_HANDLE)))) + { + // Array's of CRITICALHANDLEs are not supported. + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_CRITICALHANDLEARRAY); + } + else if (etElement == ELEMENT_TYPE_VALUETYPE) + { + if (m_thElement == TypeHandle(MscorlibBinder::GetClass(CLASS__DATE_TIME))) + { + if (ntElement == NATIVE_TYPE_STRUCT || ntElement == NATIVE_TYPE_DEFAULT) + m_vtElement = VT_DATE; + else + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_DATETIMEARRAY); + } + else if (m_thElement == TypeHandle(MscorlibBinder::GetClass(CLASS__DECIMAL))) + { + if (ntElement == NATIVE_TYPE_STRUCT || ntElement == NATIVE_TYPE_DEFAULT) + m_vtElement = VT_DECIMAL; +#ifdef FEATURE_COMINTEROP + else if (ntElement == NATIVE_TYPE_CURRENCY) + m_vtElement = VT_CY; +#endif // FEATURE_COMINTEROP + else + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_DECIMALARRAY); + } + else + { + // When exporting, we need to handle enums specially. + if (IsAMIExport(m_flags) && m_thElement.IsEnum()) + { + // Get the element type of the underlying type. + CorElementType et = m_thElement.GetInternalCorElementType(); + + // If it is not a 32-bit type, convert as the underlying type. + if ((et == ELEMENT_TYPE_I4) || (et == ELEMENT_TYPE_U4)) + m_vtElement = VT_RECORD; + else + m_vtElement = OleVariant::GetVarTypeForTypeHandle(m_thElement); + } + else + { + m_vtElement = OleVariant::GetVarTypeForTypeHandle(m_thElement); + } + } + } +#ifdef FEATURE_COMINTEROP + else if (m_thElement == TypeHandle(MscorlibBinder::GetClass(CLASS__ERROR_WRAPPER))) + { + m_vtElement = VT_ERROR; + } +#endif + else + { +#ifdef FEATURE_COMINTEROP + + // Compat: Even if the classes have layout, we still convert them to interface pointers. + + ItfMarshalInfo itfInfo; + MarshalInfo::GetItfMarshalInfo(m_thElement, TypeHandle(), FALSE, FALSE, ms, &itfInfo); + + // Compat: We must always do VT_UNKNOWN marshaling for parameters, even if the interface is marked late-bound. + if (ms == MarshalInfo::MARSHAL_SCENARIO_FIELD) + m_vtElement = static_cast<VARTYPE>(!!(itfInfo.dwFlags & ItfMarshalInfo::ITF_MARSHAL_DISP_ITF) ? VT_DISPATCH : VT_UNKNOWN); + else + m_vtElement = VT_UNKNOWN; + + m_thElement = itfInfo.thItf.IsNull() ? TypeHandle(g_pObjectClass) : itfInfo.thItf; + m_thInterfaceArrayElementClass = itfInfo.thClass; + +#else // FEATURE_COMINTEROP + ReportInvalidArrayMarshalInfo(IDS_EE_BADMARSHAL_UNSUPPORTED_SIG); +#endif // FEATURE_COMINTEROP + } + } + +#ifdef FEATURE_CORECLR + // Avoid throwing exceptions for any managed structs that have layouts and have types of fields that gets default to those banned types by default + // We don't know if they will be passed to native code anyway, and the right place to make the check is in the marshallers + if (AppX::IsAppXProcess() && ms != MarshalInfo::MARSHAL_SCENARIO_FIELD) + { + bool set_error = false; + UINT m_resID = 0; + switch (m_vtElement) + { + case VT_DISPATCH: + m_resID = IDS_EE_BADMARSHAL_TYPE_IDISPATCH ; + set_error = true; + break; + } + if (set_error) + COMPlusThrow(kPlatformNotSupportedException, m_resID); + } +#endif // FEATURE_CORECLR + + // If we are exporting, we need to substitute the VTHACK_* VARTYPE with the actual + // types as expressed in the type library. + if (IsAMIExport(m_flags)) + { + if (m_vtElement == VTHACK_ANSICHAR) + m_vtElement = VT_UI1; + else if (m_vtElement == VTHACK_WINBOOL) + m_vtElement = VT_I4; + else if (m_vtElement == VTHACK_CBOOL) + m_vtElement = VT_UI1; + } + +LExit:; + + RETURN; +} + +bool IsUnsupportedValueTypeReturn(MetaSig& msig) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END + + CorElementType type = msig.GetReturnTypeNormalized(); + + if (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_TYPEDBYREF) + { +#ifdef _TARGET_X86_ + // On x86, the internal CorElementType for value types is normalized by the type loader + // (see calls to ComputeInternalCorElementTypeForValueType in MethodTableBuilder). + // We don't need to redo the normalization here. + return true; +#else + TypeHandle th = msig.GetRetTypeHandleThrowing(); + + return EEClass::ComputeInternalCorElementTypeForValueType(th.GetMethodTable()) == ELEMENT_TYPE_VALUETYPE; +#endif // _TARGET_X86_ + } + + return false; +} + +#ifndef CROSSGEN_COMPILE + +#include "stubhelpers.h" +FCIMPL3(void*, StubHelpers::CreateCustomMarshalerHelper, + MethodDesc* pMD, + mdToken paramToken, + TypeHandle hndManagedType) +{ + FCALL_CONTRACT; + + CustomMarshalerHelper* pCMHelper = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + Module* pModule = pMD->GetModule(); + Assembly* pAssembly = pModule->GetAssembly(); + + +#ifdef FEATURE_COMINTEROP + if (!hndManagedType.IsTypeDesc() && + IsTypeRefOrDef(g_CollectionsEnumeratorClassName, hndManagedType.GetModule(), hndManagedType.GetCl())) + { + pCMHelper = SetupCustomMarshalerHelper(ENUMERATOR_TO_ENUM_VARIANT_CM_NAME, + ENUMERATOR_TO_ENUM_VARIANT_CM_NAME_LEN, + ENUMERATOR_TO_ENUM_VARIANT_CM_COOKIE, + ENUMERATOR_TO_ENUM_VARIANT_CM_COOKIE_LEN, + pAssembly, hndManagedType); + } + else +#endif // FEATURE_COMINTEROP + { + // + // Retrieve the native type for the current parameter. + // + + BOOL result; + NativeTypeParamInfo ParamInfo; + result = ParseNativeTypeInfo(paramToken, pModule->GetMDImport(), &ParamInfo); + + // + // this should all have been done at stub creation time + // + CONSISTENCY_CHECK(result != 0); + CONSISTENCY_CHECK(ParamInfo.m_NativeType == NATIVE_TYPE_CUSTOMMARSHALER); + + // Set up the custom marshaler info. + pCMHelper = SetupCustomMarshalerHelper(ParamInfo.m_strCMMarshalerTypeName, + ParamInfo.m_cCMMarshalerTypeNameBytes, + ParamInfo.m_strCMCookie, + ParamInfo.m_cCMCookieStrBytes, + pAssembly, + hndManagedType); + } + + HELPER_METHOD_FRAME_END(); + + return (void*)pCMHelper; +} +FCIMPLEND + +#endif // CROSSGEN_COMPILE |