Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Share code between source-generated and built-in marshallers #69043

Merged
merged 2 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 0 additions & 59 deletions src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,6 @@ internal static unsafe IntPtr ConvertToNative(int flags, string strManaged, IntP
return new string((sbyte*)cstr);
}

internal static void ClearNative(IntPtr pNative)
{
Marshal.FreeCoTaskMem(pNative);
}

internal static unsafe void ConvertFixedToNative(int flags, string strManaged, IntPtr pNativeBuffer, int length)
{
if (strManaged == null)
Expand Down Expand Up @@ -191,60 +186,6 @@ internal static unsafe string ConvertFixedToManaged(IntPtr cstr, int length)
}
} // class CSTRMarshaler

internal static class UTF8Marshaler
{
private const int MAX_UTF8_CHAR_SIZE = 3;
internal static unsafe IntPtr ConvertToNative(int flags, string strManaged, IntPtr pNativeBuffer)
{
if (null == strManaged)
{
return IntPtr.Zero;
}

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) * MAX_UTF8_CHAR_SIZE;

// 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;
}

internal static unsafe string? ConvertToManaged(IntPtr cstr)
{
if (IntPtr.Zero == cstr)
return null;

byte* pBytes = (byte*)cstr;
int nbBytes = string.strlen(pBytes);
return string.CreateStringFromEncoding(pBytes, nbBytes, Encoding.UTF8);
}

internal static void ClearNative(IntPtr pNative)
{
Marshal.FreeCoTaskMem(pNative);
}
}

internal static class UTF8BufferMarshaler
{
internal static unsafe IntPtr ConvertToNative(StringBuilder sb, IntPtr pNativeBuffer, int flags)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,6 @@ public static unsafe string AnsiStringToString(byte* buffer)
return PInvokeMarshal.AnsiStringToString(buffer);
}

internal static unsafe byte* StringToUTF8String(string str)
{
if (str == null)
return null;

fixed (char* charsPtr = str)
{
int length = Encoding.UTF8.GetByteCount(str);
byte* bytesPtr = (byte*)Marshal.AllocCoTaskMem(checked(length + 1));
int bytes = Encoding.UTF8.GetBytes(charsPtr, str.Length, bytesPtr, length);
Debug.Assert(bytes == length);
bytesPtr[length] = 0;
return bytesPtr;
}
}

public static unsafe string UTF8StringToString(byte* buffer)
{
if (buffer == null)
return null;

return Encoding.UTF8.GetString(buffer, string.strlen(buffer));
}

internal static unsafe void StringToByValAnsiString(string str, byte* pNative, int charCount, bool bestFit, bool throwOnUnmappableChar)
{
if (str != null)
Expand Down
91 changes: 64 additions & 27 deletions src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1772,62 +1772,99 @@ protected override void EmitCleanupManaged(ILCodeStream codeStream)
}
}

#if !READYTORUN
class UTF8StringMarshaller : Marshaller
{
internal override bool CleanupRequired
{
get
{
return true;
}
}
private const int LocalBufferLength = 0x100;

private ILLocalVariable? _marshallerInstance = null;

private TypeDesc Marshaller => Context.SystemModule.GetKnownType("System.Runtime.InteropServices.Marshalling", "Utf8StringMarshaller");

internal override bool CleanupRequired => true;

internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter)
{
Debug.Assert(_marshallerInstance is null);

codeStream.Emit(ILOpcode.call, emitter.NewToken(
Context.GetHelperEntryPoint("InteropHelpers", "CoTaskMemFree")));
InteropTypes.GetMarshal(Context).GetKnownMethod("CoTaskMemFree", null)));
}

protected override void TransformManagedToNative(ILCodeStream codeStream)
{
ILEmitter emitter = _ilCodeStreams.Emitter;
TypeDesc marshaller = Marshaller;

//
// UTF8 marshalling. Allocate a byte array, copy characters
//
var stringToAnsi = Context.GetHelperEntryPoint("InteropHelpers", "StringToUTF8String");
LoadManagedValue(codeStream);
codeStream.Emit(ILOpcode.call, emitter.NewToken(stringToAnsi));
if (_marshallerInstance == null)
_marshallerInstance = emitter.NewLocal(marshaller);

if (In && !Out && !IsManagedByRef)
{
var vBuffer = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.IntPtr));
codeStream.EmitLdc(LocalBufferLength);
codeStream.Emit(ILOpcode.localloc);
codeStream.EmitStLoc(vBuffer);

LoadManagedValue(codeStream);

// Create ReadOnlySpan<byte> from the stack-allocated buffer
codeStream.EmitLdLoc(vBuffer);
codeStream.EmitLdc(LocalBufferLength);

var spanOfByte = Context.SystemModule.GetKnownType("System", "Span`1").MakeInstantiatedType(
new TypeDesc[] { Context.GetWellKnownType(WellKnownType.Byte) });

codeStream.Emit(ILOpcode.newobj, emitter.NewToken(spanOfByte.GetKnownMethod(".ctor",
new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void),
new TypeDesc[] { Context.GetWellKnownType(WellKnownType.Void).MakePointerType(), Context.GetWellKnownType(WellKnownType.Int32) }))));

