diff options
author | Jeremy Koritzinsky <jekoritz@microsoft.com> | 2022-03-23 19:06:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-23 19:06:32 +0300 |
commit | a02b49a0486e92ace50803b977adf8c533ab118f (patch) | |
tree | 738b0eff3dbbb212c4b26b6b0a82bf7c333bb981 /src/libraries/System.Runtime.InteropServices/tests | |
parent | 3c126e6e42b0f9c879cae93d94a1deb7e7680f66 (diff) |
Add the CustomTypeMarshallerAttribute type to make it easier to identify marshaller types (#65591)
Diffstat (limited to 'src/libraries/System.Runtime.InteropServices/tests')
13 files changed, 2082 insertions, 1634 deletions
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 @@ <ItemGroup> <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs" Link="GeneratedMarshallingAttribute.cs" /> + <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs" Link="CustomTypeMarshallerKind.cs" /> + <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs" Link="CustomTypeMarshallerDirection.cs" /> + <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs" Link="CustomTypeMarshallerFeatures.cs" /> <Compile Include="$(LibrariesProjectRoot)Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs" Link="ArrayMarshaller.cs" /> </ItemGroup> </Project> 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<T> { private ReadOnlySpan<T> _managedSpan; @@ -47,47 +50,15 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling } } - /// <summary> - /// 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. - /// </summary> - public const int BufferSize = 0x200; - public const bool RequiresStackBuffer = true; + public ReadOnlySpan<T> GetManagedValuesSource() => _managedSpan; - public Span<T> ManagedValues => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_managedSpan), _managedSpan.Length); + public Span<byte> GetNativeValuesDestination() => NativeValueStorage; - public Span<byte> NativeValueStorage { get; private set; } + private Span<byte> 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<byte>(value, _managedSpan.Length * _sizeOfNativeElement); - } - } - } - - public ReadOnlySpan<T> 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<T> { - private ReadOnlySpanMarshaller<T> _inner; + private Span<T> _managedSpan; + private readonly int _sizeOfNativeElement; + private IntPtr _allocatedMemory; public SpanMarshaller(int sizeOfNativeElement) : this() { - _inner = new ReadOnlySpanMarshaller<T>(sizeOfNativeElement); + _sizeOfNativeElement = sizeOfNativeElement; } public SpanMarshaller(Span<T> managed, int sizeOfNativeElement) + :this(managed, Span<byte>.Empty, sizeOfNativeElement) { - _inner = new ReadOnlySpanMarshaller<T>(managed, sizeOfNativeElement); } public SpanMarshaller(Span<T> managed, Span<byte> stackSpace, int sizeOfNativeElement) { - _inner = new ReadOnlySpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement); - } - - /// <summary> - /// 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. - /// </summary> - public const int BufferSize = ReadOnlySpanMarshaller<T>.BufferSize; - public const bool RequiresStackBuffer = ReadOnlySpanMarshaller<T>.RequiresStackBuffer; - - public Span<T> ManagedValues => _inner.ManagedValues; - - public Span<byte> 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<byte>((void*)_allocatedMemory, spaceToAllocate); + } } - public ref byte GetPinnableReference() => ref _inner.GetPinnableReference(); - - public void SetUnmarshalledCollectionLength(int length) - { - _inner.SetUnmarshalledCollectionLength(length); - } + private Span<byte> NativeValueStorage { get; set; } - public byte* Value - { - get => _inner.Value; - set => _inner.Value = value; - } + public ReadOnlySpan<T> GetManagedValuesSource() => _managedSpan; + public Span<T> GetManagedValuesDestination(int length) => _managedSpan = new T[length]; + public Span<byte> GetNativeValuesDestination() => NativeValueStorage; + public ReadOnlySpan<byte> GetNativeValuesSource(int length) => NativeValueStorage = new Span<byte>((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<T> ToManaged() { - ReadOnlySpan<T> 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<T> { private SpanMarshaller<T> _inner; @@ -177,47 +149,14 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling _inner = new SpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement); } - /// <summary> - /// 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. - /// </summary> - public const int BufferSize = SpanMarshaller<T>.BufferSize; - - public Span<T> ManagedValues => _inner.ManagedValues; - - public Span<byte> 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<T> GetManagedValuesSource() => _inner.GetManagedValuesSource(); + public Span<T> GetManagedValuesDestination(int length) => _inner.GetManagedValuesDestination(length); + public Span<byte> GetNativeValuesDestination() => _inner.GetNativeValuesDestination(); + public ReadOnlySpan<byte> 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<T> 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<T> { private ReadOnlySpanMarshaller<T> _inner; @@ -248,50 +187,10 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling _inner = new ReadOnlySpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement); } - /// <summary> - /// 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. - /// </summary> - public const int BufferSize = SpanMarshaller<T>.BufferSize; - public const bool RequiresStackBuffer = SpanMarshaller<T>.RequiresStackBuffer; - - public Span<T> ManagedValues => _inner.ManagedValues; - - public Span<byte> 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<T> ToManaged() => _inner.ToManaged(); + public ReadOnlySpan<T> GetManagedValuesSource() => _inner.GetManagedValuesSource(); + public Span<byte> 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<T> where T : unmanaged { - private int _unmarshalledLength; private T* _allocatedMemory; + private T* _nativeValue; private Span<T> _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<T>() != sizeOfNativeElement) - { - throw new ArgumentException("This marshaller only supports blittable element types. The provided type parameter must be blittable", nameof(T)); - } } public DirectSpanMarshaller(Span<T> managed, int sizeOfNativeElement) @@ -337,42 +232,29 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling _data = managed; } - /// <summary> - /// 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. - /// </summary> - public const int BufferSize = 0; - - public Span<T> ManagedValues => _data; - - public Span<byte> NativeValueStorage => _allocatedMemory != null + public ReadOnlySpan<T> GetManagedValuesSource() => _data; + public Span<T> GetManagedValuesDestination(int length) => _data = new Span<T>(_nativeValue, length); + public Span<byte> GetNativeValuesDestination() => _allocatedMemory != null ? new Span<byte>(_allocatedMemory, _data.Length * Unsafe.SizeOf<T>()) : MemoryMarshal.Cast<T, byte>(_data); + public ReadOnlySpan<byte> GetNativeValuesSource(int length) => new ReadOnlySpan<byte>(_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<T>(value, _unmarshalledLength); - } + _allocatedMemory = null; + _nativeValue = value; } public Span<T> ToManaged() diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs index 8fd114021a4..6075ce224ee 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs @@ -77,7 +77,7 @@ namespace LibraryImportGenerator.IntegrationTests [LibraryImport(NativeExportsNE_Binary, EntryPoint = "transpose_matrix")] [return: MarshalUsing(CountElementName = "numColumns")] - [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionLevel = 1)] + [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionDepth = 1)] public static partial int[][] TransposeMatrix(int[][] matrix, int[] numRows, int numColumns); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs index f8676d68e26..f77258710ca 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs @@ -36,20 +36,20 @@ namespace LibraryImportGenerator.IntegrationTests public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(ListMarshaller<int>), CountElementName = "numValues")] out List<int> res); [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")] - public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List<string> strArray); + public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray); [LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_replace")] - public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] ref List<string> strArray, out int numElements); + public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] ref List<string> strArray, out int numElements); [LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_return")] - [return: MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] - public static partial List<string> ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List<string> strArray, out int numElements); + [return: MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] + public static partial List<string> ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray, out int numElements); [LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_out")] public static partial void ReverseStrings_Out( - [MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List<string> strArray, + [MarshalUsing(typeof(ListMarshaller<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray, out int numElements, - [MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] out List<string> res); + [MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] out List<string> res); [LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")] [return:MarshalUsing(typeof(ListMarshaller<byte>), ConstantElementCount = sizeof(long))] diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs index 057bef953c7..18a3ac4fc1a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs @@ -8,6 +8,7 @@ using Xunit; namespace LibraryImportGenerator.IntegrationTests { + [CustomTypeMarshaller(typeof(int))] public struct SetLastErrorMarshaller { public int val; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs index e9a7f5c47b4..fa879e7d134 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs @@ -31,6 +31,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -82,6 +83,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -172,6 +174,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -204,6 +207,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -236,6 +240,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs index f876a914e0f..7edda25746c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs @@ -38,6 +38,7 @@ struct S {{ }} +[CustomTypeMarshaller(typeof(S))] struct Native {{ public Native(S s) {{ }} @@ -79,6 +80,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -122,6 +124,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -169,6 +172,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -220,6 +224,7 @@ struct S {{ }} +[CustomTypeMarshaller(typeof(S))] struct Native {{ public Native(S s) {{ }} @@ -268,6 +273,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 93b822d6f7b..3312098bfe5 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -381,6 +381,7 @@ partial class Test { string typeName = typeof(T).ToString(); return BasicParametersAndModifiersWithStringMarshallingCustomType(typeName, "Native", DisableRuntimeMarshalling) + @$" +[CustomTypeMarshaller(typeof({typeName}))] struct Native {{ public Native({typeName} s) {{ }} @@ -636,6 +637,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S))] struct Native { private int i; @@ -655,6 +657,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S))] struct Native { private int i; @@ -674,6 +677,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)] struct Native { private int i; @@ -683,8 +687,6 @@ struct Native } public S ToManaged() => new S { b = i != 0 }; - - public const int BufferSize = 1; } "; public static string CustomStructMarshallingStackallocOnlyRefParameter = BasicParameterWithByRefModifier("ref", "S", DisableRuntimeMarshalling) + @" @@ -694,6 +696,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)] struct Native { private int i; @@ -703,9 +706,6 @@ struct Native } public S ToManaged() => new S { b = i != 0 }; - - public const int BufferSize = 1; - public const bool RequiresStackBuffer = false; } "; public static string CustomStructMarshallingOptionalStackallocParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @" @@ -715,6 +715,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)] struct Native { private int i; @@ -728,9 +729,6 @@ struct Native } public S ToManaged() => new S { b = i != 0 }; - - public const int BufferSize = 1; - public const bool RequiresStackBuffer = true; } "; @@ -741,18 +739,17 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] struct Native { public Native(S s, System.Span<byte> b) { - Value = s.b ? 1 : 0; } - public S ToManaged() => new S { b = Value != 0 }; - - public int Value { get; set; } + public S ToManaged() => new S { b = true }; - public const int BufferSize = 1; + public int ToNativeValue() => throw null; + public void FromNativeValue(int value) => throw null; } "; public static string CustomStructMarshallingValuePropertyParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @" @@ -762,16 +759,17 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native { public Native(S s) { - Value = s.b ? 1 : 0; } - public S ToManaged() => new S { b = Value != 0 }; + public S ToManaged() => new S { b = true }; - public int Value { get; set; } + public int ToNativeValue() => throw null; + public void FromNativeValue(int value) => throw null; } "; public static string CustomStructMarshallingPinnableParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @" @@ -783,6 +781,7 @@ class S public ref int GetPinnableReference() => ref i; } +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private int* ptr; @@ -794,15 +793,14 @@ unsafe struct Native public S ToManaged() => new S { i = *ptr }; - public nint Value - { - get => (nint)ptr; - set => ptr = (int*)value; - } + public nint ToNativeValue() => (nint)ptr; + + public void FromNativeValue(nint value) => ptr = (int*)value; } "; public static string CustomStructMarshallingNativeTypePinnable = @" +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System; @@ -814,6 +812,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] unsafe ref struct Native { private byte* ptr; @@ -823,6 +822,7 @@ unsafe ref struct Native { ptr = (byte*)Marshal.AllocCoTaskMem(sizeof(byte)); *ptr = s.c; + stackBuffer = new Span<byte>(ptr, 1); } public Native(S s, Span<byte> buffer) : this() @@ -831,18 +831,16 @@ unsafe ref struct Native stackBuffer[0] = s.c; } - public ref byte GetPinnableReference() => ref (ptr != null ? ref *ptr : ref stackBuffer.GetPinnableReference()); + public ref byte GetPinnableReference() => ref stackBuffer.GetPinnableReference(); public S ToManaged() { return new S { c = *ptr }; } - public byte* Value - { - get => ptr != null ? ptr : throw new InvalidOperationException(); - set => ptr = value; - } + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + + public void FromNativeValue(byte* value) => ptr = value; public void FreeNative() { @@ -851,8 +849,6 @@ unsafe ref struct Native Marshal.FreeCoTaskMem((IntPtr)ptr); } } - - public const int BufferSize = 1; } partial class Test @@ -871,6 +867,7 @@ class S public byte c = 0; } +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private S value; @@ -880,7 +877,7 @@ unsafe struct Native value = s; } - public ref byte Value { get => ref value.c; } + public ref byte ToNativeValue() => ref value.c; } "; @@ -921,6 +918,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] struct Native { private int i; @@ -939,6 +937,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] struct Native { private int i; @@ -957,6 +956,7 @@ struct S } [StructLayout(LayoutKind.Sequential)] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)] struct Native { private int i; @@ -971,16 +971,17 @@ public struct IntStructWrapper public int Value; } +[CustomTypeMarshaller(typeof(IntStructWrapper), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] public struct IntStructWrapperNative { public IntStructWrapperNative(IntStructWrapper managed) { - Value = managed.Value; } - public int Value { get; set; } + public int ToNativeValue() => throw null; + public void FromNativeValue(int value) => throw null; - public IntStructWrapper ToManaged() => new IntStructWrapper { Value = Value }; + public IntStructWrapper ToManaged() => new IntStructWrapper { Value = 1 }; } "; @@ -991,6 +992,7 @@ public struct IntStructWrapper public int Value; } +[CustomTypeMarshaller(typeof(IntStructWrapper))] public struct IntStructWrapperNative { private int value; @@ -1116,13 +1118,18 @@ struct RecursiveStruct2 [NativeMarshalling(typeof(Marshaller<>))] class TestCollection<T> {} -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Marshaller<T> { + public Marshaller(int nativeElementSize) : this() {} public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {} - public System.Span<T> ManagedValues { get; } - public System.Span<byte> NativeValueStorage { get; } - public System.IntPtr Value { get; } + public System.ReadOnlySpan<T> GetManagedValuesSource() => throw null; + public System.Span<T> GetManagedValuesDestination(int length) => throw null; + public System.ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + public void FromNativeValue(System.IntPtr value) => throw null; + public TestCollection<T> ToManaged() => throw null; } "; @@ -1150,15 +1157,17 @@ partial class Test string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty; return nativeMarshallingAttribute + @"class TestCollection<T> {} -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Marshaller<T> { public Marshaller(int nativeElementSize) : this() {} public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {} - public System.Span<T> ManagedValues { get; } - public System.Span<byte> NativeValueStorage { get; } - public System.IntPtr Value { get; set; } - public void SetUnmarshalledCollectionLength(int length) {} + public System.ReadOnlySpan<T> GetManagedValuesSource() => throw null; + public System.Span<T> GetManagedValuesDestination(int length) => throw null; + public System.ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + public void FromNativeValue(System.IntPtr value) => throw null; public TestCollection<T> ToManaged() => throw null; }"; } @@ -1247,13 +1256,18 @@ partial class Test [NativeMarshalling(typeof(Marshaller<,>))] class TestCollection<T> {} -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Marshaller<T, U> { public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {} - public System.Span<T> ManagedValues { get; } - public System.Span<byte> NativeValueStorage { get; } - public System.IntPtr Value { get; } + + public System.ReadOnlySpan<T> GetManagedValuesSource() => throw null; + public System.Span<T> GetManagedValuesDestination(int length) => throw null; + public System.ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + public void FromNativeValue(System.IntPtr value) => throw null; + public TestCollection<T> ToManaged() => throw null; }"; @@ -1264,13 +1278,13 @@ partial class Test { [LibraryImport(""DoesNotExist"")] [return:MarshalUsing(ConstantElementCount=10)] - [return:MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] + [return:MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] public static partial TestCollection<int> Method( - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection<int> p, - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] in TestCollection<int> pIn, + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] TestCollection<int> p, + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] in TestCollection<int> pIn, int pRefSize, - [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] ref TestCollection<int> pRef, - [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] out TestCollection<int> pOut, + [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] ref TestCollection<int> pRef, + [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] out TestCollection<int> pOut, out int pOutSize ); } @@ -1283,14 +1297,14 @@ struct IntWrapper " + CustomCollectionWithMarshaller(enableDefaultMarshalling: true); - public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel => @" + public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth => @" using System.Runtime.InteropServices; [assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling] partial class Test { [LibraryImport(""DoesNotExist"")] public static partial void Method( - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection<int> p); + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] TestCollection<int> p); } struct IntWrapper @@ -1301,14 +1315,14 @@ struct IntWrapper " + CustomCollectionWithMarshaller(enableDefaultMarshalling: true); - public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel => @" + public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth => @" using System.Runtime.InteropServices; [assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling] partial class Test { [LibraryImport(""DoesNotExist"")] public static partial void Method( - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 2)] TestCollection<int> p); + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 2)] TestCollection<int> p); } struct IntWrapper @@ -1385,72 +1399,72 @@ partial class Test { [LibraryImport(""DoesNotExist"")] public static partial void Method( - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)] - [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)] - [MarshalUsing(CountElementName=""arr9"", ElementIndirectionLevel = 9)] - [MarshalUsing(CountElementName=""arr10"", ElementIndirectionLevel = 10)] ref int[][][][][][][][][][][] arr11, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)] - [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)] - [MarshalUsing(CountElementName=""arr9"", ElementIndirectionLevel = 9)]ref int[][][][][][][][][][] arr10, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)] - [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)]ref int[][][][][][][][][] arr9, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)]ref int[][][][][][][][][] arr8, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)]ref int[][][][][][][] arr7, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]ref int[][][][][][] arr6, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]ref int[][][][][] arr5, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]ref int[][][][] arr4, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]ref int[][][] arr3, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]ref int[][] arr2, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]ref int[] arr1, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] + [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)] + [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)] + [MarshalUsing(CountElementName=""arr10"", ElementIndirectionDepth = 10)] ref int[][][][][][][][][][][] arr11, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] + [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)] + [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)]ref int[][][][][][][][][][][] arr10, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] + [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)]ref int[][][][][][][][][][] arr9, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)]ref int[][][][][][][][][] arr8, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)]ref int[][][][][][][] arr7, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]ref int[][][][][][] arr6, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]ref int[][][][][] arr5, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]ref int[][][][] arr4, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]ref int[][][] arr3, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]ref int[][] arr2, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]ref int[] arr1, ref int arr0 ); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs index 262072e940c..3a5403e70c3 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs @@ -110,8 +110,8 @@ namespace LibraryImportGenerator.UnitTests yield return new object[] { CodeSnippets.GenericCollectionMarshallingArityMismatch, 2, 0 }; yield return new object[] { CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 2, 0 }; - yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel, 2, 0 }; - yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel, 1, 0 }; + yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 }; + yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 }; yield return new object[] { CodeSnippets.RecursiveCountElementNameOnReturnValue, 2, 0 }; yield return new object[] { CodeSnippets.RecursiveCountElementNameOnParameter, 2, 0 }; yield return new object[] { CodeSnippets.MutuallyRecursiveCountElementNameOnParameter, 4, 0 }; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs new file mode 100644 index 00000000000..9707345179c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs @@ -0,0 +1,1769 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; +using static Microsoft.Interop.Analyzers.CustomTypeMarshallerAnalyzer; + +using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< + Microsoft.Interop.Analyzers.CustomTypeMarshallerAnalyzer, + Microsoft.Interop.Analyzers.CustomTypeMarshallerFixer>; + +namespace LibraryImportGenerator.UnitTests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)] + public class CustomTypeMarshallerFixerTests + { + [Fact] + public async Task NullNativeType_ReportsDiagnostic() + { + string source = @" +using System.Runtime.InteropServices; + +[{|#0:NativeMarshalling(null)|}] +struct S +{ + public string s; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), + source); + } + + [Fact] + public async Task NonNamedNativeType_ReportsDiagnostic() + { + string source = @" +using System.Runtime.InteropServices; + +[{|#0:NativeMarshalling(typeof(int*))|}] +struct S +{ + public string s; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), + source); + } + + [Fact] + public async Task NonBlittableNativeType_ReportsDiagnostic() + { + string source = @" +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S))] +struct {|#0:Native|} +{ + private string value; + + public Native(S s) + { + value = s.s; + } + + public S ToManaged() => new S { s = value }; +}"; + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); + } + + [Fact] + public async Task ClassNativeType_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +struct S +{ + public string s; +} + +[{|CS0592:CustomTypeMarshaller|}(typeof(S))] +class {|#0:Native|} +{ + private IntPtr value; + + public Native(S s) + { + } + + public S ToManaged() => new S(); +}"; + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); + } + + [Fact] + public async Task BlittableNativeType_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S))] +struct Native +{ + private IntPtr value; + + public Native(S s) + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task BlittableNativeWithNonBlittableToNativeValue_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native +{ + private IntPtr value; + + public Native(S s) + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); + + public string {|#0:ToNativeValue|}() => throw null; + public void FromNativeValue(string value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("string", "S"), + source); + } + + [Fact] + public async Task NonBlittableNativeTypeWithBlittableToNativeValue_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native +{ + private string value; + + public Native(S s) + { + value = s.s; + } + + public S ToManaged() => new S() { s = value }; + + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task NonBlittableGetPinnableReferenceReturnType_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public string s; + + public ref string {|#0:GetPinnableReference|}() => ref s; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private IntPtr value; + + public Native(S s) + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(GetPinnableReferenceReturnTypeBlittableRule).WithLocation(0), + source); + } + + [Fact] + public async Task BlittableGetPinnableReferenceReturnType_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; + + public ref byte GetPinnableReference() => ref c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private IntPtr value; + + public Native(S s) : this() + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task NonBlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public char c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private IntPtr value; + + public Native(S s) + { + value = IntPtr.Zero; + } + + public ref char GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<char>(); + + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task BlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private IntPtr value; + + public Native(S s) : this() + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); + + public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>(); + + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task TypeWithGetPinnableReferenceNonPointerReturnType_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; + + public ref byte GetPinnableReference() => ref c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private IntPtr value; + + public Native(S s) : this() + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); + + public int {|#0:ToNativeValue|}() => throw null; + public void FromNativeValue(int value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBePointerSizedRule).WithLocation(0).WithArguments("int", "S"), + source); + } + + [Fact] + public async Task TypeWithGetPinnableReferencePointerReturnType_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; + + public ref byte GetPinnableReference() => ref c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private IntPtr value; + + public Native(S s) : this() + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); + + public int* ToNativeValue() => throw null; + public void FromNativeValue(int* value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task TypeWithGetPinnableReferenceByRefValuePropertyType_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; + + public ref byte GetPinnableReference() => ref c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private S value; + + public Native(S s) : this() + { + value = s; + } + public ref byte {|#0:ToNativeValue|}() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(RefNativeValueUnsupportedRule).WithLocation(0).WithArguments("Native"), + source); + } + + [Fact] + public async Task NativeTypeWithGetPinnableReferenceByRefValuePropertyType_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private S value; + + public Native(S s) : this() + { + value = s; + } + + public ref byte GetPinnableReference() => ref value.c; + + public ref byte {|#0:ToNativeValue|}() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(RefNativeValueUnsupportedRule).WithLocation(0).WithArguments("Native"), + source); + } + + [Fact] + public async Task NativeTypeWithGetPinnableReferenceNoValueProperty_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] +unsafe struct Native +{ + private byte value; + + public Native(S s) : this() + { + value = s.c; + } + + public ref byte {|#0:GetPinnableReference|}() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>(); +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule).WithLocation(0).WithArguments("Native"), + source); + } + + [Fact] + public async Task NativeTypeWithGetPinnableReferenceWithNonPointerValueProperty_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + private byte value; + + public Native(S s) : this() + { + value = s.c; + } + + public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>(); + + public int ToNativeValue() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task NativeTypeWithNoMarshallingMethods_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.None)] +struct {|#0:Native|} +{ +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(CustomMarshallerTypeMustSupportDirectionRule).WithLocation(0).WithArguments("Native", "S"), + source); + } + + [Fact] + public async Task CollectionNativeTypeWithNoMarshallingMethods_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.None)] +struct {|#0:Native|} +{ +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(CustomMarshallerTypeMustSupportDirectionRule).WithLocation(0).WithArguments("Native", "S"), + source); + } + + [Fact] + public async Task CollectionNativeTypeWithWrongConstructor_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct {|#0:Native|} +{ + public Native(S s) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(S s) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public Native(S managed, int nativeElementSize) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), + fixedSource); + } + + [Fact] + public async Task CollectionNativeTypeWithCorrectConstructor_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(S s, int nativeElementSize) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task CollectionNativeTypeWithIncorrectStackallocConstructor_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] +ref struct {|#0:Native|} +{ + public Native(S s, Span<byte> stackSpace) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] +ref struct Native +{ + public Native(S s, Span<byte> stackSpace) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public Native(S managed, int nativeElementSize) + { + throw new NotImplementedException(); + } + + public Native(S managed, Span<byte> buffer, int nativeElementSize) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), + VerifyCS.Diagnostic(LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule).WithLocation(0).WithArguments("Native", "S")); + } + + [Fact] + public async Task CollectionNativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] +ref struct {|#0:Native|} +{ + public Native(S s, Span<byte> stackSpace, int nativeElementSize) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] +ref struct Native +{ + public Native(S s, Span<byte> stackSpace, int nativeElementSize) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public Native(S managed, int nativeElementSize) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"), + VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S")); + } + + [Fact] + public async Task CollectionNativeTypeWithMissingManagedValuesSource_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct {|#0:Native|} +{ + public Native(S s, int nativeElementSize) : this() {} + + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(S s, int nativeElementSize) : this() {} + + public System.Span<byte> GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public ReadOnlySpan<object> GetManagedValuesSource() + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(LinearCollectionInRequiresCollectionMethodsRule).WithLocation(0).WithArguments("Native", "S"), + fixedSource); + } + + [Fact] + public async Task CollectionNativeTypeWithMissingNativeValuesDestination_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct {|#0:Native|} +{ + public Native(S s, int nativeElementSize) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.IntPtr ToNativeValue() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(S s, int nativeElementSize) : this() {} + + public System.ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public Span<byte> GetNativeValuesDestination() + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(LinearCollectionInRequiresCollectionMethodsRule).WithLocation(0).WithArguments("Native", "S"), + fixedSource); + } + + [Fact] + public async Task CollectionNativeTypeWithCorrectRefShape_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(int nativeElementSize) : this() {} + public Native(S s, int nativeElementSize) : this() {} + + public ReadOnlySpan<int> GetManagedValuesSource() => throw null; + public Span<byte> GetNativeValuesDestination() => throw null; + public ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null; + public Span<int> GetManagedValuesDestination(int length) => throw null; + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; + public S ToManaged() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task CollectionNativeTypeWithMismatched_Element_Type_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(int nativeElementSize) : this() {} + public Native(S s, int nativeElementSize) : this() {} + + public ReadOnlySpan<int> {|#0:GetManagedValuesSource|}() => throw null; + public Span<byte> GetNativeValuesDestination() => throw null; + public ReadOnlySpan<byte> GetNativeValuesSource(int length) => throw null; + public Span<long> GetManagedValuesDestination(int length) => throw null; + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; + public S ToManaged() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + source, + VerifyCS.Diagnostic(LinearCollectionElementTypesMustMatchRule) + .WithLocation(0)); + } + + [Fact] + public async Task NativeTypeWithOnlyConstructor_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] +struct Native +{ + public Native(S s) {} +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task NativeTypeWithOnlyToManagedMethod_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)] +struct Native +{ + public S ToManaged() => new S(); +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task NativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] +struct {|#0:Native|} +{ + public Native(S s, Span<byte> buffer) {} +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] +struct Native +{ + public Native(S s, Span<byte> buffer) {} + + public Native(S managed) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"), + VerifyCS.Diagnostic(ValueInRequiresOneParameterConstructorRule).WithLocation(0).WithArguments("Native", "S")); + } + + [Fact] + public async Task TypeWithOnlyGetPinnableReference_AndInSupport_ReportsDiagnostics() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class {|#0:S|} +{ + public byte c; + public ref byte GetPinnableReference() => ref c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)] +struct {|#1:Native|} +{ + public S ToManaged() => default; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("S", "Native"), + source); + } + + [Fact] + public async Task NativeTypeWithConstructorAndFromNativeValueMethod_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct {|#0:Native|} +{ + public Native(S s) {} + + public void FromNativeValue(IntPtr value) => throw null; + + public S ToManaged() => new S(); +}"; + + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native +{ + public Native(S s) {} + + public void FromNativeValue(IntPtr value) => throw null; + + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(InTwoStageMarshallingRequiresToNativeValueRule).WithLocation(0).WithArguments("Native"), + fixedSource); + } + + [Fact] + public async Task NativeTypeWithToManagedAndToNativeValueMethod_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct {|#0:Native|} +{ + public Native(S managed) {} + + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() => IntPtr.Zero; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native +{ + public Native(S managed) {} + + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() => IntPtr.Zero; + + public void FromNativeValue(IntPtr value) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(OutTwoStageMarshallingRequiresFromNativeValueRule).WithLocation(0).WithArguments("Native"), + fixedSource); + } + + [Fact] + public async Task BlittableNativeTypeOnMarshalUsingParameter_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S))] +struct Native +{ + private IntPtr value; + + public Native(S s) + { + value = IntPtr.Zero; + } + + public S ToManaged() => new S(); +} + + +static class Test +{ + static void Foo([MarshalUsing(typeof(Native))] S s) + {} +} +"; + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task NonBlittableNativeTypeOnMarshalUsingParameter_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S))] +struct {|#0:Native|} +{ + private string value; + + public Native(S s) : this() + { + } + + public S ToManaged() => new S(); +} + + +static class Test +{ + static void Foo([MarshalUsing(typeof(Native))] S s) + {} +} +"; + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); + } + + [Fact] + public async Task NonBlittableNativeTypeOnMarshalUsingReturn_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S))] +struct {|#0:Native|} +{ + private string value; + + public Native(S s) : this() + { + } + + public S ToManaged() => new S(); +} + + +static class Test +{ + [return: MarshalUsing(typeof(Native))] + static S Foo() => new S(); +} +"; + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); + } + + [Fact] + public async Task GenericNativeTypeWithGenericMemberInstantiatedWithBlittable_DoesNotReportDiagnostic() + { + + string source = @" +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native<int>))] +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native<T> + where T : unmanaged +{ + public Native(S s) + { + } + + public S ToManaged() => new S(); + + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; +}"; + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task UninstantiatedGenericNativeTypeOnNonGeneric_ReportsDiagnostic() + { + + string source = @" +using System.Runtime.InteropServices; + +[{|#0:NativeMarshalling(typeof(Native<>))|}] +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native<T> + where T : unmanaged +{ + public Native(S s) + { + } + + public S ToManaged() => new S(); + + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; +}"; + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S"), + source); + } + + [Fact] + public async Task MarshalUsingUninstantiatedGenericNativeType_ReportsDiagnostic() + { + + string source = @" +using System.Runtime.InteropServices; + +struct S +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native<T> + where T : unmanaged +{ + public Native(S s) + { + } + + public S ToManaged() => new S(); + + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; +} + +static class Test +{ + static void Foo([{|#0:MarshalUsing(typeof(Native<>))|}] S s) + {} +}"; + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S"), + source); + } + + [Fact] + public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMismatch_ReportsDiagnostic() + { + string source = @" +using System.Runtime.InteropServices; + +[{|#0:NativeMarshalling(typeof(Native<,>))|}] +struct S<T> +{ + public string s; +} + +[CustomTypeMarshaller(typeof(S<>), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct {|#1:Native|}<T, U> + where T : new() +{ + public Native(S<T> s) + { + } + + public S<T> ToManaged() => new S<T>(); + + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; +}"; + await VerifyCS.VerifyCodeFixAsync(source, + source, + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S<T>"), + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(1).WithArguments("Native<T, U>", "S<>")); + } + + [Fact] + public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMatch_DoesNotReportDiagnostic() + { + string source = @" +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native<>))] +struct S<T> +{ + public T t; +} + +[CustomTypeMarshaller(typeof(S<>), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native<T> + where T : new() +{ + public Native(S<T> s) + { + } + + public S<T> ToManaged() => new S<T>(); + + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; +}"; + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task NativeTypeWithStackallocConstructorWithoutBufferSize_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] +struct Native +{ + public Native(S s) {} + public {|#0:Native|}(S s, Span<byte> buffer) {} +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeRule).WithLocation(0).WithArguments("Native"), + source); + } + + [Fact] + public async Task CustomTypeMarshallerForTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder), Direction = CustomTypeMarshallerDirection.In)] +struct Native<T> +{ + public Native(T a) {} +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task CustomTypeMarshallerForArrayTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), Direction = CustomTypeMarshallerDirection.In)] +struct Native<T> +{ + public Native(T[] a) {} +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task CustomTypeMarshallerForPointerTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*), Direction = CustomTypeMarshallerDirection.In)] +unsafe struct Native<T> where T : unmanaged +{ + public Native(T* a) {} +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task CustomTypeMarshallerForArrayOfPointerTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), Direction = CustomTypeMarshallerDirection.In)] +unsafe struct Native<T> where T : unmanaged +{ + public Native(T*[] a) {} +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task CustomTypeMarshallerWithFreeNativeMethod_NoUnmanagedResourcesFeatures_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public void FreeNative() { } +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] +unsafe struct Native +{ + public Native(S s){} + + public void FreeNative() { } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule) + .WithArguments("Native") + .WithLocation(0)); + } + [Fact] + public async Task CustomTypeMarshallerWithCallerAllocatedBufferConstructor_NoFeature_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, BufferSize = 0x100)] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public Native(S s, Span<byte> buffer) { } +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, BufferSize = 256, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] +unsafe struct Native +{ + public Native(S s){} + + public Native(S s, Span<byte> buffer) { } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule) + .WithArguments("Native") + .WithLocation(0)); + } + + [Fact] + public async Task Add_Feature_Declaration_Preserves_Attribute_Argument_Location() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.None, Direction = CustomTypeMarshallerDirection.In)] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public void FreeNative() { } +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.UnmanagedResources, Direction = CustomTypeMarshallerDirection.In)] +unsafe struct Native +{ + public Native(S s){} + + public void FreeNative() { } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule) + .WithArguments("Native") + .WithLocation(0)); + } + + [Fact] + public async Task CustomTypeMarshallerWithTwoStageMarshallingMethod_NoFeature_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S))] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public int ToNativeValue() => throw null; + + public S ToManaged() => throw null; +} + +[CustomTypeMarshaller(typeof(S))] +unsafe struct {|#1:Native2|} +{ + public Native2(S s){} + + public void FromNativeValue(int value) { } + + public S ToManaged() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + public Native(S s){} + + public int ToNativeValue() => throw null; + + public S ToManaged() => throw null; + + public void FromNativeValue(int value) + { + throw new NotImplementedException(); + } +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native2 +{ + public Native2(S s){} + + public void FromNativeValue(int value) { } + + public S ToManaged() => throw null; + + public int ToNativeValue() + { + throw new NotImplementedException(); + } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + new[] + { + VerifyCS.Diagnostic(ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule) + .WithArguments("Native") + .WithLocation(0), + VerifyCS.Diagnostic(FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule) + .WithArguments("Native2") + .WithLocation(1) + }, + fixedSource, + // One code-fix run is expected for each of the two diagnostics. + // Each fix of the "specifiy the feature" diagnostic will result in code that reports another diagnostic + // for the missing other member. + // The second two code-fix runs are the fixes for those diagnostics. + numIncrementalIterations: 4, + // The first run adds the feature flag and the second adds the missing members for the feature. + numFixAllIterations: 2); + } + + [Fact] + public async Task Mismatched_NativeValue_Type_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + public Native(S s) { } + + public S ToManaged() => new S(); + + public int {|#0:ToNativeValue|}() => throw null; + public void FromNativeValue(long value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + source, + VerifyCS.Diagnostic(TwoStageMarshallingNativeTypesMustMatchRule) + .WithLocation(0)); + } + + [Fact] + public async Task Same_NativeValue_Type_DifferentName_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; +using Value2 = N.Value; + +namespace N +{ + struct Value + { + private int i; + } +} + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + public Native(S s) { } + + public S ToManaged() => new S(); + + public N.Value ToNativeValue() => throw null; + public void FromNativeValue(Value2 value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + source); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs deleted file mode 100644 index bdc1eaf3625..00000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ /dev/null @@ -1,1240 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Testing; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; -using static Microsoft.Interop.Analyzers.ManualTypeMarshallingAnalyzer; - -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.ManualTypeMarshallingAnalyzer>; - -namespace LibraryImportGenerator.UnitTests -{ - [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)] - public class ManualTypeMarshallingAnalyzerTests - { - [Fact] - public async Task NullNativeType_ReportsDiagnostic() - { - string source = @" -using System.Runtime.InteropServices; - -[{|#0:NativeMarshalling(null)|}] -struct S -{ - public string s; -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeNonNullRule).WithLocation(0).WithArguments("S")); - } - - [Fact] - public async Task NonNamedNativeType_ReportsDiagnostic() - { - string source = @" -using System.Runtime.InteropServices; - -[{|#0:NativeMarshalling(typeof(int*))|}] -struct S -{ - public string s; -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("int*", "S")); - } - - [Fact] - public async Task NonBlittableNativeType_ReportsDiagnostic() - { - string source = @" -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -struct S -{ - public string s; -} - -struct {|#0:Native|} -{ - private string value; - - public Native(S s) - { - value = s.s; - } - - public S ToManaged() => new S { s = value }; -}"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task ClassNativeType_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -struct S -{ - public string s; -} - -class {|#0:Native|} -{ - private IntPtr value; - - public Native(S s) - { - } - - public S ToManaged() => new S(); -}"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task BlittableNativeType_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -struct S -{ - public string s; -} - -struct Native -{ - private IntPtr value; - - public Native(S s) - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task BlittableNativeWithNonBlittableValueProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -struct S -{ - public string s; -} - -struct Native -{ - private IntPtr value; - - public Native(S s) - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); - - public string {|#0:Value|} { get => null; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("string", "S")); - } - - [Fact] - public async Task NonBlittableNativeTypeWithBlittableValueProperty_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -struct S -{ - public string s; -} - -struct Native -{ - private string value; - - public Native(S s) - { - value = s.s; - } - - public S ToManaged() => new S() { s = value }; - - public IntPtr Value { get => IntPtr.Zero; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task ClassNativeTypeWithValueProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -struct S -{ - public string s; -} - -class {|#0:Native|} -{ - private string value; - - public Native(S s) - { - value = s.s; - } - - public S ToManaged() => new S() { s = value }; - - public IntPtr Value { get => IntPtr.Zero; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task NonBlittableGetPinnableReferenceReturnType_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public string s; - - public ref string {|#0:GetPinnableReference|}() => ref s; -} - -unsafe struct Native -{ - private IntPtr value; - - public Native(S s) - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); - - public IntPtr Value { get => IntPtr.Zero; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(GetPinnableReferenceReturnTypeBlittableRule).WithLocation(0)); - } - - [Fact] - public async Task BlittableGetPinnableReferenceReturnType_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; - - public ref byte GetPinnableReference() => ref c; -} - -unsafe struct Native -{ - private IntPtr value; - - public Native(S s) : this() - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); - - public IntPtr Value { get => IntPtr.Zero; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task NonBlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public char c; -} - -unsafe struct Native -{ - private IntPtr value; - - public Native(S s) - { - value = IntPtr.Zero; - } - - public ref char GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<char>(); - - public S ToManaged() => new S(); - - public IntPtr Value { get => IntPtr.Zero; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task BlittableMarshallerGetPinnableReferenceReturnType_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -unsafe struct Native -{ - private IntPtr value; - - public Native(S s) : this() - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); - - public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>(); - - public IntPtr Value { get => IntPtr.Zero; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task TypeWithGetPinnableReferenceNonPointerReturnType_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; - - public ref byte GetPinnableReference() => ref c; -} - -unsafe struct Native -{ - private IntPtr value; - - public Native(S s) : this() - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); - - public int {|#0:Value|} { get => 0; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBePointerSizedRule).WithLocation(0).WithArguments("int", "S")); - } - - [Fact] - public async Task TypeWithGetPinnableReferencePointerReturnType_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; - - public ref byte GetPinnableReference() => ref c; -} - -unsafe struct Native -{ - private IntPtr value; - - public Native(S s) : this() - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); - - public int* Value { get => null; set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task TypeWithGetPinnableReferenceByRefValuePropertyType_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; - - public ref byte GetPinnableReference() => ref c; -} - -unsafe struct Native -{ - private S value; - - public Native(S s) : this() - { - value = s; - } - - public ref byte {|#0:Value|} { get => ref value.GetPinnableReference(); } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native")); - } - - [Fact] - public async Task NativeTypeWithGetPinnableReferenceByRefValuePropertyType_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -unsafe struct Native -{ - private S value; - - public Native(S s) : this() - { - value = s; - } - - public ref byte GetPinnableReference() => ref value.c; - - public ref byte {|#0:Value|} { get => ref GetPinnableReference(); } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native")); - } - - [Fact] - public async Task NativeTypeWithGetPinnableReferenceNoValueProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -unsafe struct Native -{ - private byte value; - - public Native(S s) : this() - { - value = s.c; - } - - public ref byte {|#0:GetPinnableReference|}() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>(); -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(MarshallerGetPinnableReferenceRequiresValuePropertyRule).WithLocation(0).WithArguments("Native")); - } - - [Fact] - public async Task NativeTypeWithGetPinnableReferenceWithNonPointerValueProperty_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -unsafe struct Native -{ - private byte value; - - public Native(S s) : this() - { - value = s.c; - } - - public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef<byte>(); - - public int Value { get; set; } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task NativeTypeWithNoMarshallingMethods_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -struct {|#0:Native|} -{ -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task CollectionNativeTypeWithNoMarshallingMethods_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -[GenericContiguousCollectionMarshaller] -struct {|#0:Native|} -{ -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task CollectionNativeTypeWithWrongConstructor_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -[GenericContiguousCollectionMarshaller] -ref struct {|#0:Native|} -{ - public Native(S s) : this() {} - - public Span<int> ManagedValues { get; set; } - public Span<byte> NativeValueStorage { get; set; } - - public IntPtr Value { get; } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task CollectionNativeTypeWithCorrectConstructor_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -[GenericContiguousCollectionMarshaller] -ref struct Native -{ - public Native(S s, int nativeElementSize) : this() {} - - public Span<int> ManagedValues { get; set; } - public Span<byte> NativeValueStorage { get; set; } - - public IntPtr Value { get; } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task CollectionNativeTypeWithIncorrectStackallocConstructor_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -[GenericContiguousCollectionMarshaller] -ref struct {|#0:Native|} -{ - public Native(S s, Span<byte> stackSpace) : this() {} - - public const int BufferSize = 1; - - public Span<int> ManagedValues { get; set; } - public Span<byte> NativeValueStorage { get; set; } - - public IntPtr Value { get; } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task CollectionNativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -[GenericContiguousCollectionMarshaller] -ref struct {|#0:Native|} -{ - public Native(S s, Span<byte> stackSpace, int nativeElementSize) : this() {} - - public const int BufferSize = 1; - - public Span<int> ManagedValues { get; set; } - public Span<byte> NativeValueStorage { get; set; } - - public IntPtr Value { get; } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task CollectionNativeTypeWithMissingManagedValuesProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -[GenericContiguousCollectionMarshaller] -ref struct {|#0:Native|} -{ - public Native(S s, int nativeElementSize) : this() {} - - public Span<byte> NativeValueStorage { get; set; } - - public IntPtr Value { get; } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task CollectionNativeTypeWithMissingNativeValueStorageProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -[GenericContiguousCollectionMarshaller] -ref struct {|#0:Native|} -{ - public Native(S s, int nativeElementSize) : this() {} - - public Span<int> ManagedValues { get; set; } - - public IntPtr Value { get; } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task NativeTypeWithOnlyConstructor_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -struct Native -{ - public Native(S s) {} -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task NativeTypeWithOnlyToManagedMethod_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -struct Native -{ - public S ToManaged() => new S(); -}"; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task NativeTypeWithOnlyStackallocConstructor_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -struct {|#0:Native|} -{ - public Native(S s, Span<byte> buffer) {} - - public const int BufferSize = 0x100; -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native")); - } - - [Fact] - public async Task TypeWithOnlyNativeStackallocConstructorAndGetPinnableReference_ReportsDiagnostics() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[{|#0:NativeMarshalling(typeof(Native))|}] -class S -{ - public byte c; - public ref byte GetPinnableReference() => ref c; -} - -struct {|#1:Native|} -{ - public Native(S s, Span<byte> buffer) {} - - public IntPtr Value => IntPtr.Zero; - - public const int BufferSize = 0x100; -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(1).WithArguments("Native"), - VerifyCS.Diagnostic(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("S", "Native")); - } - - [Fact] - public async Task NativeTypeWithConstructorAndSetOnlyValueProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -struct Native -{ - public Native(S s) {} - - public IntPtr {|#0:Value|} { set {} } -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(ValuePropertyMustHaveGetterRule).WithLocation(0).WithArguments("Native")); - } - - [Fact] - public async Task NativeTypeWithToManagedAndGetOnlyValueProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -struct Native -{ - public S ToManaged() => new S(); - - public IntPtr {|#0:Value|} => IntPtr.Zero; -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(ValuePropertyMustHaveSetterRule).WithLocation(0).WithArguments("Native")); - } - - [Fact] - public async Task BlittableNativeTypeOnMarshalUsingParameter_DoesNotReportDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -struct S -{ - public string s; -} - -struct Native -{ - private IntPtr value; - - public Native(S s) - { - value = IntPtr.Zero; - } - - public S ToManaged() => new S(); -} - - -static class Test -{ - static void Foo([MarshalUsing(typeof(Native))] S s) - {} -} -"; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task NonBlittableNativeTypeOnMarshalUsingParameter_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -struct S -{ - public string s; -} - -struct {|#0:Native|} -{ - private string value; - - public Native(S s) : this() - { - } - - public S ToManaged() => new S(); -} - - -static class Test -{ - static void Foo([MarshalUsing(typeof(Native))] S s) - {} -} -"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task NonBlittableNativeTypeOnMarshalUsingParameter_MultipleCompilations_ReportsDiagnostic_WithLocation() - { - string source1 = @" -using System; -using System.Runtime.InteropServices; - -public struct S -{ - public string s; -} - -public struct Native -{ - private string value; - - public Native(S s) : this() - { - } - - public S ToManaged() => new S(); -} -"; - Compilation compilation1 = await TestUtils.CreateCompilation(source1); - - string source2 = @" -using System; -using System.Runtime.InteropServices; - -static class Test -{ - static void Foo([{|#0:MarshalUsing(typeof(Native))|}] S s) - {} -} -"; - var test = new Verifiers.CSharpCodeFixVerifier<Microsoft.Interop.Analyzers.ManualTypeMarshallingAnalyzer, EmptyCodeFixProvider>.Test - { - ExpectedDiagnostics = - { - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S") - }, - SolutionTransforms = - { - (solution, projectId) => solution.AddMetadataReference(projectId, compilation1.ToMetadataReference()) - }, - TestCode = source2 - }; - - await test.RunAsync(); - } - - [Fact] - public async Task NonBlittableNativeTypeOnMarshalUsingReturn_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -struct S -{ - public string s; -} - -struct {|#0:Native|} -{ - private string value; - - public Native(S s) : this() - { - } - - public S ToManaged() => new S(); -} - - -static class Test -{ - [return: MarshalUsing(typeof(Native))] - static S Foo() => new S(); -} -"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task NonBlittableNativeTypeOnMarshalUsingField_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -struct S -{ - public string s; -} - -struct {|#0:Native|} -{ - private string value; - - public Native(S s) : this() - { - } - - public S ToManaged() => new S(); -} - - -struct Test -{ - [MarshalUsing(typeof(Native))] - S s; -} -"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); - } - - [Fact] - public async Task GenericNativeTypeWithValueTypeValueProperty_DoesNotReportDiagnostic() - { - string source = @" -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native<S>))] -struct S -{ - public string s; -} - -struct Native<T> - where T : new() -{ - public Native(T s) - { - Value = 0; - } - - public T ToManaged() => new T(); - - public int Value { get; set; } -}"; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GenericNativeTypeWithGenericMemberInstantiatedWithBlittable_DoesNotReportDiagnostic() - { - - string source = @" -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native<int>))] -struct S -{ - public string s; -} - -struct Native<T> - where T : new() -{ - public Native(S s) - { - Value = new T(); - } - - public S ToManaged() => new S(); - - public T Value { get; set; } -}"; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task UninstantiatedGenericNativeTypeOnNonGeneric_ReportsDiagnostic() - { - - string source = @" -using System.Runtime.InteropServices; - -[{|#0:NativeMarshalling(typeof(Native<>))|}] -struct S -{ - public string s; -} - -struct Native<T> - where T : new() -{ - public Native(S s) - { - Value = new T(); - } - - public S ToManaged() => new S(); - - public T Value { get; set; } -}"; - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S")); - } - - [Fact] - public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMismatch_ReportsDiagnostic() - { - string source = @" -using System.Runtime.InteropServices; - -[{|#0:NativeMarshalling(typeof(Native<,>))|}] -struct S<T> -{ - public string s; -} - -struct Native<T, U> - where T : new() -{ - public Native(S<T> s) - { - Value = 0; - } - - public S<T> ToManaged() => new S<T>(); - - public int Value { get; set; } -}"; - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<,>", "S<T>")); - } - - [Fact] - public async Task UninstantiatedGenericNativeTypeOnGenericWithArityMatch_DoesNotReportDiagnostic() - { - string source = @" -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native<>))] -struct S<T> -{ - public T t; -} - -struct Native<T> - where T : new() -{ - public Native(S<T> s) - { - Value = 0; - } - - public S<T> ToManaged() => new S<T>(); - - public int Value { get; set; } -}"; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task NativeTypeWithStackallocConstructorWithoutBufferSize_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -class S -{ - public byte c; -} - -struct Native -{ - public Native(S s) {} - public {|#0:Native|}(S s, Span<byte> buffer) {} -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeConstantRule).WithLocation(0).WithArguments("Native")); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs index f56377d272e..dd30ab7a8c8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs @@ -66,6 +66,34 @@ namespace LibraryImportGenerator.UnitTests.Verifiers await test.RunAsync(CancellationToken.None); } + /// <inheritdoc cref="CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult[], string)"/> + public static async Task VerifyCodeFixAsync(string source, string fixedSource, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource, + int numIncrementalIterations, int numFixAllIterations) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + NumberOfIncrementalIterations = numIncrementalIterations, + NumberOfFixAllIterations = numFixAllIterations + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + internal class Test : CSharpCodeFixTest<TAnalyzer, TCodeFix, XUnitVerifier> { public Test() diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs index e1211664390..155edea1bc9 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -17,6 +17,7 @@ namespace SharedTypes public string str2; } + [CustomTypeMarshaller(typeof(StringContainer), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] public struct StringContainerNative { public IntPtr str1; @@ -44,6 +45,7 @@ namespace SharedTypes } } + [CustomTypeMarshaller(typeof(double), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] public struct DoubleToLongMarshaler { public long l; @@ -55,11 +57,9 @@ namespace SharedTypes public double ToManaged() => MemoryMarshal.Cast<long, double>(MemoryMarshal.CreateSpan(ref l, 1))[0]; - public long Value - { - get => l; - set => l = value; - } + public long ToNativeValue() => l; + + public void FromNativeValue(long value) => l = value; } [NativeMarshalling(typeof(BoolStructNative))] @@ -70,6 +70,7 @@ namespace SharedTypes public bool b3; } + [CustomTypeMarshaller(typeof(BoolStruct))] public struct BoolStructNative { public byte b1; @@ -101,6 +102,7 @@ namespace SharedTypes public ref int GetPinnableReference() => ref i; } + [CustomTypeMarshaller(typeof(IntWrapper), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] public unsafe struct IntWrapperMarshaler { public IntWrapperMarshaler(IntWrapper managed) @@ -109,7 +111,10 @@ namespace SharedTypes *Value = managed.i; } - public int* Value { get; set; } + private int* Value { get; set; } + + public int* ToNativeValue() => Value; + public void FromNativeValue(int* value) => Value = value; public IntWrapper ToManaged() => new IntWrapper { i = *Value }; @@ -119,6 +124,7 @@ namespace SharedTypes } } + [CustomTypeMarshaller(typeof(string), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] public unsafe ref struct Utf16StringMarshaller { private ushort* allocated; @@ -160,24 +166,19 @@ namespace SharedTypes return ref span.GetPinnableReference(); } - public ushort* Value + public ushort* ToNativeValue() => (ushort*)Unsafe.AsPointer(ref GetPinnableReference()); + + public void FromNativeValue(ushort* value) { - get - { - return (ushort*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set + allocated = value; + span = new Span<ushort>(value, value == null ? 0 : FindStringLength(value)); + isNullString = value == null; + + static int FindStringLength(ushort* ptr) { - allocated = value; - span = new Span<ushort>(value, value == null ? 0 : FindStringLength(value)); - isNullString = value == null; - - static int FindStringLength(ushort* ptr) - { - // Implemented similarly to string.wcslen as we can't access that outside of CoreLib - var searchSpace = new Span<ushort>(ptr, int.MaxValue); - return searchSpace.IndexOf((ushort)0); - } + // Implemented similarly to string.wcslen as we can't access that outside of CoreLib + var searchSpace = new Span<ushort>(ptr, int.MaxValue); + return searchSpace.IndexOf((ushort)0); } } @@ -190,11 +191,9 @@ namespace SharedTypes { Marshal.FreeCoTaskMem((IntPtr)allocated); } - - public const int BufferSize = 0x100; - public const bool RequiresStackBuffer = true; } + [CustomTypeMarshaller(typeof(string), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] public unsafe ref struct Utf8StringMarshaller { private byte* allocated; @@ -223,24 +222,13 @@ namespace SharedTypes } } - public byte* Value - { - get - { - return allocated != null ? allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); - } - set - { - allocated = value; - } - } + public byte* ToNativeValue() => allocated != null ? allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); - public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)Value); + public void FromNativeValue(byte* value) => allocated = value; - public void FreeNative() => Marshal.FreeCoTaskMem((IntPtr)allocated); + public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)allocated); - public const int BufferSize = 0x100; - public const bool RequiresStackBuffer = true; + public void FreeNative() => Marshal.FreeCoTaskMem((IntPtr)allocated); } [NativeMarshalling(typeof(IntStructWrapperNative))] @@ -249,6 +237,7 @@ namespace SharedTypes public int Value; } + [CustomTypeMarshaller(typeof(IntStructWrapper))] public struct IntStructWrapperNative { public int value; @@ -260,7 +249,7 @@ namespace SharedTypes public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value }; } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x200)] public unsafe ref struct ListMarshaller<T> { private List<T> managedList; @@ -302,48 +291,39 @@ namespace SharedTypes } } - /// <summary> - /// 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. - /// </summary> - public const int BufferSize = 0x200; - public const bool RequiresStackBuffer = true; - - public Span<T> ManagedValues => CollectionsMarshal.AsSpan(managedList); + public ReadOnlySpan<T> GetManagedValuesSource() => CollectionsMarshal.AsSpan(managedList); - public Span<byte> NativeValueStorage { get; private set; } - - public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference(); - - public void SetUnmarshalledCollectionLength(int length) + public Span<T> GetManagedValuesDestination(int length) { + if (allocatedMemory == IntPtr.Zero) + { + managedList = null; + return default; + } managedList = new List<T>(length); for (int i = 0; i < length; i++) { managedList.Add(default); } + return CollectionsMarshal.AsSpan(managedList); } - public byte* Value + private Span<byte> NativeValueStorage { get; set; } + + public Span<byte> GetNativeValuesDestination() => NativeValueStorage; + + public ReadOnlySpan<byte> GetNativeValuesSource(int length) { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - managedList = null; - NativeValueStorage = default; - } - else - { - allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span<byte>(value, (managedList?.Count ?? 0) * sizeOfNativeElement); - } - } + return allocatedMemory == IntPtr.Zero ? default : NativeValueStorage = new Span<byte>((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 List<T> ToManaged() => managedList; |