Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Implement struct marshalling via IL Stubs instead of via FieldMarshalers #26340

Merged
merged 6 commits into from Oct 24, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/System.Private.CoreLib/Resources/Strings.resx
Expand Up @@ -2401,6 +2401,15 @@
<data name="Interop_Marshal_Unmappable_Char" xml:space="preserve">
<value>Cannot marshal: Encountered unmappable character.</value>
</data>
<data name="Interop_Marshal_SafeHandle_InvalidOperation" xml:space="preserve">
<value>Structures containing SafeHandle fields are not allowed in this operation.</value>
</data>
<data name="Interop_Marshal_CannotCreateSafeHandleField" xml:space="preserve">
<value>SafeHandle fields cannot be created from an unmanaged handle.</value>
</data>
<data name="Interop_Marshal_CannotCreateCriticalHandleField" xml:space="preserve">
<value>CriticalHandle fields cannot be created from an unmanaged handle.</value>
</data>
<data name="InvalidCast_CannotCastNullToValueType" xml:space="preserve">
<value>Null object cannot be converted to a value type.</value>
</data>
Expand Down
188 changes: 152 additions & 36 deletions src/System.Private.CoreLib/src/System/StubHelpers.cs
Expand Up @@ -121,6 +121,75 @@ internal static void ClearNative(IntPtr pNative)
{
Interop.Ole32.CoTaskMemFree(pNative);
}

internal static unsafe void ConvertFixedToNative(int flags, string strManaged, IntPtr pNativeBuffer, int length)
{
int numChars = strManaged.Length;
if (numChars >= length)
{
numChars = length - 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a doc reference for the logic where we apply a guaranteed null terminator?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe we have a doc reference for the auto-null-terminator logic.

}

byte* buffer = (byte*)pNativeBuffer;

// Flags defined in ILFixedCSTRMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit).
bool throwOnUnmappableChar = 0 != (flags >> 8);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
bool bestFit = 0 != (flags & 0xFF);
uint defaultCharUsed = 0;

int cbWritten;

fixed (char* pwzChar = strManaged)
{
#if PLATFORM_WINDOWS
cbWritten = Interop.Kernel32.WideCharToMultiByte(
Interop.Kernel32.CP_ACP,
bestFit ? 0 : Interop.Kernel32.WC_NO_BEST_FIT_CHARS,
pwzChar,
numChars,
buffer,
length,
IntPtr.Zero,
throwOnUnmappableChar ? new IntPtr(&defaultCharUsed) : IntPtr.Zero);
#else
cbWritten = Encoding.UTF8.GetBytes(pwzChar, numChars, buffer, length);
#endif
}

if (defaultCharUsed != 0)
{
throw new ArgumentException(SR.Interop_Marshal_Unmappable_Char);
}

if (cbWritten == (int)length)
{
cbWritten--;
}

buffer[cbWritten] = 0;
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}

internal static unsafe string ConvertFixedToManaged(IntPtr cstr, int length)
{
int allocSize = length + 2;
if (allocSize < length)
{
throw new OutOfMemoryException();
}
Span<sbyte> originalBuffer = new Span<sbyte>((byte*)cstr, length);

Span<sbyte> tempBuf = stackalloc sbyte[allocSize];

originalBuffer.CopyTo(tempBuf);
tempBuf[length - 1] = 0;
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
tempBuf[length] = 0;
tempBuf[length + 1] = 0;

fixed (sbyte* buffer = tempBuf)
{
return new string(buffer, 0, string.strlen((byte*)buffer));
}
}
} // class CSTRMarshaler

internal static class UTF8Marshaler
Expand Down Expand Up @@ -443,23 +512,17 @@ internal static void ClearNative(IntPtr pNative)
}
} // class AnsiBSTRMarshaler

internal static class WSTRBufferMarshaler
internal static class FixedWSTRMarshaler
{
internal static IntPtr ConvertToNative(string strManaged)
internal static unsafe void ConvertToNative(string? strManaged, IntPtr nativeHome, int length)
{
Debug.Fail("NYI");
return IntPtr.Zero;
}
ReadOnlySpan<char> managed = strManaged;
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
Span<char> native = new Span<char>((char*)nativeHome, length);

internal static string? ConvertToManaged(IntPtr bstr)
{
Debug.Fail("NYI");
return null;
}
int numChars = Math.Min(managed.Length, length - 1);

internal static void ClearNative(IntPtr pNative)
{
Debug.Fail("NYI");
managed.Slice(0, numChars).CopyTo(native);
native[numChars] = '\0';
}
} // class WSTRBufferMarshaler

Expand Down Expand Up @@ -573,26 +636,45 @@ internal static class ObjectMarshaler

#endif // FEATURE_COMINTEROP