codeStream.Emit(ILOpcode.newobj, emitter.NewToken(marshaller.GetKnownMethod(".ctor",
new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void),
new TypeDesc[] { Context.GetWellKnownType(WellKnownType.String), spanOfByte }))));
codeStream.EmitStLoc(_marshallerInstance.Value);
}
else
{
LoadManagedValue(codeStream);
codeStream.Emit(ILOpcode.newobj, emitter.NewToken(marshaller.GetKnownMethod(".ctor",
new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void),
new TypeDesc[] { Context.GetWellKnownType(WellKnownType.String) }))));
codeStream.EmitStLoc(_marshallerInstance.Value);
}

codeStream.EmitLdLoca(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("ToNativeValue", null)));
StoreNativeValue(codeStream);
}

protected override void TransformNativeToManaged(ILCodeStream codeStream)
{
ILEmitter emitter = _ilCodeStreams.Emitter;
var ansiToString = Context.GetHelperEntryPoint("InteropHelpers", "UTF8StringToString");
TypeDesc marshaller = Marshaller;

if (_marshallerInstance == null)
_marshallerInstance = emitter.NewLocal(marshaller);

codeStream.EmitLdLoca(_marshallerInstance.Value);
LoadNativeValue(codeStream);
codeStream.Emit(ILOpcode.call, emitter.NewToken(ansiToString));
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("FromNativeValue", null)));

codeStream.EmitLdLoca(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("ToManaged", null)));
StoreManagedValue(codeStream);
}

protected override void EmitCleanupManaged(ILCodeStream codeStream)
{
var emitter = _ilCodeStreams.Emitter;
var lNullCheck = emitter.NewCodeLabel();
ILEmitter emitter = _ilCodeStreams.Emitter;

// Check for null array
LoadManagedValue(codeStream);
codeStream.Emit(ILOpcode.brfalse, lNullCheck);
Debug.Assert(_marshallerInstance != null);

LoadNativeValue(codeStream);
codeStream.EmitLdLoca(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(
Context.GetHelperEntryPoint("InteropHelpers", "CoTaskMemFree")));

codeStream.EmitLabel(lNullCheck);
Marshaller.GetKnownMethod("FreeNative", null)));
}
}
#endif

class SafeHandleMarshaller : Marshaller
{
Expand Down
13 changes: 8 additions & 5 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ DEFINE_CLASS(BYREFERENCE, System, ByReference`1)
DEFINE_METHOD(BYREFERENCE, CTOR, .ctor, NoSig)
DEFINE_METHOD(BYREFERENCE, GET_VALUE, get_Value, NoSig)
DEFINE_CLASS(SPAN, System, Span`1)
DEFINE_METHOD(SPAN, CTOR_PTR_INT, .ctor, IM_VoidPtr_Int_RetVoid)
DEFINE_METHOD(SPAN, GET_ITEM, get_Item, IM_Int_RetRefT)
DEFINE_CLASS(READONLY_SPAN, System, ReadOnlySpan`1)
DEFINE_METHOD(READONLY_SPAN, GET_ITEM, get_Item, IM_Int_RetReadOnlyRefT)
Expand Down Expand Up @@ -1035,7 +1036,6 @@ DEFINE_METHOD(CSTRMARSHALER, CONVERT_TO_NATIVE, ConvertToNative,
DEFINE_METHOD(CSTRMARSHALER, CONVERT_FIXED_TO_NATIVE,ConvertFixedToNative, SM_Int_Str_IntPtr_Int_RetVoid)
DEFINE_METHOD(CSTRMARSHALER, CONVERT_TO_MANAGED, ConvertToManaged, SM_IntPtr_RetStr)
DEFINE_METHOD(CSTRMARSHALER, CONVERT_FIXED_TO_MANAGED,ConvertFixedToManaged, SM_IntPtr_Int_RetStr)
DEFINE_METHOD(CSTRMARSHALER, CLEAR_NATIVE, ClearNative, SM_IntPtr_RetVoid)

DEFINE_CLASS(FIXEDWSTRMARSHALER, StubHelpers, FixedWSTRMarshaler)
DEFINE_METHOD(FIXEDWSTRMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, SM_Str_IntPtr_Int_RetVoid)
Expand Down Expand Up @@ -1183,10 +1183,13 @@ DEFINE_METHOD(ICASTABLEHELPERS, GETIMPLTYPE, GetImplType, SM_ICast

#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(UTF8STRINGMARSHALLER, Marshalling, Utf8StringMarshaller)
DEFINE_METHOD(UTF8STRINGMARSHALLER, CTOR, .ctor, IM_Str_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER, CTOR_SPAN, .ctor, IM_Str_SpanOfByte_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER, TO_NATIVE_VALUE, ToNativeValue, IM_RetPtrByte)
DEFINE_METHOD(UTF8STRINGMARSHALLER, FROM_NATIVE_VALUE, FromNativeValue, IM_PtrByte_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER, TO_MANAGED, ToManaged, IM_RetStr)
DEFINE_METHOD(UTF8STRINGMARSHALLER, FREE_NATIVE, FreeNative, IM_RetVoid)

DEFINE_CLASS(UTF8BUFFERMARSHALER, StubHelpers, UTF8BufferMarshaler)
DEFINE_METHOD(UTF8BUFFERMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, NoSig)
Expand Down
Loading