summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTijoy Tom Kalathiparambil <tijoytk@microsoft.com>2016-05-03 14:25:22 -0700
committertijoytk <tijoytk@microsoft.com>2016-05-06 18:26:29 -0700
commite63ae93ca3fc777ff25930491332e07cf79a9ce3 (patch)
treea2baee2f867d93cf8ffdc44a1bb146588c808f18 /src
parent92f671fe9f0fc583805d4f9c3116126444dabcf4 (diff)
downloadcoreclr-e63ae93ca3fc777ff25930491332e07cf79a9ce3.tar.gz
coreclr-e63ae93ca3fc777ff25930491332e07cf79a9ce3.tar.bz2
coreclr-e63ae93ca3fc777ff25930491332e07cf79a9ce3.zip
UTF8 Marshaling support(UnmanagedType.LPUTF8Str)
Usage: [MarshalAs(UnmanagedType.LPUTF8Str)] applied to string and stringbuilder. Implementation mostly use Encoding.UTF8 API to do the byte buffer to string roundtripping. Introducing two new marshalers, UTF8StringMarshaler and UTF8BufferMarshaler which handle string and StringBuilder respectively. [Out] StringBuilder marshaling use builder capacity as the buffer size ie (builder. Capacity + 1) *3 which is enough for any UTF8 char in BMP plane, infact Encoding.UTF8 mscorlib APIs use the same length.All marshaling flags(ThrowOnUnmapable, defaultchar) are ignored since they do not make sense in UTF16 to UTD8 context. The public contracts are not yet updated, the public contracts and public marshaling API (Marshal.PtrToStringUtf8 and StringToHGlobalUtf8) will be added once the implementation is in. The marshal api are anyway going to be a wrapper around Encoding.GetBytes and GetChars.
Diffstat (limited to 'src')
-rw-r--r--src/inc/corhdr.h2
-rw-r--r--src/mscorlib/model.xml10
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs3
-rw-r--r--src/mscorlib/src/System/String.cs10
-rw-r--r--src/mscorlib/src/System/StubHelpers.cs106
-rw-r--r--src/vm/fieldmarshaler.cpp108
-rw-r--r--src/vm/fieldmarshaler.h13
-rw-r--r--src/vm/ilmarshalers.cpp242
-rw-r--r--src/vm/ilmarshalers.h60
-rw-r--r--src/vm/methodtable.cpp1
-rw-r--r--src/vm/mlinfo.cpp10
-rw-r--r--src/vm/mscorlib.h9
-rw-r--r--src/vm/mtypes.h2
-rw-r--r--src/vm/nsenums.h2
14 files changed, 576 insertions, 2 deletions
diff --git a/src/inc/corhdr.h b/src/inc/corhdr.h
index 071490dde0..c194def07f 100644
--- a/src/inc/corhdr.h
+++ b/src/inc/corhdr.h
@@ -1077,7 +1077,7 @@ typedef enum CorNativeType
NATIVE_TYPE_IINSPECTABLE = 0x2e,
NATIVE_TYPE_HSTRING = 0x2f,
-
+ NATIVE_TYPE_LPUTF8STR = 0x30, // utf-8 string
NATIVE_TYPE_MAX = 0x50, // first invalid element type
} CorNativeType;
diff --git a/src/mscorlib/model.xml b/src/mscorlib/model.xml
index d20c3c4aa3..28ba1a61dc 100644
--- a/src/mscorlib/model.xml
+++ b/src/mscorlib/model.xml
@@ -6124,6 +6124,7 @@
<Member MemberType="Field" Name="LPStruct" />
<Member MemberType="Field" Name="LPTStr" />
<Member MemberType="Field" Name="LPWStr" />
+ <Member MemberType="Field" Name="LPUTF8Str" />
<Member MemberType="Field" Name="R4" />
<Member MemberType="Field" Name="R8" />
<Member MemberType="Field" Name="SafeArray" />
@@ -10100,6 +10101,15 @@
<Member Name="ConvertToManaged(System.IntPtr)" />
<Member Name="ClearNative(System.IntPtr)" />
</Type>
+ <Type Status="ImplRoot" Name="System.StubHelpers.UTF8Marshaler">
+ <Member Name="ConvertToNative(System.Int32,System.String,System.IntPtr)" />
+ <Member Name="ConvertToManaged(System.IntPtr)" />
+ <Member Name="ClearNative(System.IntPtr)" />
+ </Type>
+ <Type Status="ImplRoot" Name="System.StubHelpers.UTF8BufferMarshaler">
+ <Member Name="ConvertToNative(System.Text.StringBuilder,System.IntPtr,System.Int32)" />
+ <Member Name="ConvertToManaged(System.Text.StringBuilder,System.IntPtr)" />
+ </Type>
<Type Status="ApiFxInternal" Name="System.StubHelpers.EventArgsMarshaler" Condition="FEATURE_COMINTEROP">
<Member Name="CreateNativeNCCEventArgsInstance(System.Int32,System.Object,System.Object,System.Int32,System.Int32)"/>
<Member Name="CreateNativePCEventArgsInstance(System.String)" />
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs b/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs
index 9e6ae6b68e..06c963a555 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs
@@ -516,6 +516,9 @@ namespace System.Runtime.InteropServices{
[System.Runtime.InteropServices.ComVisible(false)]
HString = 0x2f, // Windows Runtime HSTRING
+
+ [System.Runtime.InteropServices.ComVisible(false)]
+ LPUTF8Str = 0x30, // UTF8 string
}
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)]
diff --git a/src/mscorlib/src/System/String.cs b/src/mscorlib/src/System/String.cs
index e00fae194b..d2b5b9648d 100644
--- a/src/mscorlib/src/System/String.cs
+++ b/src/mscorlib/src/System/String.cs
@@ -1441,6 +1441,16 @@ namespace System {
return s;
}
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ unsafe internal int GetBytesFromEncoding(byte* pbNativeBuffer, int cbNativeBuffer,Encoding encoding)
+ {
+ // encoding == Encoding.UTF8
+ fixed (char* pwzChar = &this.m_firstChar)
+ {
+ return encoding.GetBytes(pwzChar, m_stringLength, pbNativeBuffer, cbNativeBuffer);
+ }
+ }
[System.Security.SecuritySafeCritical] // auto-generated
unsafe internal int ConvertToAnsi(byte *pbNativeBuffer, int cbNativeBuffer, bool fBestFit, bool fThrowOnUnmappableChar)
diff --git a/src/mscorlib/src/System/StubHelpers.cs b/src/mscorlib/src/System/StubHelpers.cs
index b189fa50d1..3a5ece6217 100644
--- a/src/mscorlib/src/System/StubHelpers.cs
+++ b/src/mscorlib/src/System/StubHelpers.cs
@@ -125,6 +125,112 @@ namespace System.StubHelpers {
}
} // class CSTRMarshaler
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal static class UTF8Marshaler
+ {
+ const int UTF8_CHAR_SIZE = 3;
+ static internal unsafe IntPtr ConvertToNative(int flags, string strManaged, IntPtr pNativeBuffer)
+ {
+ if (null == strManaged)
+ {
+ return IntPtr.Zero;
+ }
+ StubHelpers.CheckStringLength(strManaged.Length);
+
+ int nb;
+ byte* pbNativeBuffer = (byte*)pNativeBuffer;
+
+ // If we are marshaling into a stack buffer allocated by the ILStub
+ // we will use a "1-pass" mode where we convert the string directly into the unmanaged buffer.
+ // else we will allocate the precise native heap memory.
+ if (pbNativeBuffer != null)
+ {
+ // this is the number of bytes allocated by the ILStub.
+ nb = (strManaged.Length + 1) * UTF8_CHAR_SIZE;
+
+ // +1 for the '\0' that we add
+ nb += 1;
+
+ // nb is the actual number of bytes written by Encoding.GetBytes.
+ // use nb to de-limit the string since we are allocating more than
+ // required on stack
+ nb = strManaged.GetBytesFromEncoding(pbNativeBuffer, nb, Encoding.UTF8);
+ }
+ // required bytes > 260 , allocate required bytes on heap
+ else
+ {
+ nb = Encoding.UTF8.GetByteCount(strManaged);
+ // + 1 for the null character.
+ pbNativeBuffer = (byte*)Marshal.AllocCoTaskMem(nb + 1);
+ strManaged.GetBytesFromEncoding(pbNativeBuffer, nb, Encoding.UTF8);
+ }
+ pbNativeBuffer[nb] = 0x0;
+ return (IntPtr)pbNativeBuffer;
+ }
+
+ static internal unsafe string ConvertToManaged(IntPtr cstr)
+ {
+ if (IntPtr.Zero == cstr)
+ return null;
+ int nbBytes = StubHelpers.strlen((sbyte*)cstr);
+ return String.CreateStringFromEncoding((byte*)cstr, nbBytes, Encoding.UTF8);
+ }
+
+ static internal void ClearNative(IntPtr pNative)
+ {
+ if (pNative != IntPtr.Zero)
+ {
+ Win32Native.CoTaskMemFree(pNative);
+ }
+ }
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ internal static class UTF8BufferMarshaler
+ {
+ static internal unsafe IntPtr ConvertToNative(StringBuilder sb, IntPtr pNativeBuffer, int flags)
+ {
+ if (null == sb)
+ {
+ return IntPtr.Zero;
+ }
+
+ // Convert to string first
+ string strManaged = sb.ToString();
+
+ // Get byte count
+ int nb = Encoding.UTF8.GetByteCount(strManaged);
+
+ // EmitConvertSpaceCLRToNative allocates memory
+ byte* pbNativeBuffer = (byte*)pNativeBuffer;
+ nb = strManaged.GetBytesFromEncoding(pbNativeBuffer, nb, Encoding.UTF8);
+
+ pbNativeBuffer[nb] = 0x0;
+ return (IntPtr)pbNativeBuffer;
+ }
+
+ static internal unsafe void ConvertToManaged(StringBuilder sb, IntPtr pNative)
+ {
+ if (pNative == null)
+ return;
+
+ int nbBytes = StubHelpers.strlen((sbyte*)pNative);
+ int numChar = Encoding.UTF8.GetCharCount((byte*)pNative, nbBytes);
+
+ // Encoding.UTF8.GetChars throw an argument exception
+ // if pBuffer is null
+ if (numChar == 0)
+ return;
+
+ char[] cCharBuffer = new char[numChar];
+ fixed (char* pBuffer = cCharBuffer)
+ {
+ numChar = Encoding.UTF8.GetChars((byte*)pNative, nbBytes, pBuffer, numChar);
+ // replace string builder internal buffer
+ sb.ReplaceBufferInternal(pBuffer, numChar);
+ }
+ }
+ }
#if FEATURE_COMINTEROP
diff --git a/src/vm/fieldmarshaler.cpp b/src/vm/fieldmarshaler.cpp
index 37ad39eb4f..39465c269d 100644
--- a/src/vm/fieldmarshaler.cpp
+++ b/src/vm/fieldmarshaler.cpp
@@ -770,6 +770,10 @@ do \
case NATIVE_TYPE_LPWSTR:
INITFIELDMARSHALER(NFT_STRINGUNI, FieldMarshaler_StringUni, ());
break;
+
+ case NATIVE_TYPE_LPUTF8STR:
+ INITFIELDMARSHALER(NFT_STRINGUTF8, FieldMarshaler_StringUtf8, ());
+ break;
case NATIVE_TYPE_LPTSTR:
// We no longer support Win9x so LPTSTR always maps to a Unicode string.
@@ -3158,7 +3162,109 @@ VOID FieldMarshaler_StringAnsi::DestroyNativeImpl(LPVOID pNativeValue) const
CoTaskMemFree(sz);
}
+//=======================================================================
+// CoTask Utf8 <--> System.String
+// See FieldMarshaler for details.
+//=======================================================================
+VOID FieldMarshaler_StringUtf8::UpdateNativeImpl(OBJECTREF* pCLRValue, LPVOID pNativeValue, OBJECTREF *ppCleanupWorkListOnStack) const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pNativeValue));
+ }
+ CONTRACTL_END;
+
+ STRINGREF pString = (STRINGREF)(*pCLRValue);
+ if (pString == NULL)
+ {
+ MAYBE_UNALIGNED_WRITE(pNativeValue, _PTR, NULL);
+ }
+ else
+ {
+ DWORD nc = pString->GetStringLength();
+ if (nc > MAX_SIZE_FOR_INTEROP)
+ COMPlusThrow(kMarshalDirectiveException, IDS_EE_STRING_TOOLONG);
+
+ // Characters would be # of characters + 1 in case left over high surrogate is ?
+ // Max 3 bytes per char for basic multi-lingual plane.
+ nc = (nc + 1) * 3;
+ // +1 for '\0'
+ LPUTF8 lpBuffer = (LPUTF8)CoTaskMemAlloc(nc + 1);
+ if (!lpBuffer)
+ COMPlusThrowOM();
+
+ // UTF8Marshaler.ConvertToNative
+ MethodDescCallSite convertToNative(METHOD__CUTF8MARSHALER__CONVERT_TO_NATIVE);
+
+ ARG_SLOT args[] =
+ {
+ ((ARG_SLOT)(CLR_I4)0),
+ ObjToArgSlot(*pCLRValue),
+ PtrToArgSlot(lpBuffer)
+ };
+ convertToNative.Call(args);
+ MAYBE_UNALIGNED_WRITE(pNativeValue, _PTR, lpBuffer);
+ }
+}
+
+
+//=======================================================================
+// CoTask Utf8 <--> System.String
+// See FieldMarshaler for details.
+//=======================================================================
+VOID FieldMarshaler_StringUtf8::UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pNativeValue));
+ PRECONDITION(CheckPointer(ppProtectedCLRValue));
+ }
+ CONTRACTL_END;
+
+ STRINGREF pString = NULL;
+ LPCUTF8 sz = (LPCUTF8)MAYBE_UNALIGNED_READ(pNativeValue, _PTR);
+ if (!sz)
+ pString = NULL;
+ else
+ {
+ MethodDescCallSite convertToManaged(METHOD__CUTF8MARSHALER__CONVERT_TO_MANAGED);
+ ARG_SLOT args[] =
+ {
+ PtrToArgSlot(pNativeValue),
+ };
+ pString = convertToManaged.Call_RetSTRINGREF(args);
+ }
+ *((STRINGREF*)ppProtectedCLRValue) = pString;
+}
+
+//=======================================================================
+// CoTask Utf8 <--> System.String
+// See FieldMarshaler for details.
+//=======================================================================
+VOID FieldMarshaler_StringUtf8::DestroyNativeImpl(LPVOID pNativeValue) const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pNativeValue));
+ }
+ CONTRACTL_END;
+ LPSTR lpBuffer = (LPSTR)MAYBE_UNALIGNED_READ(pNativeValue, _PTR);
+ MAYBE_UNALIGNED_WRITE(pNativeValue, _PTR, NULL);
+ if (lpBuffer)
+ CoTaskMemFree(lpBuffer);
+}
//=======================================================================
// FixedString <--> System.String
@@ -4607,6 +4713,7 @@ VOID NStructFieldTypeToString(FieldMarshaler* pFM, SString& strNStructFieldType)
switch (GetNStructFieldType()) { \
case NFT_STRINGUNI: rettype ((FieldMarshaler_StringUni*)this)->name##Impl args; break; \
case NFT_STRINGANSI: rettype ((FieldMarshaler_StringAnsi*)this)->name##Impl args; break; \
+ case NFT_STRINGUTF8: rettype ((FieldMarshaler_StringUtf8*)this)->name##Impl args; break; \
case NFT_FIXEDSTRINGUNI: rettype ((FieldMarshaler_FixedStringUni*)this)->name##Impl args; break; \
case NFT_FIXEDSTRINGANSI: rettype ((FieldMarshaler_FixedStringAnsi*)this)->name##Impl args; break; \
case NFT_FIXEDCHARARRAYANSI: rettype ((FieldMarshaler_FixedCharArrayAnsi*)this)->name##Impl args; break; \
@@ -4649,6 +4756,7 @@ VOID NStructFieldTypeToString(FieldMarshaler* pFM, SString& strNStructFieldType)
switch (GetNStructFieldType()) { \
case NFT_STRINGUNI: rettype ((FieldMarshaler_StringUni*)this)->name##Impl args; break; \
case NFT_STRINGANSI: rettype ((FieldMarshaler_StringAnsi*)this)->name##Impl args; break; \
+ case NFT_STRINGUTF8: rettype ((FieldMarshaler_StringUtf8*)this)->name##Impl args; break; \
case NFT_FIXEDSTRINGUNI: rettype ((FieldMarshaler_FixedStringUni*)this)->name##Impl args; break; \
case NFT_FIXEDSTRINGANSI: rettype ((FieldMarshaler_FixedStringAnsi*)this)->name##Impl args; break; \
case NFT_FIXEDCHARARRAYANSI: rettype ((FieldMarshaler_FixedCharArrayAnsi*)this)->name##Impl args; break; \
diff --git a/src/vm/fieldmarshaler.h b/src/vm/fieldmarshaler.h
index 3c06528499..9ec7e87610 100644
--- a/src/vm/fieldmarshaler.h
+++ b/src/vm/fieldmarshaler.h
@@ -757,6 +757,19 @@ public:
ELEMENT_SIZE_IMPL(sizeof(LPWSTR), sizeof(LPWSTR))
};
+//=======================================================================
+// LPUTF8STR <--> System.String
+//=======================================================================
+class FieldMarshaler_StringUtf8 : public FieldMarshaler
+{
+public:
+
+ VOID UpdateNativeImpl(OBJECTREF* pCLRValue, LPVOID pNativeValue, OBJECTREF *ppCleanupWorkListOnStack) const;
+ VOID UpdateCLRImpl(const VOID *pNativeValue, OBJECTREF *ppProtectedCLRValue, OBJECTREF *ppProtectedOldCLRValue) const;
+ VOID DestroyNativeImpl(LPVOID pNativeValue) const;
+
+ ELEMENT_SIZE_IMPL(sizeof(LPSTR), sizeof(LPSTR))
+};
//=======================================================================
// LPSTR <--> System.String
diff --git a/src/vm/ilmarshalers.cpp b/src/vm/ilmarshalers.cpp
index 8062cfd608..ebd8250459 100644
--- a/src/vm/ilmarshalers.cpp
+++ b/src/vm/ilmarshalers.cpp
@@ -507,6 +507,155 @@ void ILOptimizedAllocMarshaler::EmitClearNative(ILCodeStream* pslILEmit)
}
}
+LocalDesc ILUTF8BufferMarshaler::GetManagedType()
+{
+ STANDARD_VM_CONTRACT;
+ return LocalDesc(MscorlibBinder::GetClass(CLASS__STRING_BUILDER));
+}
+
+void ILUTF8BufferMarshaler::EmitConvertSpaceCLRToNative(ILCodeStream* pslILEmit)
+{
+ STANDARD_VM_CONTRACT;
+
+ ILCodeLabel* pNullRefLabel = pslILEmit->NewCodeLabel();
+
+ pslILEmit->EmitLoadNullPtr();
+ EmitStoreNativeValue(pslILEmit);
+
+ EmitLoadManagedValue(pslILEmit);
+ pslILEmit->EmitBRFALSE(pNullRefLabel);
+
+ EmitLoadManagedValue(pslILEmit);
+ // int System.Text.StringBuilder.get_Capacity()
+ pslILEmit->EmitCALL(METHOD__STRING_BUILDER__GET_CAPACITY, 1, 1);
+ pslILEmit->EmitDUP();
+
+ // static void StubHelpers.CheckStringLength(int length)
+ pslILEmit->EmitCALL(METHOD__STUBHELPERS__CHECK_STRING_LENGTH, 1, 0);
+
+ // Max number of bytes for UTF8 string in BMP plane is ( StringBuilder.Capacity + 1 ) * 3 + 1
+ // first +1 if the high surrogate is '?' and second +1 for null byte.
+
+ // stack: capacity_in_bytes
+ pslILEmit->EmitLDC(1);
+ pslILEmit->EmitADD();
+
+ // stack: capacity
+ pslILEmit->EmitLDC(3);
+ pslILEmit->EmitMUL();
+
+ // stack: offset_of_null
+ DWORD dwTmpOffsetOfSecretNull = pslILEmit->NewLocal(ELEMENT_TYPE_I4);
+ pslILEmit->EmitDUP();
+ pslILEmit->EmitSTLOC(dwTmpOffsetOfSecretNull); // make sure the stack is empty for localloc
+
+ // make space for '\0'
+ pslILEmit->EmitLDC(1);
+ pslILEmit->EmitADD();
+
+ // stack: alloc_size_in_bytes
+ ILCodeLabel *pAllocRejoin = pslILEmit->NewCodeLabel();
+ if (IsCLRToNative(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags))
+ {
+ ILCodeLabel *pNoOptimize = pslILEmit->NewCodeLabel();
+ m_dwLocalBuffer = pslILEmit->NewLocal(ELEMENT_TYPE_I);
+
+ // LocalBuffer = 0
+ pslILEmit->EmitLoadNullPtr();
+ pslILEmit->EmitSTLOC(m_dwLocalBuffer);
+
+ // if (alloc_size_in_bytes > MAX_LOCAL_BUFFER_LENGTH) goto NoOptimize
+ pslILEmit->EmitDUP();
+ pslILEmit->EmitLDC(MAX_LOCAL_BUFFER_LENGTH);
+ pslILEmit->EmitCGT_UN();
+ pslILEmit->EmitBRTRUE(pNoOptimize);
+
+ pslILEmit->EmitLOCALLOC();
+ pslILEmit->EmitDUP();
+ pslILEmit->EmitSTLOC(m_dwLocalBuffer);
+ pslILEmit->EmitBR(pAllocRejoin);
+
+ pslILEmit->EmitLabel(pNoOptimize);
+ }
+
+ // static IntPtr AllocCoTaskMem(int cb)
+ pslILEmit->EmitCALL(METHOD__MARSHAL__ALLOC_CO_TASK_MEM, 1, 1);
+
+ pslILEmit->EmitLabel(pAllocRejoin);
+
+ // stack: native_addr
+
+ pslILEmit->EmitDUP();
+ EmitStoreNativeValue(pslILEmit);
+
+ pslILEmit->EmitLDLOC(dwTmpOffsetOfSecretNull);
+
+ // stack: native_addr offset_of_null
+ pslILEmit->EmitADD();
+
+ // stack: addr_of_null0
+ pslILEmit->EmitLDC(0);
+ pslILEmit->EmitSTIND_I1();
+
+ pslILEmit->EmitLabel(pNullRefLabel);
+}
+
+void ILUTF8BufferMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit)
+{
+ STANDARD_VM_CONTRACT;
+ DWORD dwUtf8MarshalFlags =
+ (m_pargs->m_pMarshalInfo->GetBestFitMapping() & 0xFF) |
+ (m_pargs->m_pMarshalInfo->GetThrowOnUnmappableChar() << 8);
+
+ // setup to call UTF8BufferMarshaler.ConvertToNative
+ EmitLoadManagedValue(pslILEmit);
+ EmitLoadNativeValue(pslILEmit);
+ pslILEmit->EmitLDC(dwUtf8MarshalFlags);
+
+ //ConvertToNative(StringBuilder sb,IntPtr pNativeBuffer, int flags)
+ pslILEmit->EmitCALL(METHOD__UTF8BUFFERMARSHALER__CONVERT_TO_NATIVE, 3, 1);
+ EmitStoreNativeValue(pslILEmit);
+}
+
+void ILUTF8BufferMarshaler::EmitConvertSpaceNativeToCLR(ILCodeStream* pslILEmit)
+{
+ STANDARD_VM_CONTRACT;
+
+ ILCodeLabel* pNullRefLabel = pslILEmit->NewCodeLabel();
+
+ EmitLoadNativeValue(pslILEmit);
+ pslILEmit->EmitBRFALSE(pNullRefLabel);
+
+ if (IsIn(m_dwMarshalFlags) || IsCLRToNative(m_dwMarshalFlags))
+ {
+ EmitLoadNativeValue(pslILEmit);
+ // static int System.StubHelpers.StubHelpers.strlen(sbyte* ptr)
+ pslILEmit->EmitCALL(METHOD__STUBHELPERS__STRLEN, 1, 1);
+ }
+ else
+ {
+ // don't touch the native buffer in the native->CLR out-only case
+ pslILEmit->EmitLDC(0);
+ }
+ // Convert to UTF8 and then call
+ // System.Text.StringBuilder..ctor(int capacity)
+ pslILEmit->EmitNEWOBJ(METHOD__STRING_BUILDER__CTOR_INT, 1);
+ EmitStoreManagedValue(pslILEmit);
+ pslILEmit->EmitLabel(pNullRefLabel);
+}
+
+void ILUTF8BufferMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit)
+{
+ STANDARD_VM_CONTRACT;
+
+ EmitLoadManagedValue(pslILEmit);
+ EmitLoadNativeValue(pslILEmit);
+
+ //void UTF8BufferMarshaler.ConvertToManaged(StringBuilder sb, IntPtr pNative)
+ pslILEmit->EmitCALL(METHOD__UTF8BUFFERMARSHALER__CONVERT_TO_MANAGED, 2, 0);
+}
+
+
LocalDesc ILWSTRBufferMarshaler::GetManagedType()
{
STANDARD_VM_CONTRACT;
@@ -1924,6 +2073,99 @@ void ILHSTRINGMarshaler::EmitClearNative(ILCodeStream* pslILEmit)
#endif // FEATURE_COMINTEROP
+LocalDesc ILCUTF8Marshaler::GetManagedType()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return LocalDesc(ELEMENT_TYPE_STRING);
+}
+
+void ILCUTF8Marshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit)
+{
+ STANDARD_VM_CONTRACT;
+
+ DWORD dwUtf8MarshalFlags =
+ (m_pargs->m_pMarshalInfo->GetBestFitMapping() & 0xFF) |
+ (m_pargs->m_pMarshalInfo->GetThrowOnUnmappableChar() << 8);
+
+ bool bPassByValueInOnly = IsIn(m_dwMarshalFlags) && !IsOut(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags);
+ if (bPassByValueInOnly)
+ {
+ DWORD dwBufSize = pslILEmit->NewLocal(ELEMENT_TYPE_I4);
+ m_dwLocalBuffer = pslILEmit->NewLocal(ELEMENT_TYPE_I);
+
+ // LocalBuffer = 0
+ pslILEmit->EmitLoadNullPtr();
+ pslILEmit->EmitSTLOC(m_dwLocalBuffer);
+
+ ILCodeLabel* pNoOptimize = pslILEmit->NewCodeLabel();
+
+ // if == NULL, goto NoOptimize
+ EmitLoadManagedValue(pslILEmit);
+ pslILEmit->EmitBRFALSE(pNoOptimize);
+
+ // (String.Length + 1)
+ // Characters would be # of characters + 1 in case left over high surrogate is ?
+ EmitLoadManagedValue(pslILEmit);
+ pslILEmit->EmitCALL(METHOD__STRING__GET_LENGTH, 1, 1);
+ pslILEmit->EmitLDC(1);
+ pslILEmit->EmitADD();
+
+ // Max 3 bytes per char.
+ // (String.Length + 1) * 3
+ pslILEmit->EmitLDC(3);
+ pslILEmit->EmitMUL();
+
+ // +1 for the 0x0 that we put in.
+ // ((String.Length + 1) * 3) + 1
+ pslILEmit->EmitLDC(1);
+ pslILEmit->EmitADD();
+
+ // BufSize = ( (String.Length+1) * 3) + 1
+ pslILEmit->EmitSTLOC(dwBufSize);
+
+ // if (MAX_LOCAL_BUFFER_LENGTH < BufSize ) goto NoOptimize
+ pslILEmit->EmitLDC(MAX_LOCAL_BUFFER_LENGTH);
+ pslILEmit->EmitLDLOC(dwBufSize);
+ pslILEmit->EmitCLT();
+ pslILEmit->EmitBRTRUE(pNoOptimize);
+
+ // LocalBuffer = localloc(BufSize);
+ pslILEmit->EmitLDLOC(dwBufSize);
+ pslILEmit->EmitLOCALLOC();
+ pslILEmit->EmitSTLOC(m_dwLocalBuffer);
+
+ // NoOptimize:
+ pslILEmit->EmitLabel(pNoOptimize);
+ }
+
+ // UTF8Marshaler.ConvertToNative(dwUtf8MarshalFlags,pManaged, pLocalBuffer)
+ pslILEmit->EmitLDC(dwUtf8MarshalFlags);
+ EmitLoadManagedValue(pslILEmit);
+
+ if (m_dwLocalBuffer != LOCAL_NUM_UNUSED)
+ {
+ pslILEmit->EmitLDLOC(m_dwLocalBuffer);
+ }
+ else
+ {
+ pslILEmit->EmitLoadNullPtr();
+ }
+
+ pslILEmit->EmitCALL(METHOD__CUTF8MARSHALER__CONVERT_TO_NATIVE, 3, 1);
+
+ EmitStoreNativeValue(pslILEmit);
+}
+
+void ILCUTF8Marshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit)
+{
+ STANDARD_VM_CONTRACT;
+
+ EmitLoadNativeValue(pslILEmit);
+ pslILEmit->EmitCALL(METHOD__CUTF8MARSHALER__CONVERT_TO_MANAGED, 1, 1);
+ EmitStoreManagedValue(pslILEmit);
+}
+
LocalDesc ILCSTRMarshaler::GetManagedType()
{
diff --git a/src/vm/ilmarshalers.h b/src/vm/ilmarshalers.h
index d750de1c82..3ed74b4a08 100644
--- a/src/vm/ilmarshalers.h
+++ b/src/vm/ilmarshalers.h
@@ -1996,6 +1996,35 @@ protected:
DWORD m_dwLocalBuffer; // localloc'ed temp buffer variable or -1 if not used
};
+class ILUTF8BufferMarshaler : public ILOptimizedAllocMarshaler
+{
+public:
+ enum
+ {
+ c_fInOnly = FALSE,
+ c_nativeSize = sizeof(void *),
+ c_CLRSize = sizeof(OBJECTREF),
+ };
+
+ enum
+ {
+ // If required buffer length > MAX_LOCAL_BUFFER_LENGTH, don't optimize by allocating memory on stack
+ MAX_LOCAL_BUFFER_LENGTH = MAX_PATH_FNAME + 1
+ };
+
+ ILUTF8BufferMarshaler() :
+ ILOptimizedAllocMarshaler(METHOD__WIN32NATIVE__COTASKMEMFREE)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ virtual LocalDesc GetManagedType();
+ virtual void EmitConvertSpaceCLRToNative(ILCodeStream* pslILEmit);
+ virtual void EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit);
+ virtual void EmitConvertSpaceNativeToCLR(ILCodeStream* pslILEmit);
+ virtual void EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit);
+};
+
class ILWSTRBufferMarshaler : public ILOptimizedAllocMarshaler
{
public:
@@ -2522,6 +2551,37 @@ protected:
};
#endif // FEATURE_COMINTEROP
+
+class ILCUTF8Marshaler : public ILOptimizedAllocMarshaler
+{
+public:
+ enum
+ {
+ c_fInOnly = TRUE,
+ c_nativeSize = sizeof(void *),
+ c_CLRSize = sizeof(OBJECTREF),
+ };
+
+ enum
+ {
+ // If required buffer length > MAX_LOCAL_BUFFER_LENGTH, don't optimize by allocating memory on stack
+ MAX_LOCAL_BUFFER_LENGTH = MAX_PATH_FNAME + 1
+ };
+
+ ILCUTF8Marshaler() :
+ ILOptimizedAllocMarshaler(METHOD__CSTRMARSHALER__CLEAR_NATIVE)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+protected:
+ virtual LocalDesc GetManagedType();
+ virtual void EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit);
+ virtual void EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit);
+};
+
+
+
class ILCSTRMarshaler : public ILOptimizedAllocMarshaler
{
public:
diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp
index bc2597f0f1..c620046afa 100644
--- a/src/vm/methodtable.cpp
+++ b/src/vm/methodtable.cpp
@@ -2918,6 +2918,7 @@ bool MethodTable::ClassifyEightBytesWithNativeLayout(SystemVStructRegisterPassin
case NFT_STRINGUNI:
case NFT_STRINGANSI:
case NFT_ANSICHAR:
+ case NFT_STRINGUTF8:
case NFT_WINBOOL:
case NFT_CBOOL:
case NFT_DELEGATE:
diff --git a/src/vm/mlinfo.cpp b/src/vm/mlinfo.cpp
index 25f33c2549..ab2545296e 100644
--- a/src/vm/mlinfo.cpp
+++ b/src/vm/mlinfo.cpp
@@ -2171,6 +2171,10 @@ MarshalInfo::MarshalInfo(Module* pModule,
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:
{
@@ -4463,6 +4467,9 @@ VOID MarshalInfo::MarshalTypeToString(SString& strMarshalType, BOOL fSizeIsSpeci
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");
@@ -4474,6 +4481,9 @@ VOID MarshalInfo::MarshalTypeToString(SString& strMarshalType, BOOL fSizeIsSpeci
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;
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index a64269fb06..9620627bea 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -2207,6 +2207,15 @@ DEFINE_METHOD(ICASTABLE, ISINSTANCEOF, IsInstanceOfInterface, IM_Ru
DEFINE_METHOD(ICASTABLE, GETIMPLTYPE, GetImplType, IM_RuntimeTypeHandle_RetRuntimeTypeHandle)
#endif // FEATURE_ICASTABLE
+DEFINE_CLASS(CUTF8MARSHALER, StubHelpers, UTF8Marshaler)
+DEFINE_METHOD(CUTF8MARSHALER, CONVERT_TO_NATIVE, ConvertToNative, SM_Int_Str_IntPtr_RetIntPtr)
+DEFINE_METHOD(CUTF8MARSHALER, CONVERT_TO_MANAGED, ConvertToManaged, SM_IntPtr_RetStr)
+DEFINE_METHOD(CUTF8MARSHALER, CLEAR_NATIVE, ClearNative, SM_IntPtr_RetVoid)
+
+DEFINE_CLASS(UTF8BUFFERMARSHALER, StubHelpers, UTF8BufferMarshaler)
+DEFINE_METHOD(UTF8BUFFERMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, NoSig)
+DEFINE_METHOD(UTF8BUFFERMARSHALER, CONVERT_TO_MANAGED, ConvertToManaged, NoSig)
+
#undef DEFINE_CLASS
#undef DEFINE_METHOD
#undef DEFINE_FIELD
diff --git a/src/vm/mtypes.h b/src/vm/mtypes.h
index 6237e3a26e..603409b3dc 100644
--- a/src/vm/mtypes.h
+++ b/src/vm/mtypes.h
@@ -41,6 +41,7 @@ DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_DATE, DateMarshaler,
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_LPWSTR, WSTRMarshaler, false)
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_LPSTR, CSTRMarshaler, false)
+DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_LPUTF8STR, CUTF8Marshaler, false)
#ifdef FEATURE_COMINTEROP
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_BSTR, BSTRMarshaler, false)
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_ANSIBSTR, AnsiBSTRMarshaler, false)
@@ -53,6 +54,7 @@ DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_PCEVENTARGS, PCEventArgsMarshaler,
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_LPWSTR_BUFFER, WSTRBufferMarshaler, false)
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_LPSTR_BUFFER, CSTRBufferMarshaler, false)
+DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_UTF8_BUFFER, UTF8BufferMarshaler, false)
#if defined(FEATURE_COMINTEROP) || !defined(FEATURE_CORECLR)
// CoreCLR doesn't have any support for marshalling interface pointers.
diff --git a/src/vm/nsenums.h b/src/vm/nsenums.h
index 960d2ae403..f5facc76cb 100644
--- a/src/vm/nsenums.h
+++ b/src/vm/nsenums.h
@@ -66,7 +66,7 @@ DEFINE_NFT(NFT_DATETIMEOFFSET, sizeof(INT64), true)
DEFINE_NFT(NFT_SYSTEMTYPE, sizeof(TypeNameNative), true) // System.Type -> Windows.UI.Xaml.Interop.TypeName
DEFINE_NFT(NFT_WINDOWSFOUNDATIONHRESULT, sizeof(int), true) // Windows.Foundation.HResult is marshaled to System.Exception.
#endif // FEATURE_COMINTEROP
-
+DEFINE_NFT(NFT_STRINGUTF8, sizeof(LPVOID), false)
DEFINE_NFT(NFT_ILLEGAL, 1, true)
#ifdef FEATURE_COMINTEROP