From a02b49a0486e92ace50803b977adf8c533ab118f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 23 Mar 2022 09:06:32 -0700 Subject: Add the CustomTypeMarshallerAttribute type to make it easier to identify marshaller types (#65591) --- .../Ancillary.Interop/Ancillary.Interop.csproj | 3 + .../tests/Ancillary.Interop/SpanMarshallers.cs | 264 ++++++--------------- 2 files changed, 76 insertions(+), 191 deletions(-) (limited to 'src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop') diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj index 710d935b82f..a1c0dd27574 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj @@ -12,6 +12,9 @@ + + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs index 46cd7302fbf..358fc92cf6b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs @@ -6,7 +6,10 @@ using System.Runtime.CompilerServices; namespace System.Runtime.InteropServices.GeneratedMarshalling { - [GenericContiguousCollectionMarshaller] + // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + // blow the stack since this is a new optimization in the code-generated interop. + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct ReadOnlySpanMarshaller { private ReadOnlySpan _managedSpan; @@ -47,47 +50,15 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling } } - /// - /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't - /// blow the stack since this is a new optimization in the code-generated interop. - /// - public const int BufferSize = 0x200; - public const bool RequiresStackBuffer = true; + public ReadOnlySpan GetManagedValuesSource() => _managedSpan; - public Span ManagedValues => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_managedSpan), _managedSpan.Length); + public Span GetNativeValuesDestination() => NativeValueStorage; - public Span NativeValueStorage { get; private set; } + private Span NativeValueStorage { get; } public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); - public void SetUnmarshalledCollectionLength(int length) - { - _managedSpan = new T[length]; - } - - public byte* Value - { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - _managedSpan = null; - NativeValueStorage = default; - } - else - { - _allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span(value, _managedSpan.Length * _sizeOfNativeElement); - } - } - } - - public ReadOnlySpan ToManaged() => _managedSpan; + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); public void FreeNative() { @@ -95,68 +66,69 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling } } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct SpanMarshaller { - private ReadOnlySpanMarshaller _inner; + private Span _managedSpan; + private readonly int _sizeOfNativeElement; + private IntPtr _allocatedMemory; public SpanMarshaller(int sizeOfNativeElement) : this() { - _inner = new ReadOnlySpanMarshaller(sizeOfNativeElement); + _sizeOfNativeElement = sizeOfNativeElement; } public SpanMarshaller(Span managed, int sizeOfNativeElement) + :this(managed, Span.Empty, sizeOfNativeElement) { - _inner = new ReadOnlySpanMarshaller(managed, sizeOfNativeElement); } public SpanMarshaller(Span managed, Span stackSpace, int sizeOfNativeElement) { - _inner = new ReadOnlySpanMarshaller(managed, stackSpace, sizeOfNativeElement); - } - - /// - /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't - /// blow the stack since this is a new optimization in the code-generated interop. - /// - public const int BufferSize = ReadOnlySpanMarshaller.BufferSize; - public const bool RequiresStackBuffer = ReadOnlySpanMarshaller.RequiresStackBuffer; - - public Span ManagedValues => _inner.ManagedValues; - - public Span NativeValueStorage - { - get => _inner.NativeValueStorage; + _allocatedMemory = default; + _sizeOfNativeElement = sizeOfNativeElement; + if (managed.Length == 0) + { + _managedSpan = default; + NativeValueStorage = default; + return; + } + _managedSpan = managed; + int spaceToAllocate = managed.Length * sizeOfNativeElement; + if (spaceToAllocate <= stackSpace.Length) + { + NativeValueStorage = stackSpace[0..spaceToAllocate]; + } + else + { + _allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate); + NativeValueStorage = new Span((void*)_allocatedMemory, spaceToAllocate); + } } - public ref byte GetPinnableReference() => ref _inner.GetPinnableReference(); - - public void SetUnmarshalledCollectionLength(int length) - { - _inner.SetUnmarshalledCollectionLength(length); - } + private Span NativeValueStorage { get; set; } - public byte* Value - { - get => _inner.Value; - set => _inner.Value = value; - } + public ReadOnlySpan GetManagedValuesSource() => _managedSpan; + public Span GetManagedValuesDestination(int length) => _managedSpan = new T[length]; + public Span GetNativeValuesDestination() => NativeValueStorage; + public ReadOnlySpan GetNativeValuesSource(int length) => NativeValueStorage = new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); + public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference(); + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + public void FromNativeValue(byte* value) => _allocatedMemory = (IntPtr)value; public Span ToManaged() { - ReadOnlySpan managedInner = _inner.ToManaged(); - return MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(managedInner), managedInner.Length); + return _managedSpan; } public void FreeNative() { - _inner.FreeNative(); + Marshal.FreeCoTaskMem(_allocatedMemory); } } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct NeverNullSpanMarshaller { private SpanMarshaller _inner; @@ -177,47 +149,14 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling _inner = new SpanMarshaller(managed, stackSpace, sizeOfNativeElement); } - /// - /// Stack-alloc threshold set to 256 bytes to enable small spans to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of span parameters doesn't - /// blow the stack. - /// - public const int BufferSize = SpanMarshaller.BufferSize; - - public Span ManagedValues => _inner.ManagedValues; - - public Span NativeValueStorage - { - get => _inner.NativeValueStorage; - } - - public ref byte GetPinnableReference() - { - if (_inner.ManagedValues.Length == 0) - { - return ref *(byte*)0xa5a5a5a5; - } - return ref _inner.GetPinnableReference(); - } - - public void SetUnmarshalledCollectionLength(int length) - { - _inner.SetUnmarshalledCollectionLength(length); - } - public byte* Value - { - get - { - if (_inner.ManagedValues.Length == 0) - { - return (byte*)0xa5a5a5a5; - } - return _inner.Value; - } - - set => _inner.Value = value; - } + public ReadOnlySpan GetManagedValuesSource() => _inner.GetManagedValuesSource(); + public Span GetManagedValuesDestination(int length) => _inner.GetManagedValuesDestination(length); + public Span GetNativeValuesDestination() => _inner.GetNativeValuesDestination(); + public ReadOnlySpan GetNativeValuesSource(int length) => _inner.GetNativeValuesSource(length); + public ref byte GetPinnableReference() => ref _inner.GetPinnableReference(); + public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + public void FromNativeValue(byte* value) => _inner.FromNativeValue(value); public Span ToManaged() => _inner.ToManaged(); @@ -227,7 +166,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling } } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct NeverNullReadOnlySpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -248,50 +187,10 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling _inner = new ReadOnlySpanMarshaller(managed, stackSpace, sizeOfNativeElement); } - /// - /// Stack-alloc threshold set to 256 bytes to enable small spans to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of span parameters doesn't - /// blow the stack. - /// - public const int BufferSize = SpanMarshaller.BufferSize; - public const bool RequiresStackBuffer = SpanMarshaller.RequiresStackBuffer; - - public Span ManagedValues => _inner.ManagedValues; - - public Span NativeValueStorage - { - get => _inner.NativeValueStorage; - } - - public ref byte GetPinnableReference() - { - if (_inner.ManagedValues.Length == 0) - { - return ref *(byte*)0xa5a5a5a5; - } - return ref _inner.GetPinnableReference(); - } - - public void SetUnmarshalledCollectionLength(int length) - { - _inner.SetUnmarshalledCollectionLength(length); - } - - public byte* Value - { - get - { - if (_inner.ManagedValues.Length == 0) - { - return (byte*)0xa5a5a5a5; - } - return _inner.Value; - } - - set => _inner.Value = value; - } - - public ReadOnlySpan ToManaged() => _inner.ToManaged(); + public ReadOnlySpan GetManagedValuesSource() => _inner.GetManagedValuesSource(); + public Span GetNativeValuesDestination() => _inner.GetNativeValuesDestination(); + public ref byte GetPinnableReference() => ref _inner.GetPinnableReference(); + public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference()); public void FreeNative() { @@ -299,22 +198,18 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling } } - [GenericContiguousCollectionMarshaller] + // Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned. + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0)] public unsafe ref struct DirectSpanMarshaller where T : unmanaged { - private int _unmarshalledLength; private T* _allocatedMemory; + private T* _nativeValue; private Span _data; public DirectSpanMarshaller(int sizeOfNativeElement) :this() { - // This check is not exhaustive, but it will catch the majority of cases. - if (typeof(T) == typeof(bool) || typeof(T) == typeof(char) || Unsafe.SizeOf() != sizeOfNativeElement) - { - throw new ArgumentException("This marshaller only supports blittable element types. The provided type parameter must be blittable", nameof(T)); - } } public DirectSpanMarshaller(Span managed, int sizeOfNativeElement) @@ -337,42 +232,29 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling _data = managed; } - /// - /// Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned. - /// - public const int BufferSize = 0; - - public Span ManagedValues => _data; - - public Span NativeValueStorage => _allocatedMemory != null + public ReadOnlySpan GetManagedValuesSource() => _data; + public Span GetManagedValuesDestination(int length) => _data = new Span(_nativeValue, length); + public Span GetNativeValuesDestination() => _allocatedMemory != null ? new Span(_allocatedMemory, _data.Length * Unsafe.SizeOf()) : MemoryMarshal.Cast(_data); + public ReadOnlySpan GetNativeValuesSource(int length) => new ReadOnlySpan(_nativeValue, length * sizeof(T)); + public ref T GetPinnableReference() => ref _data.GetPinnableReference(); - public void SetUnmarshalledCollectionLength(int length) + public T* ToNativeValue() { - _unmarshalledLength = length; + if (_allocatedMemory != null) + { + return _allocatedMemory; + } + return (T*)Unsafe.AsPointer(ref GetPinnableReference()); } - public T* Value + public void FromNativeValue(T* value) { - get - { - if (_allocatedMemory != null) - { - return _allocatedMemory; - } - return (T*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - // We don't save the pointer assigned here to be freed - // since this marshaller passes back the actual memory span from native code - // back to managed code. - _allocatedMemory = null; - _data = new Span(value, _unmarshalledLength); - } + _allocatedMemory = null; + _nativeValue = value; } public Span ToManaged() -- cgit v1.2.3