internal static class ValueClassMarshaler
internal sealed class HandleMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertToNative(IntPtr dst, IntPtr src, IntPtr pMT, ref CleanupWorkListElement pCleanupWorkList);
internal static unsafe IntPtr ConvertSafeHandleToNative(SafeHandle? handle, bool hasCleanupWorkList, ref CleanupWorkListElement? cleanupWorkList)
{
if (!hasCleanupWorkList)
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
{
throw new InvalidOperationException(SR.Interop_Marshal_SafeHandle_InvalidOperation);
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertToManaged(IntPtr dst, IntPtr src, IntPtr pMT);
if (handle is null)
{
throw new ArgumentNullException(nameof(handle));
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNative(IntPtr dst, IntPtr pMT);
} // class ValueClassMarshaler
return StubHelpers.AddToCleanupList(ref cleanupWorkList, handle);
}

internal static unsafe void ThrowSafeHandleFieldChanged()
{
throw new NotSupportedException(SR.Interop_Marshal_CannotCreateSafeHandleField);
}

internal static unsafe void ThrowCriticalHandleFieldChanged()
{
throw new NotSupportedException(SR.Interop_Marshal_CannotCreateCriticalHandleField);
}
}

internal static class DateMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern double ConvertToNative(DateTime managedDate);
internal static double ConvertToNative(DateTime managedDate)
{
return managedDate.ToOADate();
}

// The return type is really DateTime but we use long to avoid the pain associated with returning structures.
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern long ConvertToManaged(double nativeDate);
internal static long ConvertToManaged(double nativeDate)
{
return DateTime.DoubleDateToTicks(nativeDate);
}
} // class DateMarshaler

#if FEATURE_COMINTEROP
Expand Down Expand Up @@ -640,6 +722,7 @@ internal struct MarshalerState
#pragma warning disable CA1823 // not used by managed code
private IntPtr m_pElementMT;
private IntPtr m_Array;
private IntPtr m_pManagedNativeArrayMarshaler;
private int m_NativeDataValid;
private int m_BestFitMap;
private int m_ThrowOnUnmappableChar;
Expand All @@ -648,7 +731,7 @@ internal struct MarshalerState
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int dwFlags);
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int dwFlags, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
Expand All @@ -664,17 +747,38 @@ internal struct MarshalerState
internal static extern void ConvertContentsToManaged(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNative(IntPtr pMarshalState, IntPtr pNativeHome, int cElements);
internal static extern void ClearNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome, int cElements);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNativeContents(IntPtr pMarshalState, IntPtr pNativeHome, int cElements);
internal static extern void ClearNativeContents(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome, int cElements);
} // class MngdNativeArrayMarshaler

internal static class MngdFixedArrayMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int dwFlags, int cElements, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertContentsToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToManaged(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertContentsToManaged(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ClearNativeContents(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
} // class MngdFixedArrayMarshaler

#if FEATURE_COMINTEROP
internal static class MngdSafeArrayMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int iRank, int dwFlags);
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, int iRank, int dwFlags, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
Expand All @@ -695,7 +799,7 @@ internal static class MngdSafeArrayMarshaler
internal static class MngdHiddenLengthArrayMarshaler
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, IntPtr cbElementSize, ushort vt);
internal static extern void CreateMarshaler(IntPtr pMarshalState, IntPtr pMT, IntPtr cbElementSize, ushort vt, IntPtr pManagedMarshaler);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ConvertSpaceToNative(IntPtr pMarshalState, ref object pManagedHome, IntPtr pNativeHome);
Expand Down Expand Up @@ -971,7 +1075,8 @@ private unsafe IntPtr ConvertArrayToNative(object pManagedHome, int dwFlags)
MngdNativeArrayMarshaler.CreateMarshaler(
pvArrayMarshaler,
IntPtr.Zero, // not needed as we marshal primitive VTs only
dwArrayMarshalerFlags);
dwArrayMarshalerFlags,
IntPtr.Zero); // not needed as we marshal primitive VTs only

IntPtr pNativeHome;
IntPtr pNativeHomeAddr = new IntPtr(&pNativeHome);
Expand Down Expand Up @@ -1460,6 +1565,17 @@ internal struct NativeVariant
#endif
} // struct NativeVariant

// This NativeDecimal type is very similar to the System.Decimal type, except it requires an 8-byte alignment
// like the native DECIMAL type instead of the 4-byte requirement of the System.Decimal type.
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
[StructLayout(LayoutKind.Sequential)]
internal struct NativeDecimal
{
private ushort reserved;
private ushort signScale;
private uint hi32;
private ulong lo64;
}

internal abstract class CleanupWorkListElement
{
private CleanupWorkListElement? m_Next;
Expand All @@ -1476,7 +1592,7 @@ public void Destroy()
}
}

public static void AddToCleanupList(ref CleanupWorkListElement list, CleanupWorkListElement newElement)
public static void AddToCleanupList(ref CleanupWorkListElement? list, CleanupWorkListElement newElement)
{
if (list == null)
{
Expand Down Expand Up @@ -1560,14 +1676,14 @@ internal static class StubHelpers
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void ThrowInteropParamException(int resID, int paramIdx);

internal static IntPtr AddToCleanupList(ref CleanupWorkListElement pCleanupWorkList, SafeHandle handle)
internal static IntPtr AddToCleanupList(ref CleanupWorkListElement? pCleanupWorkList, SafeHandle handle)
{
SafeHandleCleanupWorkListElement element = new SafeHandleCleanupWorkListElement(handle);
CleanupWorkListElement.AddToCleanupList(ref pCleanupWorkList, element);
return element.AddRef();
}

internal static void KeepAliveViaCleanupList(ref CleanupWorkListElement pCleanupWorkList, object obj)
internal static void KeepAliveViaCleanupList(ref CleanupWorkListElement? pCleanupWorkList, object obj)
{
KeepAliveCleanupWorkListElement element = new KeepAliveCleanupWorkListElement(obj);
CleanupWorkListElement.AddToCleanupList(ref pCleanupWorkList, element);
Expand Down Expand Up @@ -1713,7 +1829,7 @@ internal static void CheckStringLength(uint length)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void FmtClassUpdateCLRInternal(object obj, byte* pNative);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void LayoutDestroyNativeInternal(byte* pNative, IntPtr pMT);
internal static extern unsafe void LayoutDestroyNativeInternal(object obj, byte* pNative);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object AllocateInternal(IntPtr typeHandle);

Expand Down
23 changes: 12 additions & 11 deletions src/debug/daccess/nidump.cpp
Expand Up @@ -14,6 +14,7 @@

#include <comcallablewrapper.h>
#include <gcdump.h>
#include <fieldmarshaler.h>

#if !defined(FEATURE_CORESYSTEM)
#include <algorithm>
Expand Down Expand Up @@ -8383,36 +8384,36 @@ NativeImageDumper::DumpEEClassForMethodTable( PTR_MethodTable mt )
VERBOSE_TYPES );
DisplayWriteFieldInt( m_numCTMFields, eecli->m_numCTMFields,
EEClassLayoutInfo, VERBOSE_TYPES );
PTR_FieldMarshaler fmArray = eecli->GetFieldMarshalers();
DisplayWriteFieldAddress( m_pFieldMarshalers,
PTR_NativeFieldDescriptor fmArray = eecli->GetNativeFieldDescriptors();
DisplayWriteFieldAddress( m_pNativeFieldDescriptors,
DPtrToPreferredAddr(fmArray),
eecli->m_numCTMFields
* MAXFIELDMARSHALERSIZE,
* sizeof(NativeFieldDescriptor),
EEClassLayoutInfo, VERBOSE_TYPES );
/* REVISIT_TODO Wed 03/22/2006
* Dump the various types of FieldMarshalers.
* Dump the various types of NativeFieldDescriptors.
*/
#if 0
DisplayStartArrayWithOffset( m_pFieldMarshalers, NULL,
DisplayStartArrayWithOffset( m_pNativeFieldDescriptors, NULL,
EEClassLayoutInfo, VERBOSE_TYPES );
for( unsigned i = 0; i < eecli->m_numCTMFields; ++i )
{
/* REVISIT_TODO Wed 03/22/2006
* Try to display the type of the field marshaler in the future.
*/
PTR_FieldMarshaler current = fmArray + i;
DisplayStartStructure( "FieldMarshaler",
PTR_NativeFieldDescriptor current = fmArray + i;
DisplayStartStructure( "NativeFieldDescriptor",
DPtrToPreferredAddr(current),
sizeof(*current), VERBOSE_TYPES );
WriteFieldFieldDesc( m_pFD, PTR_FieldDesc(TO_TADDR(current->m_pFD)),
FieldMarshaler, VERBOSE_TYPES );
DisplayWriteFieldInt( m_dwExternalOffset,
current->m_dwExternalOffset, FieldMarshaler,
NativeFieldDescriptor, VERBOSE_TYPES );
DisplayWriteFieldInt( m_offset,
current->m_offset, NativeFieldDescriptor,
VERBOSE_TYPES );
DisplayEndStructure( VERBOSE_TYPES ); //FieldMarshaler
}

DisplayEndArray( "Number of FieldMarshalers", VERBOSE_TYPES ); //m_pFieldMarshalers
DisplayEndArray( "Number of NativeFieldDescriptors", VERBOSE_TYPES ); //m_pNativeFieldDescriptors
#endif

DisplayEndStructure( EECLASSES ); //LayoutInfo
Expand Down
2 changes: 2 additions & 0 deletions src/dlls/mscorrc/mscorrc.rc
Expand Up @@ -758,6 +758,8 @@ BEGIN
IDS_CLASSLOAD_WRONGCPU "Could not load file or assembly '%1'. This assembly was compiled for a different processor."

IDS_CANNOT_MARSHAL "Type '%1' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."

IDS_CANNOT_MARSHAL_RECURSIVE_DEF "Type '%1' cannot be marshaled as an unmanaged structure; its native layout contains a recursive definition so no meaningful size can be computed."

IDS_INVALID_REDIM "Illegal attempt to replace or redimension a fixed or locked SafeArray."

Expand Down
1 change: 1 addition & 0 deletions src/dlls/mscorrc/resource.h
Expand Up @@ -179,6 +179,7 @@
#define IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD 0x1769

#define IDS_CANNOT_MARSHAL 0x1770
#define IDS_CANNOT_MARSHAL_RECURSIVE_DEF 0x1771
#define IDS_EE_HASH_VAL_FAILED 0x1772


Expand Down