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 | |
parent | 3c126e6e42b0f9c879cae93d94a1deb7e7680f66 (diff) |
Add the CustomTypeMarshallerAttribute type to make it easier to identify marshaller types (#65591)
Diffstat (limited to 'src/libraries')
55 files changed, 5045 insertions, 2941 deletions
diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index a48640d1e46..423973b759d 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -44,6 +44,9 @@ namespace System.DirectoryServices.Protocols public string packageList; public int packageListLength; +#if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] +#endif [StructLayout(LayoutKind.Sequential)] internal struct Native { @@ -173,6 +176,7 @@ namespace System.DirectoryServices.Protocols public IntPtr bv_val = IntPtr.Zero; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(BerVal), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly BerVal _managed; @@ -183,7 +187,7 @@ namespace System.DirectoryServices.Protocols public ref int GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef<int>() : ref _managed.bv_len); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } @@ -211,6 +215,7 @@ namespace System.DirectoryServices.Protocols #if NET7_0_OR_GREATER public static readonly unsafe int Size = sizeof(Marshaller.Native); + [CustomTypeMarshaller(typeof(LdapReferralCallback), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] public unsafe struct Marshaller { public unsafe struct Native @@ -234,11 +239,9 @@ namespace System.DirectoryServices.Protocols _native.dereference = managed.dereference is not null ? Marshal.GetFunctionPointerForDelegate(managed.dereference) : IntPtr.Zero; } - public Native Value - { - get => _native; - set => _native = value; - } + public Native ToNativeValue() => _native; + + public void FromNativeValue(Native value) => _native = value; public LdapReferralCallback ToManaged() { diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs index 50be738d918..94dd8bc4360 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs @@ -26,6 +26,7 @@ internal static partial class Interop internal ushort MaximumLength; internal string Buffer; + [CustomTypeMarshaller(typeof(MARSHALLED_UNICODE_STRING), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] public struct Native { internal ushort Length; diff --git a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs index 4bdd0880f52..ce07eea1841 100644 --- a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs +++ b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs @@ -37,6 +37,7 @@ internal static partial class Interop internal uint nStartPage; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal unsafe struct Native { private uint dwSize; @@ -139,6 +140,7 @@ internal static partial class Interop internal IntPtr hSelectedCertStore; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal unsafe struct Native { private uint dwSize; diff --git a/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs b/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs index 26c0dd93fd3..a4219658a3a 100644 --- a/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs +++ b/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs @@ -49,6 +49,7 @@ internal static partial class Interop internal string Value; internal uint ValueLength; + [CustomTypeMarshaller(typeof(HttpHeader), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal struct Native { private IntPtr Name; diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs index 7074ad6cdc0..b7e40e77b99 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs @@ -53,6 +53,7 @@ internal static partial class Interop uint modifiers); #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(StringBuilder), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] private unsafe struct SimpleStringBufferMarshaller { public SimpleStringBufferMarshaller(StringBuilder builder) @@ -64,7 +65,9 @@ internal static partial class Interop builder.CopyTo(0, buffer, length - 1); } - public void* Value { get; } + private void* Value { get; } + + public void* ToNativeValue() => Value; public void FreeNative() { diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs index 47f105b6c3f..0a0c24292ca 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs @@ -259,6 +259,7 @@ internal static partial class Interop [MarshalAs(UnmanagedType.Bool)] public bool AutoLoginIfChallenged; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] public struct Native { private uint Flags; diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs index 28478031eb3..b19cd8dba06 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs @@ -74,6 +74,7 @@ internal static partial class Interop internal byte[] MulticastAddress; // IP address of group. internal int InterfaceIndex; // Local interface index. + [CustomTypeMarshaller(typeof(IPv6MulticastRequest))] public unsafe struct Native { private const int MulticastAddressLength = 16; diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs index d28dba25b39..f5c5277a7d3 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs @@ -12,6 +12,10 @@ using System.Runtime.CompilerServices; namespace System.Runtime.InteropServices.GeneratedMarshalling { + // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + // blow the stack since this is a new optimization in the code-generated interop. + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] #if LIBRARYIMPORT_GENERATOR_TEST public #else @@ -58,44 +62,24 @@ 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() => _managedArray; + public Span<T> GetManagedValuesDestination(int length) => _allocatedMemory == IntPtr.Zero ? null : _managedArray = new T[length]; + public Span<byte> GetNativeValuesDestination() => NativeValueStorage; - public Span<T> ManagedValues => _managedArray; - - public Span<byte> NativeValueStorage { get; private set; } + public ReadOnlySpan<byte> GetNativeValuesSource(int length) + { + return _allocatedMemory == IntPtr.Zero ? default : NativeValueStorage = new Span<byte>((void*)_allocatedMemory, length * _sizeOfNativeElement); + } + private Span<byte> NativeValueStorage { get; set; } public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); - public void SetUnmarshalledCollectionLength(int length) - { - _managedArray = new T[length]; - } - public byte* Value + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + + public void FromNativeValue(byte* value) { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - _managedArray = null; - NativeValueStorage = default; - } - else - { - _allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span<byte>(value, (_managedArray?.Length ?? 0) * _sizeOfNativeElement); - } - } + _allocatedMemory = (IntPtr)value; } public T[]? ToManaged() => _managedArray; @@ -106,6 +90,10 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling } } + // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + // blow the stack since this is a new optimization in the code-generated interop. + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] #if LIBRARYIMPORT_GENERATOR_TEST public #else @@ -152,45 +140,22 @@ 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 Span<IntPtr> ManagedValues => Unsafe.As<IntPtr[]>(_managedArray); + public ReadOnlySpan<IntPtr> GetManagedValuesSource() => Unsafe.As<IntPtr[]>(_managedArray); + public Span<IntPtr> GetManagedValuesDestination(int length) => _allocatedMemory == IntPtr.Zero ? null : Unsafe.As<IntPtr[]>(_managedArray = new T*[length]); + public Span<byte> GetNativeValuesDestination() => NativeValueStorage; - public Span<byte> NativeValueStorage { get; private set; } - - public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); - - public void SetUnmarshalledCollectionLength(int length) + public ReadOnlySpan<byte> GetNativeValuesSource(int length) { - _managedArray = new T*[length]; + return _allocatedMemory == IntPtr.Zero ? default : NativeValueStorage = new Span<byte>((void*)_allocatedMemory, length * _sizeOfNativeElement); } + private Span<byte> NativeValueStorage { get; set; } - public byte* Value - { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - _managedArray = null; - NativeValueStorage = default; - } - else - { - _allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span<byte>(value, (_managedArray?.Length ?? 0) * _sizeOfNativeElement); - } + public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } + public void FromNativeValue(byte* value) + { + _allocatedMemory = (IntPtr)value; } public T*[]? ToManaged() => _managedArray; diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs new file mode 100644 index 00000000000..0ee347d72b9 --- /dev/null +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; + +namespace System.Runtime.InteropServices +{ + /// <summary> + /// A direction of marshalling data into or out of the managed environment + /// </summary> + [Flags] +#if LIBRARYIMPORT_GENERATOR_TEST + public +#else + internal +#endif + enum CustomTypeMarshallerDirection + { + /// <summary> + /// No marshalling direction + /// </summary> + [EditorBrowsable(EditorBrowsableState.Never)] + None = 0, + /// <summary> + /// Marshalling from a managed environment to an unmanaged environment + /// </summary> + In = 0x1, + /// <summary> + /// Marshalling from an unmanaged environment to a managed environment + /// </summary> + Out = 0x2, + /// <summary> + /// Marshalling to and from managed and unmanaged environments + /// </summary> + Ref = In | Out, + } +} diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs new file mode 100644 index 00000000000..fe58f386f72 --- /dev/null +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + /// <summary> + /// Optional features supported by custom type marshallers. + /// </summary> + [Flags] +#if LIBRARYIMPORT_GENERATOR_TEST + public +#else + internal +#endif + enum CustomTypeMarshallerFeatures + { + /// <summary> + /// No optional features supported + /// </summary> + None = 0, + /// <summary> + /// The marshaller owns unmanaged resources that must be freed + /// </summary> + UnmanagedResources = 0x1, + /// <summary> + /// The marshaller can use a caller-allocated buffer instead of allocating in some scenarios + /// </summary> + CallerAllocatedBuffer = 0x2, + /// <summary> + /// The marshaller uses the two-stage marshalling design for its <see cref="CustomTypeMarshallerKind"/> instead of the one-stage design. + /// </summary> + TwoStageMarshalling = 0x4 + } +} diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs new file mode 100644 index 00000000000..06c73cdf7b6 --- /dev/null +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + /// <summary> + /// The shape of a custom type marshaller for usage in source-generated interop scenarios. + /// </summary> + /// <remarks> + /// <seealso cref="LibraryImportAttribute"/> + /// </remarks> +#if LIBRARYIMPORT_GENERATOR_TEST + public +#else + internal +#endif + enum CustomTypeMarshallerKind + { + /// <summary> + /// This custom type marshaller represents a single value. + /// </summary> + Value, + /// <summary> + /// This custom type marshaller represents a container of values that are placed sequentially in memory. + /// </summary> + LinearCollection + } +} diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index 1ea2799beaa..4a0aaf99e23 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -8,17 +8,7 @@ // namespace System.Runtime.InteropServices { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] -#if LIBRARYIMPORT_GENERATOR_TEST - public -#else - internal -#endif - sealed class GeneratedMarshallingAttribute : Attribute - { - } - - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Delegate)] #if LIBRARYIMPORT_GENERATOR_TEST public #else @@ -34,7 +24,7 @@ namespace System.Runtime.InteropServices public Type NativeType { get; } } - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)] #if LIBRARYIMPORT_GENERATOR_TEST public #else @@ -59,20 +49,64 @@ namespace System.Runtime.InteropServices public int ConstantElementCount { get; set; } - public int ElementIndirectionLevel { get; set; } + public int ElementIndirectionDepth { get; set; } public const string ReturnsCountValue = "return-value"; } - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] + /// <summary> + /// Attribute used to indicate that the type can be used to convert a value of the provided <see cref="ManagedType"/> to a native representation. + /// </summary> + /// <remarks> + /// This attribute is recognized by the runtime-provided source generators for source-generated interop scenarios. + /// It is not used by the interop marshalling system at runtime. + /// <seealso cref="LibraryImportAttribute"/> + /// </remarks> + [AttributeUsage(AttributeTargets.Struct)] #if LIBRARYIMPORT_GENERATOR_TEST public #else internal #endif - sealed class GenericContiguousCollectionMarshallerAttribute : Attribute + sealed class CustomTypeMarshallerAttribute : Attribute { - public GenericContiguousCollectionMarshallerAttribute() + public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind marshallerKind = CustomTypeMarshallerKind.Value) + { + ManagedType = managedType; + MarshallerKind = marshallerKind; + } + + /// <summary> + /// The managed type for which the attributed type is a marshaller + /// </summary> + public Type ManagedType { get; } + + /// <summary> + /// The required shape of the attributed type + /// </summary> + public CustomTypeMarshallerKind MarshallerKind { get; } + + /// <summary> + /// When the <see cref="CustomTypeMarshallerFeatures.CallerAllocatedBuffer"/> flag is set on <see cref="Features"/> the size of the caller-allocated buffer in number of elements. + /// </summary> + public int BufferSize { get; set; } + + /// <summary> + /// The marshalling directions this custom type marshaller supports. + /// </summary> + /// <remarks>Default is <see cref="CustomTypeMarshallerDirection.Ref"/></remarks> + public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref; + + /// <summary> + /// The optional features for the <see cref="MarshallerKind"/> that the marshaller supports. + /// </summary> + public CustomTypeMarshallerFeatures Features { get; set; } + + /// <summary> + /// This type is used as a placeholder for the first generic parameter when generic parameters cannot be used + /// to identify the managed type (i.e. when the marshaller type is generic over T and the managed type is T[]) + /// </summary> + public struct GenericPlaceholder { } } diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs index 5278e31ae0e..d5b0a308bc6 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs @@ -6,6 +6,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling { + [CustomTypeMarshaller(typeof(HandleRef), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal struct HandleRefMarshaller { private HandleRef _handle; @@ -15,7 +16,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling _handle = handle; } - public IntPtr Value => _handle.Handle; + public IntPtr ToNativeValue() => _handle.Handle; public void FreeNative() => GC.KeepAlive(_handle.Wrapper); } diff --git a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs index a9b92a3bca4..a4d40ab0cc4 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs +++ b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs @@ -353,6 +353,7 @@ namespace Microsoft.Win32 public CoTaskMemUnicodeSafeHandle Password; public int Flags; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(EvtRpcLogin), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] public struct Marshaller { public struct Native @@ -380,18 +381,16 @@ namespace Microsoft.Win32 _value.Flags = managed.Flags; } - public Native Value + public Native ToNativeValue() => _value; + + public void FromNativeValue(Native value) { - get => _value; - set + // SafeHandle fields cannot change the underlying handle value during marshalling. + if (_value.Password != value.Password) { - // SafeHandle fields cannot change the underlying handle value during marshalling. - if (_value.Password != value.Password) - { - throw new InvalidOperationException(); - } - _value = value; + throw new InvalidOperationException(); } + _value = value; } public EvtRpcLogin ToManaged() @@ -701,6 +700,7 @@ namespace Microsoft.Win32 public uint Type; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(EvtStringVariant), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] [StructLayout(LayoutKind.Explicit)] public struct Native { diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs index 545856dad96..0e01c1e57e2 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs @@ -188,6 +188,7 @@ internal static partial class Interop internal int fwType; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(DOCINFO), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal struct Native { internal int cbSize; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs index c5a728914bc..4817da34d12 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs @@ -98,6 +98,7 @@ namespace System.Drawing.Imaging internal ref int GetPinnableReference() => ref _width; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(BitmapData), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly BitmapData _managed; @@ -108,7 +109,7 @@ namespace System.Drawing.Imaging public ref int GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef<int>() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs index 707ff687216..336d6c68a0f 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs @@ -396,6 +396,7 @@ namespace System.Drawing.Imaging internal ref float GetPinnableReference() => ref _matrix00; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(ColorMatrix), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly ColorMatrix _managed; @@ -406,7 +407,7 @@ namespace System.Drawing.Imaging public ref float GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef<float>() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs index dfce8751ec4..55575087df7 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs @@ -34,6 +34,7 @@ namespace System.Drawing.Imaging internal ref byte GetPinnableReference() => ref Unsafe.As<MetafileType, byte>(ref type); #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(MetafileHeaderEmf), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly MetafileHeaderEmf _managed; @@ -44,7 +45,7 @@ namespace System.Drawing.Imaging public ref byte GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef<byte>() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs index 6825f2d007b..3295b5ca554 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs @@ -55,6 +55,7 @@ namespace System.Drawing.Imaging public int LogicalDpiY; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(MetafileHeaderWmf), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct InPlaceMarshaller { [StructLayout(LayoutKind.Sequential, Pack = 8)] @@ -120,11 +121,9 @@ namespace System.Drawing.Imaging _native.LogicalDpiY = managed.LogicalDpiY; } - public Native Value - { - get => _native; - set => _native = value; - } + public Native ToNativeValue() => _native; + + public void FromNativeValue(Native value) => _native = value; public MetafileHeaderWmf ToManaged() { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs index ab26edfdb1a..1f7d7cdfd70 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs @@ -106,6 +106,7 @@ namespace System.Drawing.Imaging internal ref int GetPinnableReference() => ref _key; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(WmfPlaceableFileHeader), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly WmfPlaceableFileHeader _managed; @@ -116,7 +117,7 @@ namespace System.Drawing.Imaging public ref int GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef<int>() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index 7b894cb803f..f19e664fa21 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -97,6 +97,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal internal string AppName; internal QUIC_EXECUTION_PROFILE ExecutionProfile; + [CustomTypeMarshaller(typeof(RegistrationConfig), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] [StructLayout(LayoutKind.Sequential)] public struct Native { @@ -250,6 +251,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal // TODO: define delegate for AsyncHandler and make proper use of it. internal IntPtr AsyncHandler; + [CustomTypeMarshaller(typeof(CredentialConfig), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] [StructLayout(LayoutKind.Sequential)] public struct Native { diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs index e22c7799ce0..e365435b742 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs @@ -14,25 +14,21 @@ namespace Microsoft.Interop.Analyzers { public const string Prefix = "DLLIMPORTGENANALYZER"; + // Migration from DllImport to LibraryImport + public const string ConvertToLibraryImport = Prefix + "001"; + // ManualTypeMarshalling - public const string BlittableTypeMustBeBlittable = Prefix + "001"; - public const string CannotHaveMultipleMarshallingAttributes = Prefix + "002"; - public const string NativeTypeMustBeNonNull = Prefix + "003"; - public const string NativeTypeMustBeBlittable = Prefix + "004"; + public const string MarshallerTypeMustSpecifyManagedType = Prefix + "002"; + public const string CustomTypeMarshallerAttributeMustBeValid = Prefix + "003"; + public const string InvalidNativeType = Prefix + "004"; public const string GetPinnableReferenceReturnTypeBlittable = Prefix + "005"; - public const string NativeTypeMustBePointerSized = Prefix + "006"; - public const string NativeTypeMustHaveRequiredShape = Prefix + "007"; - public const string ValuePropertyMustHaveSetter = Prefix + "008"; - public const string ValuePropertyMustHaveGetter = Prefix + "009"; - public const string GetPinnableReferenceShouldSupportAllocatingMarshallingFallback = Prefix + "010"; - public const string CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback = Prefix + "011"; - public const string CallerAllocConstructorMustHaveStackBufferSizeConstant = Prefix + "012"; - public const string RefValuePropertyUnsupported = Prefix + "014"; - public const string NativeGenericTypeMustBeClosedOrMatchArity = Prefix + "016"; - public const string MarshallerGetPinnableReferenceRequiresValueProperty = Prefix + "018"; - - // Migration from DllImport to LibraryImport - public const string ConvertToLibraryImport = Prefix + "015"; + public const string CustomMarshallerTypeMustHaveRequiredShape = Prefix + "006"; + public const string CustomMarshallerTypeMustSupportDirection = Prefix + "007"; + public const string ProvidedMethodsNotSpecifiedInShape = Prefix + "008"; + public const string MissingAllocatingMarshallingFallback = Prefix + "009"; + public const string CallerAllocConstructorMustHaveBufferSize = Prefix + "010"; + public const string InvalidSignaturesInMarshallerShape = Prefix + "011"; + public const string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling = Prefix + "012"; } internal static LocalizableResourceString GetResourceString(string resourceName) diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs new file mode 100644 index 00000000000..d9889c941c8 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs @@ -0,0 +1,909 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +using static Microsoft.Interop.Analyzers.AnalyzerDiagnostics; + +namespace Microsoft.Interop.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class CustomTypeMarshallerAnalyzer : DiagnosticAnalyzer + { + private const string Category = "Usage"; + + public const string MissingFeaturesKey = nameof(MissingFeaturesKey); + + public static class MissingMemberNames + { + public const string Key = nameof(MissingMemberNames); + public const char Delimiter = ' '; + + public const string ValueManagedToNativeConstructor = nameof(ValueManagedToNativeConstructor); + public const string ValueCallerAllocatedBufferConstructor = nameof(ValueCallerAllocatedBufferConstructor); + public const string CollectionManagedToNativeConstructor = nameof(CollectionManagedToNativeConstructor); + public const string CollectionCallerAllocatedBufferConstructor = nameof(CollectionCallerAllocatedBufferConstructor); + public const string CollectionNativeElementSizeConstructor = nameof(CollectionNativeElementSizeConstructor); + } + + public static readonly DiagnosticDescriptor MarshallerTypeMustSpecifyManagedTypeRule = + new DiagnosticDescriptor( + Ids.MarshallerTypeMustSpecifyManagedType, + "MarshallerTypeMustSpecifyManagedType", + GetResourceString(nameof(Resources.MarshallerTypeMustSpecifyManagedTypeMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.MarshallerTypeMustSpecifyManagedTypeDescription))); + + public static readonly DiagnosticDescriptor CustomTypeMarshallerAttributeMustBeValidRule = + new DiagnosticDescriptor( + Ids.CustomTypeMarshallerAttributeMustBeValid, + "CustomTypeMarshallerAttributeMustBeValid", + GetResourceString(nameof(Resources.CustomTypeMarshallerAttributeMustBeValidMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CustomTypeMarshallerAttributeMustBeValidDescription))); + + public static readonly DiagnosticDescriptor MarshallerKindMustBeValidRule = + new DiagnosticDescriptor( + Ids.CustomTypeMarshallerAttributeMustBeValid, + "CustomTypeMarshallerAttributeMustBeValid", + GetResourceString(nameof(Resources.MarshallerKindMustBeValidMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.MarshallerKindMustBeValidDescription))); + + public static readonly DiagnosticDescriptor MarshallerDirectionMustBeValidRule = + new DiagnosticDescriptor( + Ids.CustomTypeMarshallerAttributeMustBeValid, + "CustomTypeMarshallerAttributeMustBeValid", + GetResourceString(nameof(Resources.MarshallerDirectionMustBeValidMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.MarshallerDirectionMustBeValidDescription))); + + public static readonly DiagnosticDescriptor NativeTypeMustHaveCustomTypeMarshallerAttributeRule = + new DiagnosticDescriptor( + Ids.InvalidNativeType, + "InvalidNativeType", + GetResourceString(nameof(Resources.NativeTypeMustHaveCustomTypeMarshallerAttributeMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.NativeTypeMustHaveCustomTypeMarshallerAttributeDescription))); + + public static readonly DiagnosticDescriptor NativeTypeMustBeBlittableRule = + new DiagnosticDescriptor( + Ids.InvalidNativeType, + "InvalidNativeType", + GetResourceString(nameof(Resources.NativeTypeMustBeBlittableMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.NativeTypeMustBeBlittableDescription))); + + public static readonly DiagnosticDescriptor GetPinnableReferenceReturnTypeBlittableRule = + new DiagnosticDescriptor( + Ids.GetPinnableReferenceReturnTypeBlittable, + "GetPinnableReferenceReturnTypeBlittable", + GetResourceString(nameof(Resources.GetPinnableReferenceReturnTypeBlittableMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.GetPinnableReferenceReturnTypeBlittableDescription))); + + public static readonly DiagnosticDescriptor NativeTypeMustBePointerSizedRule = + new DiagnosticDescriptor( + Ids.InvalidNativeType, + "InvalidNativeType", + GetResourceString(nameof(Resources.NativeTypeMustBePointerSizedMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.NativeTypeMustBePointerSizedDescription))); + + public static readonly DiagnosticDescriptor CustomMarshallerTypeMustSupportDirectionRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustSupportDirection, + "CustomMarshallerTypeMustSupportDirection", + GetResourceString(nameof(Resources.CustomMarshallerTypeMustSupportDirectionMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CustomMarshallerTypeMustSupportDirectionDescription))); + + public static readonly DiagnosticDescriptor ValueInRequiresOneParameterConstructorRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.ValueInRequiresOneParameterConstructorMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.ValueInRequiresOneParameterConstructorDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionInRequiresTwoParameterConstructorRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionInRequiresTwoParameterConstructorMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionInRequiresTwoParameterConstructorDescription))); + + public static readonly DiagnosticDescriptor ValueInCallerAllocatedBufferRequiresSpanConstructorRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.ValueInCallerAllocatedBufferRequiresSpanConstructorMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.ValueInCallerAllocatedBufferRequiresSpanConstructorDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription))); + + public static readonly DiagnosticDescriptor OutRequiresToManagedRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.OutRequiresToManagedMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.OutRequiresToManagedDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionInRequiresCollectionMethodsRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionInRequiresCollectionMethodsMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionInRequiresCollectionMethodsDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionOutRequiresCollectionMethodsRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionOutRequiresCollectionMethodsMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionOutRequiresCollectionMethodsDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionOutRequiresIntConstructorRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionOutRequiresIntConstructorMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionOutRequiresIntConstructorDescription))); + + public static readonly DiagnosticDescriptor UnmanagedResourcesRequiresFreeNativeRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.UnmanagedResourcesRequiresFreeNativeMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.UnmanagedResourcesRequiresFreeNativeDescription))); + + public static readonly DiagnosticDescriptor OutTwoStageMarshallingRequiresFromNativeValueRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.OutTwoStageMarshallingRequiresFromNativeValueMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.OutTwoStageMarshallingRequiresFromNativeValueDescription))); + + public static readonly DiagnosticDescriptor InTwoStageMarshallingRequiresToNativeValueRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.InTwoStageMarshallingRequiresToNativeValueMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.InTwoStageMarshallingRequiresToNativeValueDescription))); + + public static readonly DiagnosticDescriptor GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule = + new DiagnosticDescriptor( + Ids.MissingAllocatingMarshallingFallback, + "GetPinnableReferenceShouldSupportAllocatingMarshallingFallback", + GetResourceString(nameof(Resources.GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription))); + + public static readonly DiagnosticDescriptor CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule = + new DiagnosticDescriptor( + Ids.MissingAllocatingMarshallingFallback, + "CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback", + GetResourceString(nameof(Resources.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackDescription))); + + public static readonly DiagnosticDescriptor CallerAllocConstructorMustHaveBufferSizeRule = + new DiagnosticDescriptor( + Ids.CallerAllocConstructorMustHaveBufferSize, + "CallerAllocConstructorMustHaveBufferSize", + GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeDescription))); + + public static readonly DiagnosticDescriptor RefNativeValueUnsupportedRule = + new DiagnosticDescriptor( + Ids.InvalidSignaturesInMarshallerShape, + "InvalidSignaturesInMarshallerShape", + GetResourceString(nameof(Resources.RefNativeValueUnsupportedMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.RefNativeValueUnsupportedDescription))); + + public static readonly DiagnosticDescriptor NativeGenericTypeMustBeClosedOrMatchArityRule = + new DiagnosticDescriptor( + Ids.InvalidNativeType, + "NativeGenericTypeMustBeClosedOrMatchArity", + GetResourceString(nameof(Resources.NativeGenericTypeMustBeClosedOrMatchArityMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.NativeGenericTypeMustBeClosedOrMatchArityDescription))); + + public static readonly DiagnosticDescriptor MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule = + new DiagnosticDescriptor( + Ids.MarshallerGetPinnableReferenceRequiresTwoStageMarshalling, + "MarshallerGetPinnableReferenceRequiresTwoStageMarshalling", + GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresTwoStageMarshallingMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription))); + + public static readonly DiagnosticDescriptor FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription))); + + public static readonly DiagnosticDescriptor ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription))); + + public static readonly DiagnosticDescriptor FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription))); + + public static readonly DiagnosticDescriptor CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription))); + + public static readonly DiagnosticDescriptor TwoStageMarshallingNativeTypesMustMatchRule = + new DiagnosticDescriptor( + Ids.InvalidSignaturesInMarshallerShape, + "InvalidSignaturesInMarshallerShape", + GetResourceString(nameof(Resources.TwoStageMarshallingNativeTypesMustMatchMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.TwoStageMarshallingNativeTypesMustMatchDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionElementTypesMustMatchRule = + new DiagnosticDescriptor( + Ids.InvalidSignaturesInMarshallerShape, + "InvalidSignaturesInMarshallerShape", + GetResourceString(nameof(Resources.LinearCollectionElementTypesMustMatchMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionElementTypesMustMatchDescription))); + + public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => + ImmutableArray.Create( + MarshallerTypeMustSpecifyManagedTypeRule, + CustomTypeMarshallerAttributeMustBeValidRule, + MarshallerKindMustBeValidRule, + MarshallerDirectionMustBeValidRule, + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + NativeTypeMustBeBlittableRule, + GetPinnableReferenceReturnTypeBlittableRule, + NativeTypeMustBePointerSizedRule, + ValueInRequiresOneParameterConstructorRule, + LinearCollectionInRequiresTwoParameterConstructorRule, + OutRequiresToManagedRule, + LinearCollectionInRequiresCollectionMethodsRule, + LinearCollectionOutRequiresCollectionMethodsRule, + LinearCollectionOutRequiresIntConstructorRule, + CustomMarshallerTypeMustSupportDirectionRule, + OutTwoStageMarshallingRequiresFromNativeValueRule, + InTwoStageMarshallingRequiresToNativeValueRule, + GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, + CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, + CallerAllocConstructorMustHaveBufferSizeRule, + RefNativeValueUnsupportedRule, + NativeGenericTypeMustBeClosedOrMatchArityRule, + MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule, + FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule, + ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule, + TwoStageMarshallingNativeTypesMustMatchRule, + LinearCollectionElementTypesMustMatchRule); + + public override void Initialize(AnalysisContext context) + { + // Don't analyze generated code + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(PrepareForAnalysis); + } + + private void PrepareForAnalysis(CompilationStartAnalysisContext context) + { + INamedTypeSymbol? spanOfT = context.Compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata); + INamedTypeSymbol? spanOfByte = spanOfT?.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); + INamedTypeSymbol? readOnlySpanOfT = context.Compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata); + INamedTypeSymbol? readOnlySpanOfByte = readOnlySpanOfT?.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); + + if (spanOfT is not null && readOnlySpanOfT is not null) + { + var perCompilationAnalyzer = new PerCompilationAnalyzer(spanOfT, spanOfByte, readOnlySpanOfT, readOnlySpanOfByte); + + // Analyze NativeMarshalling/MarshalUsing for correctness + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeTypeDefinition, SymbolKind.NamedType); + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeElement, SymbolKind.Parameter, SymbolKind.Field); + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeReturnType, SymbolKind.Method); + + // Analyze marshaller type to validate shape. + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeMarshallerType, SymbolKind.NamedType); + } + } + + private class PerCompilationAnalyzer + { + private readonly INamedTypeSymbol _spanOfT; + private readonly INamedTypeSymbol _readOnlySpanOfT; + private readonly INamedTypeSymbol _spanOfByte; + private readonly INamedTypeSymbol _readOnlySpanOfByte; + + public PerCompilationAnalyzer(INamedTypeSymbol spanOfT, INamedTypeSymbol? spanOfByte, INamedTypeSymbol readOnlySpanOfT, INamedTypeSymbol? readOnlySpanOfByte) + { + _spanOfT = spanOfT; + _spanOfByte = spanOfByte; + _readOnlySpanOfT = readOnlySpanOfT; + _readOnlySpanOfByte = readOnlySpanOfByte; + } + + public void AnalyzeTypeDefinition(SymbolAnalysisContext context) + { + INamedTypeSymbol type = (INamedTypeSymbol)context.Symbol; + + (AttributeData? attributeData, INamedTypeSymbol? marshallerType) = ManualTypeMarshallingHelper.GetDefaultMarshallerInfo(type); + + if (attributeData is null) + { + return; + } + + AnalyzeManagedTypeMarshallingInfo(context, type, attributeData, marshallerType); + } + + public void AnalyzeElement(SymbolAnalysisContext context) + { + ITypeSymbol managedType = context.Symbol switch + { + IParameterSymbol param => param.Type, + IFieldSymbol field => field.Type, + _ => throw new InvalidOperationException() + }; + AttributeData? attributeData = context.Symbol.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.MarshalUsingAttribute); + if (attributeData is null || attributeData.ConstructorArguments.Length == 0) + { + return; + } + AnalyzeManagedTypeMarshallingInfo(context, managedType, attributeData, attributeData.ConstructorArguments[0].Value as INamedTypeSymbol); + } + + public void AnalyzeReturnType(SymbolAnalysisContext context) + { + IMethodSymbol method = (IMethodSymbol)context.Symbol; + ITypeSymbol managedType = method.ReturnType; + AttributeData? attributeData = method.GetReturnTypeAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.MarshalUsingAttribute); + if (attributeData is null || attributeData.ConstructorArguments.Length == 0) + { + return; + } + AnalyzeManagedTypeMarshallingInfo(context, managedType, attributeData, attributeData.ConstructorArguments[0].Value as INamedTypeSymbol); + } + + private static void AnalyzeManagedTypeMarshallingInfo(SymbolAnalysisContext context, ITypeSymbol type, AttributeData attributeData, INamedTypeSymbol? marshallerType) + { + if (marshallerType is null) + { + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + type.ToDisplayString())); + return; + } + + if (marshallerType.IsUnboundGenericType) + { + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeGenericTypeMustBeClosedOrMatchArityRule, + marshallerType.ToDisplayString(), + type.ToDisplayString())); + } + + (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? marshallerManagedType, _) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + + marshallerManagedType = ManualTypeMarshallingHelper.ResolveManagedType(marshallerManagedType, marshallerType, context.Compilation); + + if (!hasCustomTypeMarshallerAttribute) + { + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + type.ToDisplayString())); + return; + } + + if (marshallerManagedType is null) + { + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + type.ToDisplayString())); + return; + } + + if (!TypeSymbolsConstructedFromEqualTypes(type, marshallerManagedType)) + { + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + type.ToDisplayString())); + return; + } + } + + private static bool TypeSymbolsConstructedFromEqualTypes(ITypeSymbol left, ITypeSymbol right) + { + return (left, right) switch + { + (INamedTypeSymbol namedLeft, INamedTypeSymbol namedRight) => SymbolEqualityComparer.Default.Equals(namedLeft.ConstructedFrom, namedRight.ConstructedFrom), + _ => SymbolEqualityComparer.Default.Equals(left, right) + }; + } + + public void AnalyzeMarshallerType(SymbolAnalysisContext context) + { + INamedTypeSymbol marshallerType = (INamedTypeSymbol)context.Symbol; + (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? type, CustomTypeMarshallerData? marshallerDataMaybe) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + type = ManualTypeMarshallingHelper.ResolveManagedType(type, marshallerType, context.Compilation); + + if (!hasCustomTypeMarshallerAttribute) + { + return; + } + if (type is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(MarshallerTypeMustSpecifyManagedTypeRule, marshallerType.ToDisplayString())); + return; + } + + if (marshallerDataMaybe is not { } marshallerData) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(CustomTypeMarshallerAttributeMustBeValidRule, marshallerType.ToDisplayString())); + return; + } + + if (!Enum.IsDefined(typeof(CustomTypeMarshallerKind), marshallerData.Kind)) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(MarshallerKindMustBeValidRule, marshallerType.ToDisplayString())); + return; + } + + if (type is INamedTypeSymbol { IsUnboundGenericType: true } generic) + { + if (generic.TypeArguments.Length != marshallerType.TypeArguments.Length) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + NativeGenericTypeMustBeClosedOrMatchArityRule, + marshallerType.ToDisplayString(), + type.ToDisplayString())); + return; + } + type = generic.ConstructedFrom.Construct(marshallerType.TypeArguments, marshallerType.TypeArgumentNullableAnnotations); + } + + IMethodSymbol? inConstructor = null; + IMethodSymbol? callerAllocatedSpanConstructor = null; + IMethodSymbol collectionOutConstructor = null; + foreach (IMethodSymbol ctor in marshallerType.Constructors) + { + if (ctor.IsStatic) + { + continue; + } + + if (inConstructor is null && ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallerData.Kind)) + { + inConstructor = ctor; + } + + if (callerAllocatedSpanConstructor is null && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, marshallerData.Kind)) + { + callerAllocatedSpanConstructor = ctor; + } + if (collectionOutConstructor is null && ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.SpecialType == SpecialType.System_Int32) + { + collectionOutConstructor = ctor; + } + } + + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In)) + { + if (inConstructor is null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + GetInConstructorShapeRule(marshallerData.Kind), + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + GetInConstructorMissingMemberName(marshallerData.Kind)), + marshallerType.ToDisplayString(), + type.ToDisplayString())); + } + if (marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer)) + { + if (callerAllocatedSpanConstructor is null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + GetCallerAllocatedBufferConstructorShapeRule(marshallerData.Kind), + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + GetCallerAllocatedBufferConstructorMissingMemberName(marshallerData.Kind)), + marshallerType.ToDisplayString(), + type.ToDisplayString())); + } + if (marshallerData.BufferSize == null) + { + context.ReportDiagnostic( + (callerAllocatedSpanConstructor ?? (ISymbol)marshallerType).CreateDiagnostic( + CallerAllocConstructorMustHaveBufferSizeRule, + marshallerType.ToDisplayString())); + } + } + else if (callerAllocatedSpanConstructor is not null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.CallerAllocatedBuffer)), + marshallerType.ToDisplayString())); + } + + // Validate that this type can support marshalling when stackalloc is not usable. + if (callerAllocatedSpanConstructor is not null && inConstructor is null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, + marshallerType.ToDisplayString())); + } + } + + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && !ManualTypeMarshallingHelper.HasToManagedMethod(marshallerType, type)) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + OutRequiresToManagedRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + ShapeMemberNames.Value.ToManaged), + marshallerType.ToDisplayString())); + } + + if (marshallerData.Kind == CustomTypeMarshallerKind.LinearCollection) + { + IMethodSymbol? getManagedValuesSourceMethod = ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(marshallerType, _readOnlySpanOfT); + IMethodSymbol? getManagedValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(marshallerType, _spanOfT); + IMethodSymbol? getNativeValuesSourceMethod = ManualTypeMarshallingHelper.FindGetNativeValuesSourceMethod(marshallerType, _readOnlySpanOfByte); + IMethodSymbol? getNativeValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetNativeValuesDestinationMethod(marshallerType, _spanOfByte); + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In) && (getManagedValuesSourceMethod is null || getNativeValuesDestinationMethod is null)) + { + var missingMembers = (getManagedValuesSourceMethod, getNativeValuesDestinationMethod) switch + { + (null, not null) => ShapeMemberNames.LinearCollection.GetManagedValuesSource, + (not null, null) => ShapeMemberNames.LinearCollection.GetNativeValuesDestination, + (null, null) => $"{ShapeMemberNames.LinearCollection.GetManagedValuesSource}{MissingMemberNames.Delimiter}{ShapeMemberNames.LinearCollection.GetNativeValuesDestination}", + (not null, not null) => string.Empty + }; + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + LinearCollectionInRequiresCollectionMethodsRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + missingMembers), + marshallerType.ToDisplayString())); + } + + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && (getNativeValuesSourceMethod is null || getManagedValuesDestinationMethod is null)) + { + var missingMembers = (getNativeValuesSourceMethod, getManagedValuesDestinationMethod) switch + { + (not null, null) => ShapeMemberNames.LinearCollection.GetNativeValuesSource, + (null, not null) => ShapeMemberNames.LinearCollection.GetManagedValuesDestination, + (null, null) => $"{ShapeMemberNames.LinearCollection.GetNativeValuesSource}{MissingMemberNames.Delimiter}{ShapeMemberNames.LinearCollection.GetManagedValuesDestination}", + (not null, not null) => string.Empty + }; + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + LinearCollectionOutRequiresCollectionMethodsRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + missingMembers), + marshallerType.ToDisplayString())); + } + + if (getManagedValuesSourceMethod is not null + && getManagedValuesDestinationMethod is not null + && !SymbolEqualityComparer.Default.Equals( + ((INamedTypeSymbol)getManagedValuesSourceMethod.ReturnType).TypeArguments[0], + ((INamedTypeSymbol)getManagedValuesDestinationMethod.ReturnType).TypeArguments[0])) + { + context.ReportDiagnostic(getManagedValuesSourceMethod.CreateDiagnostic(LinearCollectionElementTypesMustMatchRule)); + } + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && collectionOutConstructor is null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + LinearCollectionOutRequiresIntConstructorRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + MissingMemberNames.CollectionNativeElementSizeConstructor), + marshallerType.ToDisplayString())); + } + } + + + // Validate that the native type has at least one marshalling direction (either managed to native or native to managed) + if ((marshallerData.Direction & CustomTypeMarshallerDirection.Ref) == CustomTypeMarshallerDirection.None) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + CustomMarshallerTypeMustSupportDirectionRule, + marshallerType.ToDisplayString())); + } + + if (marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.UnmanagedResources) && !ManualTypeMarshallingHelper.HasFreeNativeMethod(marshallerType)) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + UnmanagedResourcesRequiresFreeNativeRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + ShapeMemberNames.Value.FreeNative), + marshallerType.ToDisplayString(), + type.ToDisplayString())); + } + else if (!marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.UnmanagedResources) && ManualTypeMarshallingHelper.HasFreeNativeMethod(marshallerType)) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.UnmanagedResources)), + marshallerType.ToDisplayString())); + } + + IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(marshallerType); + IMethodSymbol? fromNativeValueMethod = ManualTypeMarshallingHelper.FindFromNativeValueMethod(marshallerType); + bool toNativeValueMethodIsRefReturn = toNativeValueMethod is { ReturnsByRef: true } or { ReturnsByRefReadonly: true }; + ITypeSymbol nativeType = marshallerType; + + if (marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.TwoStageMarshalling)) + { + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In) && toNativeValueMethod is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic( + InTwoStageMarshallingRequiresToNativeValueRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + ShapeMemberNames.Value.ToNativeValue), + marshallerType.ToDisplayString())); + } + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && fromNativeValueMethod is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic( + OutTwoStageMarshallingRequiresFromNativeValueRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingMemberNames.Key, + ShapeMemberNames.Value.FromNativeValue), + marshallerType.ToDisplayString())); + } + + // ToNativeValue and FromNativeValue must be provided with the same type. + if (toNativeValueMethod is not null + && fromNativeValueMethod is not null + && !SymbolEqualityComparer.Default.Equals(toNativeValueMethod.ReturnType, fromNativeValueMethod.Parameters[0].Type)) + { + context.ReportDiagnostic(toNativeValueMethod.CreateDiagnostic(TwoStageMarshallingNativeTypesMustMatchRule)); + } + } + else if (fromNativeValueMethod is not null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.TwoStageMarshalling)), + marshallerType.ToDisplayString())); + } + else if (toNativeValueMethod is not null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + ImmutableDictionary<string, string>.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.TwoStageMarshalling)), + marshallerType.ToDisplayString())); + } + + if (toNativeValueMethod is not null) + { + if (toNativeValueMethodIsRefReturn) + { + context.ReportDiagnostic( + toNativeValueMethod.CreateDiagnostic( + RefNativeValueUnsupportedRule, + marshallerType.ToDisplayString())); + } + + nativeType = toNativeValueMethod.ReturnType; + } + else if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshallerType) is IMethodSymbol marshallerGetPinnableReferenceMethod) + { + // If we don't have a ToNativeValue method, then we disallow a GetPinnableReference on the marshaler type. + // We do this since there is no valid use case that we can think of for a GetPinnableReference on a blittable type + // being a requirement to calculate the value of the fields of the same blittable instance, + // so we're pre-emptively blocking this until a use case is discovered. + context.ReportDiagnostic( + marshallerGetPinnableReferenceMethod.CreateDiagnostic( + MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule, + nativeType.ToDisplayString())); + } + + if (!nativeType.IsConsideredBlittable()) + { + context.ReportDiagnostic( + (toNativeValueMethod ?? (ISymbol)marshallerType).CreateDiagnostic( + NativeTypeMustBeBlittableRule, + nativeType.ToDisplayString(), + type.ToDisplayString())); + } + + if (SymbolEqualityComparer.Default.Equals(ManualTypeMarshallingHelper.GetDefaultMarshallerInfo(type).marshallerType, marshallerType) + && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is IMethodSymbol managedGetPinnableReferenceMethod) + { + if (!managedGetPinnableReferenceMethod.ReturnType.IsConsideredBlittable()) + { + context.ReportDiagnostic(managedGetPinnableReferenceMethod.CreateDiagnostic(GetPinnableReferenceReturnTypeBlittableRule)); + } + // Validate that our marshaler supports scenarios where GetPinnableReference cannot be used. + if (!marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In)) + { + context.ReportDiagnostic( + type.CreateDiagnostic( + GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, + type.ToDisplayString())); + } + + // If the managed type has a GetPinnableReference method, make sure that the Value getter is also a pointer-sized primitive. + // This ensures that marshalling via pinning the managed value and marshalling via the default marshaller will have the same ABI. + if (toNativeValueMethod is not null + && !toNativeValueMethodIsRefReturn // Ref returns are already reported above as invalid, so don't issue another warning here about them + && nativeType is not ( + IPointerTypeSymbol or + { SpecialType: SpecialType.System_IntPtr } or + { SpecialType: SpecialType.System_UIntPtr })) + { + context.ReportDiagnostic( + toNativeValueMethod.CreateDiagnostic( + NativeTypeMustBePointerSizedRule, + nativeType.ToDisplayString(), + managedGetPinnableReferenceMethod.ContainingType.ToDisplayString())); + } + } + } + + private DiagnosticDescriptor GetInConstructorShapeRule(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => ValueInRequiresOneParameterConstructorRule, + CustomTypeMarshallerKind.LinearCollection => LinearCollectionInRequiresTwoParameterConstructorRule, + _ => throw new UnreachableException() + }; + private string GetInConstructorMissingMemberName(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => MissingMemberNames.ValueManagedToNativeConstructor, + CustomTypeMarshallerKind.LinearCollection => MissingMemberNames.CollectionManagedToNativeConstructor, + _ => throw new UnreachableException() + }; + private DiagnosticDescriptor GetCallerAllocatedBufferConstructorShapeRule(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => ValueInCallerAllocatedBufferRequiresSpanConstructorRule, + CustomTypeMarshallerKind.LinearCollection => LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule, + _ => throw new UnreachableException() + }; + private string GetCallerAllocatedBufferConstructorMissingMemberName(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => MissingMemberNames.ValueCallerAllocatedBufferConstructor, + CustomTypeMarshallerKind.LinearCollection => MissingMemberNames.CollectionCallerAllocatedBufferConstructor, + _ => throw new UnreachableException() + }; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs new file mode 100644 index 00000000000..08cc2550ed4 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs @@ -0,0 +1,404 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.Interop.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] + public class CustomTypeMarshallerFixer : CodeFixProvider + { + private const string AddMissingCustomTypeMarshallerMembersKey = nameof(AddMissingCustomTypeMarshallerMembersKey); + private const string AddMissingCustomTypeMarshallerFeaturesKey = nameof(AddMissingCustomTypeMarshallerFeaturesKey); + + private class CustomFixAllProvider : DocumentBasedFixAllProvider + { + protected override async Task<Document> FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics) + { + SyntaxNode? root = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false); + if (root == null) + return document; + + DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false); + + switch (fixAllContext.CodeActionEquivalenceKey) + { + case AddMissingCustomTypeMarshallerMembersKey: + foreach (IGrouping<TextSpan, Diagnostic> diagnosticsBySpan in diagnostics.GroupBy(d => d.Location.SourceSpan)) + { + SyntaxNode node = root.FindNode(diagnosticsBySpan.Key); + + AddMissingMembers(fixAllContext, editor, diagnosticsBySpan, node); + } + break; + case AddMissingCustomTypeMarshallerFeaturesKey: + foreach (IGrouping<TextSpan, Diagnostic> diagnosticsBySpan in diagnostics.GroupBy(d => d.Location.SourceSpan)) + { + SyntaxNode node = root.FindNode(diagnosticsBySpan.Key); + + await AddMissingFeatures(fixAllContext, editor, diagnosticsBySpan, node).ConfigureAwait(false); + } + break; + default: + break; + } + + return editor.GetChangedDocument(); + } + + private static void AddMissingMembers(FixAllContext fixAllContext, DocumentEditor editor, IEnumerable<Diagnostic> diagnostics, SyntaxNode node) + { + var (missingMemberNames, _) = GetRequiredShapeMissingMemberNames(diagnostics); + ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node); + editor.ReplaceNode( + node, + (node, gen) => + CustomTypeMarshallerFixer.AddMissingMembers( + node, + marshallerType, + missingMemberNames, + editor.SemanticModel.Compilation, + gen)); + } + + private static async Task AddMissingFeatures(FixAllContext fixAllContext, DocumentEditor editor, IEnumerable<Diagnostic> diagnostics, SyntaxNode node) + { + var (featuresToAdd, _) = GetFeaturesToAdd(diagnostics); + ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node); + AttributeData customTypeMarshallerAttribute = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute); + + SyntaxNode attributeSyntax = await customTypeMarshallerAttribute.ApplicationSyntaxReference!.GetSyntaxAsync(fixAllContext.CancellationToken).ConfigureAwait(false); + + editor.ReplaceNode( + attributeSyntax, + (node, gen) => + CustomTypeMarshallerFixer.AddMissingFeatures( + gen.GetName(node), + customTypeMarshallerAttribute, + featuresToAdd, + gen)); + } + } + + public override FixAllProvider? GetFixAllProvider() => new CustomFixAllProvider(); + + public override ImmutableArray<string> FixableDiagnosticIds { get; } = + ImmutableArray.Create( + AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape, + AnalyzerDiagnostics.Ids.MissingAllocatingMarshallingFallback, + AnalyzerDiagnostics.Ids.ProvidedMethodsNotSpecifiedInShape); + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + Document doc = context.Document; + SyntaxNode? root = await doc.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root == null) + return; + + SyntaxNode node = root.FindNode(context.Span); + var (missingMemberNames, missingMembersDiagnostics) = GetRequiredShapeMissingMemberNames(context.Diagnostics); + + if (missingMembersDiagnostics.Count > 0) + { + context.RegisterCodeFix( + CodeAction.Create( + Resources.AddMissingCustomTypeMarshallerMembers, + ct => AddMissingMembers(doc, node, missingMemberNames, ct), + AddMissingCustomTypeMarshallerMembersKey), + missingMembersDiagnostics); + } + + var (featuresToAdd, featuresToAddDiagnostics) = GetFeaturesToAdd(context.Diagnostics); + + if (featuresToAddDiagnostics.Count > 0 && featuresToAdd != CustomTypeMarshallerFeatures.None) + { + context.RegisterCodeFix( + CodeAction.Create( + Resources.AddMissingFeaturesToCustomTypeMarshaller, + ct => AddMissingFeatures(doc, node, featuresToAdd, ct), + AddMissingCustomTypeMarshallerFeaturesKey), + featuresToAddDiagnostics); + } + } + + private static (List<string> missingMembers, List<Diagnostic> fixedDiagnostics) GetRequiredShapeMissingMemberNames(IEnumerable<Diagnostic> diagnostics) + { + List<string> missingMemberNames = new(); + List<Diagnostic> requiredShapeDiagnostics = new(); + foreach (var diagnostic in diagnostics) + { + if (diagnostic.Id == AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape) + { + requiredShapeDiagnostics.Add(diagnostic); + if (diagnostic.Properties.TryGetValue(CustomTypeMarshallerAnalyzer.MissingMemberNames.Key, out string missingMembers)) + { + missingMemberNames.AddRange(missingMembers.Split(CustomTypeMarshallerAnalyzer.MissingMemberNames.Delimiter)); + } + } + } + + return (missingMemberNames, requiredShapeDiagnostics); + } + private static (CustomTypeMarshallerFeatures featuresToAdd, List<Diagnostic> fixedDiagnostics) GetFeaturesToAdd(IEnumerable<Diagnostic> diagnostics) + { + List<Diagnostic> featuresToAddDiagnostics = new(); + CustomTypeMarshallerFeatures featuresToAdd = CustomTypeMarshallerFeatures.None; + foreach (var diagnostic in diagnostics) + { + if (diagnostic.Id == AnalyzerDiagnostics.Ids.ProvidedMethodsNotSpecifiedInShape) + { + featuresToAddDiagnostics.Add(diagnostic); + if (diagnostic.Properties.TryGetValue(CustomTypeMarshallerAnalyzer.MissingFeaturesKey, out string missingFeatures) + && Enum.TryParse(missingFeatures, out CustomTypeMarshallerFeatures featuresValue)) + { + featuresToAdd |= featuresValue; + } + } + } + + return (featuresToAdd, featuresToAddDiagnostics); + } + private static async Task<Document> AddMissingFeatures(Document doc, SyntaxNode node, CustomTypeMarshallerFeatures featuresToAdd, CancellationToken ct) + { + var editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false); + var gen = editor.Generator; + + ISymbol marshallerType = editor.SemanticModel.GetDeclaredSymbol(node, ct); + + AttributeData customTypeMarshallerAttribute = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute); + + SyntaxNode attributeSyntax = await customTypeMarshallerAttribute.ApplicationSyntaxReference!.GetSyntaxAsync(ct).ConfigureAwait(false); + + SyntaxNode updatedDeclaration = AddMissingFeatures(gen.GetName(attributeSyntax), customTypeMarshallerAttribute, featuresToAdd, gen); + + editor.ReplaceNode(attributeSyntax, updatedDeclaration); + + return editor.GetChangedDocument(); + } + + private static SyntaxNode AddMissingFeatures(string attributeName, AttributeData? customTypeMarshallerAttribute, CustomTypeMarshallerFeatures featuresToAdd, SyntaxGenerator gen) + { + SyntaxNode newAttributeSyntax = gen.Attribute(attributeName); + + newAttributeSyntax = gen.AddAttributeArguments(newAttributeSyntax, customTypeMarshallerAttribute.ConstructorArguments.Select(a => gen.AttributeArgument(gen.TypedConstantExpression(a)))); + + CustomTypeMarshallerFeatures newFeaturesValue = featuresToAdd; + int? featuresArgLocation = null; + + newAttributeSyntax = gen.AddAttributeArguments(newAttributeSyntax, customTypeMarshallerAttribute.NamedArguments + .Where((a, i) => + { + if (a.Key == "Features") + { + // Capture the original location of the 'Features' named argument so we can update it "in place". + featuresArgLocation = customTypeMarshallerAttribute.ConstructorArguments.Length + i; + newFeaturesValue |= (CustomTypeMarshallerFeatures)a.Value.Value; + return false; + } + return true; + }) + .Select(a => gen.AttributeArgument(a.Key, gen.TypedConstantExpression(a.Value)))); + + SyntaxNode featureAttributeArgument = gen.AttributeArgument("Features", + gen.GetEnumValueAsFlagsExpression( + customTypeMarshallerAttribute.AttributeClass.GetMembers(ManualTypeMarshallingHelper.CustomMarshallerAttributeFields.Features).OfType<IPropertySymbol>().First().Type, + (int)newFeaturesValue, + includeZeroValueFlags: false)); + + newAttributeSyntax = featuresArgLocation is null + ? gen.AddAttributeArguments(newAttributeSyntax, new[] { featureAttributeArgument }) + : gen.InsertAttributeArguments(newAttributeSyntax, featuresArgLocation.Value, new[] { featureAttributeArgument }); + + return newAttributeSyntax; + } + + private static async Task<Document> AddMissingMembers(Document doc, SyntaxNode node, List<string> missingMemberNames, CancellationToken ct) + { + var editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false); + var gen = editor.Generator; + + SyntaxNode updatedDeclaration = AddMissingMembers(node, (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node, ct), missingMemberNames, editor.SemanticModel.Compilation, gen); + + editor.ReplaceNode(node, updatedDeclaration); + + return editor.GetChangedDocument(); + } + + private static SyntaxNode AddMissingMembers(SyntaxNode node, ITypeSymbol + marshallerType, List<string> missingMemberNames, Compilation compilation, SyntaxGenerator gen) + { + INamedTypeSymbol @byte = compilation.GetSpecialType(SpecialType.System_Byte); + INamedTypeSymbol @object = compilation.GetSpecialType(SpecialType.System_Object); + INamedTypeSymbol spanOfT = compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!; + INamedTypeSymbol spanOfByte = spanOfT.Construct(@byte)!; + INamedTypeSymbol readOnlySpanOfT = compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!; + INamedTypeSymbol readOnlySpanOfByte = readOnlySpanOfT.Construct(@byte)!; + INamedTypeSymbol int32 = compilation.GetSpecialType(SpecialType.System_Int32); + + SyntaxNode updatedDeclaration = node; + + + (_, ITypeSymbol managedType, _) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + + IMethodSymbol? fromNativeValueMethod = ManualTypeMarshallingHelper.FindFromNativeValueMethod(marshallerType); + IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(marshallerType); + IMethodSymbol? getManagedValuesSourceMethod = ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(marshallerType, readOnlySpanOfT); + IMethodSymbol? getManagedValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(marshallerType, spanOfT); + + SyntaxNode[] throwNotImplementedStatements = new[] + { + gen.ThrowStatement(gen.ObjectCreationExpression(gen.DottedName("System.NotImplementedException"))) + }; + + foreach (string missingMemberName in missingMemberNames) + { + switch (missingMemberName) + { + case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueManagedToNativeConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueCallerAllocatedBufferConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), + gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionManagedToNativeConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), + gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionCallerAllocatedBufferConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), + gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte)), + gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionNativeElementSizeConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.ToManaged: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.Value.ToManaged, + returnType: gen.TypeExpression(managedType), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.FreeNative: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(ShapeMemberNames.Value.FreeNative, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.FromNativeValue: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.Value.FromNativeValue, + parameters: new[] + { + gen.ParameterDeclaration("value", + type: gen.TypeExpression(toNativeValueMethod?.ReturnType ?? @byte)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.ToNativeValue: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.Value.ToNativeValue, + returnType: gen.TypeExpression(fromNativeValueMethod?.Parameters[0].Type ?? @byte), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetManagedValuesSource: + INamedTypeSymbol? getManagedValuesDestinationReturnType = (INamedTypeSymbol?)getManagedValuesDestinationMethod?.ReturnType; + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetManagedValuesSource, + returnType: gen.TypeExpression( + readOnlySpanOfT.Construct( + getManagedValuesDestinationReturnType?.TypeArguments[0] ?? @object)), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetNativeValuesDestination: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetNativeValuesDestination, + returnType: gen.TypeExpression(spanOfByte), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetNativeValuesSource: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetNativeValuesSource, + parameters: new[] + { + gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32)) + }, + returnType: gen.TypeExpression(readOnlySpanOfByte), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetManagedValuesDestination: + INamedTypeSymbol? getManagedValuesSourceReturnType = (INamedTypeSymbol?)getManagedValuesSourceMethod?.ReturnType; + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetNativeValuesDestination, + parameters: new[] + { + gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32)) + }, + returnType: gen.TypeExpression( + spanOfT.Construct( + getManagedValuesSourceReturnType?.TypeArguments[0] ?? @object)), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + default: + break; + } + } + + return updatedDeclaration; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs deleted file mode 100644 index 00abe866653..00000000000 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ /dev/null @@ -1,535 +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 System.Collections.Immutable; -using System.Linq; - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -using static Microsoft.Interop.Analyzers.AnalyzerDiagnostics; - -namespace Microsoft.Interop.Analyzers -{ - [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] - public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer - { - private const string Category = "Usage"; - - public static readonly DiagnosticDescriptor BlittableTypeMustBeBlittableRule = - new DiagnosticDescriptor( - Ids.BlittableTypeMustBeBlittable, - "BlittableTypeMustBeBlittable", - GetResourceString(nameof(Resources.BlittableTypeMustBeBlittableMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.BlittableTypeMustBeBlittableDescription))); - - public static readonly DiagnosticDescriptor CannotHaveMultipleMarshallingAttributesRule = - new DiagnosticDescriptor( - Ids.CannotHaveMultipleMarshallingAttributes, - "CannotHaveMultipleMarshallingAttributes", - GetResourceString(nameof(Resources.CannotHaveMultipleMarshallingAttributesMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.CannotHaveMultipleMarshallingAttributesDescription))); - - public static readonly DiagnosticDescriptor NativeTypeMustBeNonNullRule = - new DiagnosticDescriptor( - Ids.NativeTypeMustBeNonNull, - "NativeTypeMustBeNonNull", - GetResourceString(nameof(Resources.NativeTypeMustBeNonNullMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.NativeTypeMustBeNonNullDescription))); - - public static readonly DiagnosticDescriptor NativeTypeMustBeBlittableRule = - new DiagnosticDescriptor( - Ids.NativeTypeMustBeBlittable, - "NativeTypeMustBeBlittable", - GetResourceString(nameof(Resources.NativeTypeMustBeBlittableMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.NativeTypeMustBeBlittableDescription))); - - public static readonly DiagnosticDescriptor GetPinnableReferenceReturnTypeBlittableRule = - new DiagnosticDescriptor( - Ids.GetPinnableReferenceReturnTypeBlittable, - "GetPinnableReferenceReturnTypeBlittable", - GetResourceString(nameof(Resources.GetPinnableReferenceReturnTypeBlittableMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.GetPinnableReferenceReturnTypeBlittableDescription))); - - public static readonly DiagnosticDescriptor NativeTypeMustBePointerSizedRule = - new DiagnosticDescriptor( - Ids.NativeTypeMustBePointerSized, - "NativeTypeMustBePointerSized", - GetResourceString(nameof(Resources.NativeTypeMustBePointerSizedMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.NativeTypeMustBePointerSizedDescription))); - - public static readonly DiagnosticDescriptor NativeTypeMustHaveRequiredShapeRule = - new DiagnosticDescriptor( - Ids.NativeTypeMustHaveRequiredShape, - "NativeTypeMustHaveRequiredShape", - GetResourceString(nameof(Resources.NativeTypeMustHaveRequiredShapeMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.NativeTypeMustHaveRequiredShapeDescription))); - - public static readonly DiagnosticDescriptor CollectionNativeTypeMustHaveRequiredShapeRule = - new DiagnosticDescriptor( - Ids.NativeTypeMustHaveRequiredShape, - "NativeTypeMustHaveRequiredShape", - GetResourceString(nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeDescription))); - - public static readonly DiagnosticDescriptor ValuePropertyMustHaveSetterRule = - new DiagnosticDescriptor( - Ids.ValuePropertyMustHaveSetter, - "ValuePropertyMustHaveSetter", - GetResourceString(nameof(Resources.ValuePropertyMustHaveSetterMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.ValuePropertyMustHaveSetterDescription))); - - public static readonly DiagnosticDescriptor ValuePropertyMustHaveGetterRule = - new DiagnosticDescriptor( - Ids.ValuePropertyMustHaveGetter, - "ValuePropertyMustHaveGetter", - GetResourceString(nameof(Resources.ValuePropertyMustHaveGetterMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.ValuePropertyMustHaveGetterDescription))); - - public static readonly DiagnosticDescriptor GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule = - new DiagnosticDescriptor( - Ids.GetPinnableReferenceShouldSupportAllocatingMarshallingFallback, - "GetPinnableReferenceShouldSupportAllocatingMarshallingFallback", - GetResourceString(nameof(Resources.GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage)), - Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription))); - - public static readonly DiagnosticDescriptor CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule = - new DiagnosticDescriptor( - Ids.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback, - "CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback", - GetResourceString(nameof(Resources.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackMessage)), - Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackDescription))); - - public static readonly DiagnosticDescriptor CallerAllocConstructorMustHaveBufferSizeConstantRule = - new DiagnosticDescriptor( - Ids.CallerAllocConstructorMustHaveStackBufferSizeConstant, - "CallerAllocConstructorMustHaveBufferSizeConstant", - GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeConstantMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeConstantDescription))); - - public static readonly DiagnosticDescriptor RefValuePropertyUnsupportedRule = - new DiagnosticDescriptor( - Ids.RefValuePropertyUnsupported, - "RefValuePropertyUnsupported", - GetResourceString(nameof(Resources.RefValuePropertyUnsupportedMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.RefValuePropertyUnsupportedDescription))); - - public static readonly DiagnosticDescriptor NativeGenericTypeMustBeClosedOrMatchArityRule = - new DiagnosticDescriptor( - Ids.NativeGenericTypeMustBeClosedOrMatchArity, - "NativeGenericTypeMustBeClosedOrMatchArity", - GetResourceString(nameof(Resources.NativeGenericTypeMustBeClosedOrMatchArityMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.NativeGenericTypeMustBeClosedOrMatchArityDescription))); - - public static readonly DiagnosticDescriptor MarshallerGetPinnableReferenceRequiresValuePropertyRule = - new DiagnosticDescriptor( - Ids.MarshallerGetPinnableReferenceRequiresValueProperty, - "MarshallerGetPinnableReferenceRequiresValueProperty", - GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresValuePropertyMessage)), - Category, - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresValuePropertyDescription))); - - public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => - ImmutableArray.Create( - BlittableTypeMustBeBlittableRule, - CannotHaveMultipleMarshallingAttributesRule, - NativeTypeMustBeNonNullRule, - NativeTypeMustBeBlittableRule, - GetPinnableReferenceReturnTypeBlittableRule, - NativeTypeMustBePointerSizedRule, - NativeTypeMustHaveRequiredShapeRule, - CollectionNativeTypeMustHaveRequiredShapeRule, - ValuePropertyMustHaveSetterRule, - ValuePropertyMustHaveGetterRule, - GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, - CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, - CallerAllocConstructorMustHaveBufferSizeConstantRule, - RefValuePropertyUnsupportedRule, - NativeGenericTypeMustBeClosedOrMatchArityRule, - MarshallerGetPinnableReferenceRequiresValuePropertyRule); - - public override void Initialize(AnalysisContext context) - { - // Don't analyze generated code - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - context.RegisterCompilationStartAction(PrepareForAnalysis); - } - - private void PrepareForAnalysis(CompilationStartAnalysisContext context) - { - INamedTypeSymbol? generatedMarshallingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.GeneratedMarshallingAttribute); - INamedTypeSymbol? nativeMarshallingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.NativeMarshallingAttribute); - INamedTypeSymbol? marshalUsingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute); - INamedTypeSymbol? genericContiguousCollectionMarshallerAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.GenericContiguousCollectionMarshallerAttribute); - INamedTypeSymbol? spanOfByte = context.Compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); - - if (generatedMarshallingAttribute is not null - && nativeMarshallingAttribute is not null - && marshalUsingAttribute is not null - && genericContiguousCollectionMarshallerAttribute is not null - && spanOfByte is not null) - { - var perCompilationAnalyzer = new PerCompilationAnalyzer( - generatedMarshallingAttribute, - nativeMarshallingAttribute, - marshalUsingAttribute, - genericContiguousCollectionMarshallerAttribute, - spanOfByte, - context.Compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_StructLayoutAttribute)!); - context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeTypeDefinition(context), SymbolKind.NamedType); - context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeElement(context), SymbolKind.Parameter, SymbolKind.Field); - context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeReturnType(context), SymbolKind.Method); - } - } - - private class PerCompilationAnalyzer - { - private readonly INamedTypeSymbol _generatedMarshallingAttribute; - private readonly INamedTypeSymbol _nativeMarshallingAttribute; - private readonly INamedTypeSymbol _marshalUsingAttribute; - private readonly INamedTypeSymbol _genericContiguousCollectionMarshallerAttribute; - private readonly INamedTypeSymbol _spanOfByte; - private readonly INamedTypeSymbol _structLayoutAttribute; - - public PerCompilationAnalyzer(INamedTypeSymbol generatedMarshallingAttribute, - INamedTypeSymbol nativeMarshallingAttribute, - INamedTypeSymbol marshalUsingAttribute, - INamedTypeSymbol genericContiguousCollectionMarshallerAttribute, - INamedTypeSymbol spanOfByte, - INamedTypeSymbol structLayoutAttribute) - { - _generatedMarshallingAttribute = generatedMarshallingAttribute; - _nativeMarshallingAttribute = nativeMarshallingAttribute; - _marshalUsingAttribute = marshalUsingAttribute; - _genericContiguousCollectionMarshallerAttribute = genericContiguousCollectionMarshallerAttribute; - _spanOfByte = spanOfByte; - _structLayoutAttribute = structLayoutAttribute; - } - - public void AnalyzeTypeDefinition(SymbolAnalysisContext context) - { - INamedTypeSymbol type = (INamedTypeSymbol)context.Symbol; - - foreach (AttributeData attr in type.GetAttributes()) - { - if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, _generatedMarshallingAttribute)) - { - // If the type has the GeneratedMarshallingAttribute, - // we let the source generator handle error checking. - return; - } - else if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, _nativeMarshallingAttribute)) - { - AnalyzeNativeMarshalerType(context, type, attr, isNativeMarshallingAttribute: true); - return; - } - } - } - - public void AnalyzeElement(SymbolAnalysisContext context) - { - AttributeData? attrData = context.Symbol.GetAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(_marshalUsingAttribute, attr.AttributeClass)); - if (attrData is not null) - { - if (context.Symbol is IParameterSymbol param) - { - AnalyzeNativeMarshalerType(context, param.Type, attrData, isNativeMarshallingAttribute: false); - } - else if (context.Symbol is IFieldSymbol field) - { - AnalyzeNativeMarshalerType(context, field.Type, attrData, isNativeMarshallingAttribute: false); - } - } - } - - public void AnalyzeReturnType(SymbolAnalysisContext context) - { - var method = (IMethodSymbol)context.Symbol; - AttributeData? attrData = method.GetReturnTypeAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(_marshalUsingAttribute, attr.AttributeClass)); - if (attrData is not null) - { - AnalyzeNativeMarshalerType(context, method.ReturnType, attrData, isNativeMarshallingAttribute: false); - } - } - - private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymbol type, AttributeData nativeMarshalerAttributeData, bool isNativeMarshallingAttribute) - { - if (nativeMarshalerAttributeData.ConstructorArguments.Length == 0) - { - // This is a MarshalUsing with just count information. - return; - } - - if (nativeMarshalerAttributeData.ConstructorArguments[0].IsNull) - { - context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( - NativeTypeMustBeNonNullRule, - type.ToDisplayString())); - return; - } - - ITypeSymbol nativeType = (ITypeSymbol)nativeMarshalerAttributeData.ConstructorArguments[0].Value!; - ISymbol nativeTypeDiagnosticsTargetSymbol = nativeType; - - if (nativeType is not INamedTypeSymbol marshalerType) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeType, nativeMarshalerAttributeData).CreateDiagnostic( - NativeTypeMustHaveRequiredShapeRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - return; - } - - DiagnosticDescriptor requiredShapeRule = NativeTypeMustHaveRequiredShapeRule; - - ManualTypeMarshallingHelper.NativeTypeMarshallingVariant variant = ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard; - if (marshalerType.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(_genericContiguousCollectionMarshallerAttribute, a.AttributeClass))) - { - variant = ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.ContiguousCollection; - requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; - if (!ManualTypeMarshallingHelper.TryGetManagedValuesProperty(marshalerType, out _) - || !ManualTypeMarshallingHelper.HasNativeValueStorageProperty(marshalerType, _spanOfByte)) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, marshalerType, nativeMarshalerAttributeData).CreateDiagnostic( - requiredShapeRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - return; - } - } - - if (!nativeType.IsValueType) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeType, nativeMarshalerAttributeData).CreateDiagnostic( - requiredShapeRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - return; - } - - if (marshalerType.IsUnboundGenericType) - { - if (!isNativeMarshallingAttribute) - { - context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( - NativeGenericTypeMustBeClosedOrMatchArityRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - return; - } - if (type is not INamedTypeSymbol namedType || marshalerType.TypeArguments.Length != namedType.TypeArguments.Length) - { - context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( - NativeGenericTypeMustBeClosedOrMatchArityRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - return; - } - // Construct the marshaler type around the same type arguments as the managed type. - nativeType = marshalerType = marshalerType.ConstructedFrom.Construct(namedType.TypeArguments, namedType.TypeArgumentNullableAnnotations); - } - - bool hasConstructor = false; - bool hasCallerAllocSpanConstructor = false; - foreach (IMethodSymbol ctor in marshalerType.Constructors) - { - if (ctor.IsStatic) - { - continue; - } - - hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, variant); - - if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, variant)) - { - hasCallerAllocSpanConstructor = true; - IFieldSymbol bufferSizeField = nativeType.GetMembers(ManualTypeMarshallingHelper.BufferSizeFieldName).OfType<IFieldSymbol>().FirstOrDefault(); - if (bufferSizeField is null or { DeclaredAccessibility: not Accessibility.Public } or { IsConst: false } or { Type: not { SpecialType: SpecialType.System_Int32 } }) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, ctor, nativeMarshalerAttributeData).CreateDiagnostic( - CallerAllocConstructorMustHaveBufferSizeConstantRule, - nativeType.ToDisplayString())); - } - } - } - - bool hasToManaged = ManualTypeMarshallingHelper.HasToManagedMethod(marshalerType, type); - - // Validate that the native type has at least one marshalling method (either managed to native or native to managed) - if (!hasConstructor && !hasCallerAllocSpanConstructor && !hasToManaged) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, marshalerType, nativeMarshalerAttributeData).CreateDiagnostic( - requiredShapeRule, - marshalerType.ToDisplayString(), - type.ToDisplayString())); - } - - // Validate that this type can support marshalling when stackalloc is not usable. - if (isNativeMarshallingAttribute && hasCallerAllocSpanConstructor && !hasConstructor) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, marshalerType, nativeMarshalerAttributeData).CreateDiagnostic( - CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, - marshalerType.ToDisplayString())); - } - - IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType); - bool valuePropertyIsRefReturn = valueProperty is { ReturnsByRef: true } or { ReturnsByRefReadonly: true }; - - if (valueProperty is not null) - { - if (valuePropertyIsRefReturn) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, valueProperty, nativeMarshalerAttributeData).CreateDiagnostic( - RefValuePropertyUnsupportedRule, - marshalerType.ToDisplayString())); - } - - nativeType = valueProperty.Type; - nativeTypeDiagnosticsTargetSymbol = valueProperty; - - // Validate that we don't have partial implementations. - // We error if either of the conditions below are partially met but not fully met: - // - a constructor and a Value property getter - // - a ToManaged method and a Value property setter - if ((hasConstructor || hasCallerAllocSpanConstructor) && valueProperty.GetMethod is null) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, valueProperty, nativeMarshalerAttributeData).CreateDiagnostic( - ValuePropertyMustHaveGetterRule, - marshalerType.ToDisplayString())); - } - if (hasToManaged && valueProperty.SetMethod is null) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, valueProperty, nativeMarshalerAttributeData).CreateDiagnostic( - ValuePropertyMustHaveSetterRule, - marshalerType.ToDisplayString())); - } - } - else if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshalerType) is IMethodSymbol marshallerGetPinnableReferenceMethod) - { - // If we don't have a Value property, then we disallow a GetPinnableReference on the marshaler type. - // We do this since there is no valid use case that we can think of for a GetPinnableReference on a blittable type - // being a requirement to calculate the value of the fields of the same blittable instance, - // so we're pre-emptively blocking this until a use case is discovered. - context.ReportDiagnostic( - marshallerGetPinnableReferenceMethod.CreateDiagnostic( - MarshallerGetPinnableReferenceRequiresValuePropertyRule, - nativeType.ToDisplayString())); - } - - if (!nativeType.IsConsideredBlittable()) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeTypeDiagnosticsTargetSymbol, nativeMarshalerAttributeData).CreateDiagnostic( - NativeTypeMustBeBlittableRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - } - - if (isNativeMarshallingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is IMethodSymbol managedGetPinnableReferenceMethod) - { - if (!managedGetPinnableReferenceMethod.ReturnType.IsConsideredBlittable()) - { - context.ReportDiagnostic(managedGetPinnableReferenceMethod.CreateDiagnostic(GetPinnableReferenceReturnTypeBlittableRule)); - } - // Validate that our marshaler supports scenarios where GetPinnableReference cannot be used. - if (isNativeMarshallingAttribute && (!hasConstructor || valueProperty is { GetMethod: null })) - { - context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( - GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, - type.ToDisplayString())); - } - - // If the managed type has a GetPinnableReference method, make sure that the Value getter is also a pointer-sized primitive. - // This ensures that marshalling via pinning the managed value and marshalling via the default marshaller will have the same ABI. - if (!valuePropertyIsRefReturn // Ref returns are already reported above as invalid, so don't issue another warning here about them - && nativeType is not ( - IPointerTypeSymbol or - { SpecialType: SpecialType.System_IntPtr } or - { SpecialType: SpecialType.System_UIntPtr })) - { - IMethodSymbol getPinnableReferenceMethodToMention = managedGetPinnableReferenceMethod; - - context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeTypeDiagnosticsTargetSymbol, nativeMarshalerAttributeData).CreateDiagnostic( - NativeTypeMustBePointerSizedRule, - nativeType.ToDisplayString(), - getPinnableReferenceMethodToMention.ContainingType.ToDisplayString())); - } - } - } - - private ImmutableArray<Location> GetDiagnosticLocations(SymbolAnalysisContext context, ISymbol targetSymbol, AttributeData marshallingAttribute) - { - // If we're using a compilation that references another compilation, the symbol locations can be in source in the wrong compilation, - // which can cause exceptions when reporting diagnostics. Make sure the symbol is defined in the current Compilation's source module before using its locations. - // If the symbol is not defined in the current Compilation's source module, report the diagnostic at the marshalling attribute's location. - if (SymbolEqualityComparer.Default.Equals(context.Compilation.SourceModule, targetSymbol.ContainingModule)) - { - return targetSymbol.Locations; - } - return ImmutableArray.Create(marshallingAttribute.ApplicationSyntaxReference?.GetSyntax().GetLocation() ?? Location.None); - } - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs new file mode 100644 index 00000000000..1bda8e4cc77 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editing; + +namespace Microsoft.Interop.Analyzers +{ + internal static class SyntaxGeneratorExtensions + { + public static SyntaxNode GetEnumValueAsFlagsExpression(this SyntaxGenerator gen, ITypeSymbol enumType, object value, bool includeZeroValueFlags) + { + if (enumType.TypeKind != TypeKind.Enum) + { + throw new ArgumentException(nameof(enumType)); + } + + SpecialType underlyingType = ((INamedTypeSymbol)enumType).EnumUnderlyingType.SpecialType; + + if (!underlyingType.IsIntegralType()) + { + return gen.CastExpression(gen.TypeExpression(underlyingType), gen.LiteralExpression(value)); + } + + ulong valueToMatch = GetNumericValue(value); + + ulong currentlyMatchedFlags = 0; + SyntaxNode? enumValueSyntax = null; + foreach (ISymbol member in enumType.GetMembers()) + { + if (member is IFieldSymbol { HasConstantValue: true } enumValue) + { + ulong fieldNumericValue = GetNumericValue(enumValue.ConstantValue); + if (fieldNumericValue == 0 && !includeZeroValueFlags) + { + continue; + } + if ((fieldNumericValue & valueToMatch) == fieldNumericValue) + { + currentlyMatchedFlags |= fieldNumericValue; + enumValueSyntax = enumValueSyntax is null + ? gen.MemberAccessExpression(gen.TypeExpression(enumType), enumValue.Name) + : gen.BitwiseOrExpression(enumValueSyntax, gen.MemberAccessExpression(gen.TypeExpression(enumType), enumValue.Name)); + } + } + } + + // Unable to represent the value as the enum flags. Just use the literal value cast as the enum type. + if (currentlyMatchedFlags != valueToMatch) + { + return gen.CastExpression(gen.TypeExpression(underlyingType), gen.LiteralExpression(value)); + } + + return enumValueSyntax; + + static ulong GetNumericValue(object value) => value switch + { + byte or ushort or uint or ulong => Convert.ToUInt64(value), + sbyte or short or int or long => (ulong)Convert.ToInt64(value), + _ => throw new UnreachableException() + }; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs index fc0baa4b2d0..ec7a770472a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs @@ -145,7 +145,7 @@ namespace Microsoft.Interop // // [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, // [MarshalUsing(CountElementName="numColumns")] ref int[] numRows, diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs index fcad506c635..5e4628b58dd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs @@ -10,8 +10,8 @@ namespace Microsoft.Interop { using System; - - + + /// <summary> /// A strongly-typed resource class, for looking up localized strings, etc. /// </summary> @@ -23,15 +23,15 @@ namespace Microsoft.Interop { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { - + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } - + /// <summary> /// Returns the cached ResourceManager instance used by this class. /// </summary> @@ -45,7 +45,7 @@ namespace Microsoft.Interop { return resourceMan; } } - + /// <summary> /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. @@ -59,43 +59,61 @@ namespace Microsoft.Interop { resourceCulture = value; } } - + /// <summary> - /// Looks up a localized string similar to A type marked with 'BlittableTypeAttribute' must be blittable.. + /// Looks up a localized string similar to Add missing custom type marshaller members. /// </summary> - internal static string BlittableTypeMustBeBlittableDescription { + internal static string AddMissingCustomTypeMarshallerMembers { get { - return ResourceManager.GetString("BlittableTypeMustBeBlittableDescription", resourceCulture); + return ResourceManager.GetString("AddMissingCustomTypeMarshallerMembers", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to Type '{0}' is marked with 'BlittableTypeAttribute' but is not blittable. + /// Looks up a localized string similar to Add missing features to the 'CustomTypeMarshallerAttribute' attribute. /// </summary> - internal static string BlittableTypeMustBeBlittableMessage { + internal static string AddMissingFeaturesToCustomTypeMarshaller { get { - return ResourceManager.GetString("BlittableTypeMustBeBlittableMessage", resourceCulture); + return ResourceManager.GetString("AddMissingFeaturesToCustomTypeMarshaller", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to When a constructor taking a Span<byte> is specified on the native type, the type must also have a public integer constant named BufferSize to provide the size of the caller-allocated buffer.. + /// Looks up a localized string similar to A marshaller type that provides a constructor taking a caller-allocated 'Span<byte>' should specify that it supports the 'CallerAllocatedBuffer' feature.. /// </summary> - internal static string CallerAllocConstructorMustHaveBufferSizeConstantDescription { + internal static string CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription { get { - return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeConstantDescription", resourceCulture); + return ResourceManager.GetString("CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type '{0}' must have a 'public const int BufferSize' field that specifies the size of the stack buffer because it has a constructor that takes a caller-allocated Span<byte>. + /// Looks up a localized string similar to The type '{0}' provides a constructor taking a caller-allocated 'Span<byte>' but does not specify that it supports the 'CallerAllocatedBuffer' feature. The constructor will not be used unless the feature is specified.. /// </summary> - internal static string CallerAllocConstructorMustHaveBufferSizeConstantMessage { + internal static string CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureMessage { get { - return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeConstantMessage", resourceCulture); + return ResourceManager.GetString("CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureMessage", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to When a constructor taking a 'Span<byte>' is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer.. + /// </summary> + internal static string CallerAllocConstructorMustHaveBufferSizeDescription { + get { + return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The native type '{0}' must set the 'BufferSize' field on the applied 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to specify the size of the caller-allocated buffer because it has a constructor that takes a caller-allocated 'Span<byte>'. + /// </summary> + internal static string CallerAllocConstructorMustHaveBufferSizeMessage { + get { + return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeMessage", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to A type that supports marshalling from managed to native using a caller-allocated buffer should also support marshalling from managed to native where using a caller-allocated buffer is impossible.. /// </summary> @@ -104,7 +122,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Native type '{0}' has a constructor taking a caller-allocated buffer, but does not support marshalling in scenarios where using a caller-allocated buffer is impossible. /// </summary> @@ -113,7 +131,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The generated 'DllImportAttribute' will not have a value corresponding to '{0}'.. /// </summary> @@ -122,7 +140,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("CannotForwardToDllImportDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to '{0}' has no equivalent in 'DllImportAtttribute' and will not be forwarded. /// </summary> @@ -131,7 +149,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("CannotForwardToDllImportMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Specified 'LibraryImportAttribute' arguments cannot be forwarded to 'DllImportAttribute'. /// </summary> @@ -140,43 +158,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("CannotForwardToDllImportTitle", resourceCulture); } } - - /// <summary> - /// Looks up a localized string similar to The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive.. - /// </summary> - internal static string CannotHaveMultipleMarshallingAttributesDescription { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesDescription", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes.. - /// </summary> - internal static string CannotHaveMultipleMarshallingAttributesMessage { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesMessage", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to A native type with the 'GenericContiguousCollectionMarshallerAttribute' must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.. - /// </summary> - internal static string CollectionNativeTypeMustHaveRequiredShapeDescription { - get { - return ResourceManager.GetString("CollectionNativeTypeMustHaveRequiredShapeDescription", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>'. - /// </summary> - internal static string CollectionNativeTypeMustHaveRequiredShapeMessage { - get { - return ResourceManager.GetString("CollectionNativeTypeMustHaveRequiredShapeMessage", resourceCulture); - } - } - + /// <summary> /// Looks up a localized string similar to Source-generated P/Invokes will ignore any configuration that is not supported.. /// </summary> @@ -185,7 +167,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConfigurationNotSupportedDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The '{0}' configuration is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// </summary> @@ -194,7 +176,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConfigurationNotSupportedMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The specified marshalling configuration is not supported by source-generated P/Invokes. {0}.. /// </summary> @@ -203,7 +185,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConfigurationNotSupportedMessageMarshallingInfo", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The specified '{0}' configuration for parameter '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// </summary> @@ -212,7 +194,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConfigurationNotSupportedMessageParameter", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// </summary> @@ -221,7 +203,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConfigurationNotSupportedMessageReturn", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The specified value '{0}' for '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// </summary> @@ -230,7 +212,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConfigurationNotSupportedMessageValue", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Specified configuration is not supported by source-generated P/Invokes.. /// </summary> @@ -239,16 +221,16 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConfigurationNotSupportedTitle", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel'. + /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth'. /// </summary> internal static string ConstantAndElementCountInfoDisallowed { get { return ResourceManager.GetString("ConstantAndElementCountInfoDisallowed", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Automatically converting a P/Invoke with 'PreserveSig' set to 'false' to a source-generated P/Invoke may produce invalid code. /// </summary> @@ -257,7 +239,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConvertNoPreserveSigDllImportToGeneratedMayProduceInvalidCode", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Convert to 'LibraryImport'. /// </summary> @@ -266,7 +248,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConvertToLibraryImport", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time. /// </summary> @@ -275,7 +257,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConvertToLibraryImportDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time. /// </summary> @@ -284,7 +266,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConvertToLibraryImportMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time. /// </summary> @@ -293,7 +275,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConvertToLibraryImportTitle", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.. /// </summary> @@ -302,7 +284,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConvertToLibraryImportWarning", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Convert to 'LibraryImport' with '{0}' suffix. /// </summary> @@ -311,7 +293,43 @@ namespace Microsoft.Interop { return ResourceManager.GetString("ConvertToLibraryImportWithSuffix", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to A native must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum. + /// </summary> + internal static string CustomMarshallerTypeMustSupportDirectionDescription { + get { + return ResourceManager.GetString("CustomMarshallerTypeMustSupportDirectionDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The native type '{0}' must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum. + /// </summary> + internal static string CustomMarshallerTypeMustSupportDirectionMessage { + get { + return ResourceManager.GetString("CustomMarshallerTypeMustSupportDirectionMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The 'CustomTypeMarshallerAttribute' attribute must be semantically valid. + /// </summary> + internal static string CustomTypeMarshallerAttributeMustBeValidDescription { + get { + return ResourceManager.GetString("CustomTypeMarshallerAttributeMustBeValidDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The 'CustomTypeMarshallerAttribute' on '{0}' is semantically invalid. + /// </summary> + internal static string CustomTypeMarshallerAttributeMustBeValidMessage { + get { + return ResourceManager.GetString("CustomTypeMarshallerAttributeMustBeValidMessage", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to The specified parameter needs to be marshalled from managed to native, but the native type '{0}' does not support it.. /// </summary> @@ -320,7 +338,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("CustomTypeMarshallingManagedToNativeUnsupported", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The specified parameter needs to be marshalled from native to managed, but the native type '{0}' does not support it.. /// </summary> @@ -329,7 +347,43 @@ namespace Microsoft.Interop { return ResourceManager.GetString("CustomTypeMarshallingNativeToManagedUnsupported", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to A marshaller type that provides a 'FreeNative' method should specify that it supports the 'UnmanagedResources' feature.. + /// </summary> + internal static string FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription { + get { + return ResourceManager.GetString("FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' provides a 'FreeNative' method but does not specify that it supports the 'UnmanagedResources' feature. The method will not be used unless the feature is specified.. + /// </summary> + internal static string FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureMessage { + get { + return ResourceManager.GetString("FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A marshaller type that provides a 'FromNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature.. + /// </summary> + internal static string FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription { + get { + return ResourceManager.GetString("FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' provides a 'FromNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified.. + /// </summary> + internal static string FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage { + get { + return ResourceManager.GetString("FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to The return type of 'GetPinnableReference' (after accounting for 'ref') must be blittable.. /// </summary> @@ -338,7 +392,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("GetPinnableReferenceReturnTypeBlittableDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The dereferenced type of the return type of the 'GetPinnableReference' method must be blittable. /// </summary> @@ -347,7 +401,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("GetPinnableReferenceReturnTypeBlittableMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to A type that supports marshalling from managed to native by pinning should also support marshalling from managed to native where pinning is impossible.. /// </summary> @@ -356,7 +410,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Type '{0}' has a 'GetPinnableReference' method but its native type does not support marshalling in scenarios where pinning is impossible. /// </summary> @@ -365,7 +419,25 @@ namespace Microsoft.Interop { return ResourceManager.GetString("GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to The 'TwoStageMarshalling' feature requires a 'TNativeType ToNativeValue()' method for the 'In' direction.. + /// </summary> + internal static string InTwoStageMarshallingRequiresToNativeValueDescription { + get { + return ResourceManager.GetString("InTwoStageMarshallingRequiresToNativeValueDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The marshaller type '{0}' supports marshalling in the 'In' direction with the 'TwoStageMarshalling' feature must provide a 'ToNativeValue' instance method that returns the native value. + /// </summary> + internal static string InTwoStageMarshallingRequiresToNativeValueMessage { + get { + return ResourceManager.GetString("InTwoStageMarshallingRequiresToNativeValueMessage", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.. /// </summary> @@ -374,7 +446,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidAttributedMethodContainingTypeMissingModifiersMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic.. /// </summary> @@ -383,7 +455,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidAttributedMethodDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Method '{0}' should be 'static', 'partial', and non-generic when marked with 'LibraryImportAttribute'. P/Invoke source generation will ignore method '{0}'.. /// </summary> @@ -392,7 +464,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidAttributedMethodSignatureMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Invalid 'LibraryImportAttribute' usage. /// </summary> @@ -401,7 +473,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidLibraryImportAttributeUsageTitle", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.. /// </summary> @@ -410,7 +482,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidStringMarshallingConfigurationDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1}. /// </summary> @@ -419,7 +491,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidStringMarshallingConfigurationMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to 'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'.. /// </summary> @@ -428,7 +500,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidStringMarshallingConfigurationMissingCustomType", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to 'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified.. /// </summary> @@ -437,34 +509,187 @@ namespace Microsoft.Interop { return ResourceManager.GetString("InvalidStringMarshallingConfigurationNotCustom", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The use cases for 'GetPinnableReference' are not applicable in any scenarios where a 'Value' property is not also required.. + /// Looks up a localized string similar to The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'.. /// </summary> - internal static string MarshallerGetPinnableReferenceRequiresValuePropertyDescription { + internal static string LinearCollectionElementTypesMustMatchDescription { get { - return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresValuePropertyDescription", resourceCulture); + return ResourceManager.GetString("LinearCollectionElementTypesMustMatchDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless a 'Value' property is also provided. + /// Looks up a localized string similar to The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'. /// </summary> - internal static string MarshallerGetPinnableReferenceRequiresValuePropertyMessage { + internal static string LinearCollectionElementTypesMustMatchMessage { get { - return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresValuePropertyMessage", resourceCulture); + return ResourceManager.GetString("LinearCollectionElementTypesMustMatchMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type '{0}' must be a closed generic so the emitted code can use a specific instantiation.. + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the third parameter. /// </summary> - internal static string NativeGenericTypeMustBeClosedDescription { + internal static string LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription { get { - return ResourceManager.GetString("NativeGenericTypeMustBeClosedDescription", resourceCulture); + return ResourceManager.GetString("LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a three-parameter constructor that takes a '{1}' , a 'Span<byte>', and an 'int'. + /// </summary> + internal static string LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage { + get { + return ResourceManager.GetString("LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports marshalling in the 'In' direction must provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>'.. + /// </summary> + internal static string LinearCollectionInRequiresCollectionMethodsDescription { + get { + return ResourceManager.GetString("LinearCollectionInRequiresCollectionMethodsDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' specifies that is supports marshalling in the 'In' direction, but it does not provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>'. + /// </summary> + internal static string LinearCollectionInRequiresCollectionMethodsMessage { + get { + return ResourceManager.GetString("LinearCollectionInRequiresCollectionMethodsMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A 'LinearCollection'-kind native type must provide a two-parameter constructor taking the managed type as the first parameter and the native size of the element as the second parameter. + /// </summary> + internal static string LinearCollectionInRequiresTwoParameterConstructorDescription { + get { + return ResourceManager.GetString("LinearCollectionInRequiresTwoParameterConstructorDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a two-parameter constructor that takes a '{1}' as the first parameter and an 'int' as the second parameter. + /// </summary> + internal static string LinearCollectionInRequiresTwoParameterConstructorMessage { + get { + return ResourceManager.GetString("LinearCollectionInRequiresTwoParameterConstructorMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>'.. + /// </summary> + internal static string LinearCollectionOutRequiresCollectionMethodsDescription { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresCollectionMethodsDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>'. + /// </summary> + internal static string LinearCollectionOutRequiresCollectionMethodsMessage { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresCollectionMethodsMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a constructor that takes the size of the native element as an 'int'.. + /// </summary> + internal static string LinearCollectionOutRequiresIntConstructorDescription { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresIntConstructorDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a constructor that takes the size of the native element as an 'int'.. + /// </summary> + internal static string LinearCollectionOutRequiresIntConstructorMessage { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresIntConstructorMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The specified marshaller direction must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum.. + /// </summary> + internal static string MarshallerDirectionMustBeValidDescription { + get { + return ResourceManager.GetString("MarshallerDirectionMustBeValidDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The specified custom marshaller direction for '{0}' is invalid. + /// </summary> + internal static string MarshallerDirectionMustBeValidMessage { + get { + return ResourceManager.GetString("MarshallerDirectionMustBeValidMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The use cases for 'GetPinnableReference' are not applicable in any scenarios where 'TwoStageMarshalling' is not also required.. + /// </summary> + internal static string MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription { + get { + return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless the 'TwoStageMarshalling' feature is also supported. + /// </summary> + internal static string MarshallerGetPinnableReferenceRequiresTwoStageMarshallingMessage { + get { + return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresTwoStageMarshallingMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The specified marshaller kind must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum.. + /// </summary> + internal static string MarshallerKindMustBeValidDescription { + get { + return ResourceManager.GetString("MarshallerKindMustBeValidDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The specified custom marshaller kind for '{0}' is invalid. + /// </summary> + internal static string MarshallerKindMustBeValidMessage { + get { + return ResourceManager.GetString("MarshallerKindMustBeValidMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A type with a 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' must specify a managed type. + /// </summary> + internal static string MarshallerTypeMustSpecifyManagedTypeDescription { + get { + return ResourceManager.GetString("MarshallerTypeMustSpecifyManagedTypeDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' does not specify a managed type in the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' applied to the type. + /// </summary> + internal static string MarshallerTypeMustSpecifyManagedTypeMessage { + get { + return ResourceManager.GetString("MarshallerTypeMustSpecifyManagedTypeMessage", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to The native type '{0}' must be a closed generic or have the same number of generic parameters as the managed type so the emitted code can use a specific instantiation.. /// </summary> @@ -473,7 +698,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("NativeGenericTypeMustBeClosedOrMatchArityDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The native type '{0}' for managed type '{1}' must be a closed generic type or have the same arity as the managed type.. /// </summary> @@ -482,106 +707,115 @@ namespace Microsoft.Interop { return ResourceManager.GetString("NativeGenericTypeMustBeClosedOrMatchArityMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to A native type for a given type must be blittable.. + /// Looks up a localized string similar to A native type must be blittable.. /// </summary> internal static string NativeTypeMustBeBlittableDescription { get { return ResourceManager.GetString("NativeTypeMustBeBlittableDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type '{0}' for the type '{1}' is not blittable. + /// Looks up a localized string similar to The native type '{0}' for type '{1}' must be blittable. /// </summary> internal static string NativeTypeMustBeBlittableMessage { get { return ResourceManager.GetString("NativeTypeMustBeBlittableMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to A native type for a given type must be non-null.. + /// Looks up a localized string similar to The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type.. /// </summary> - internal static string NativeTypeMustBeNonNullDescription { + internal static string NativeTypeMustBePointerSizedDescription { get { - return ResourceManager.GetString("NativeTypeMustBeNonNullDescription", resourceCulture); + return ResourceManager.GetString("NativeTypeMustBePointerSizedDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type for the type '{0}' is null. + /// Looks up a localized string similar to The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method. /// </summary> - internal static string NativeTypeMustBeNonNullMessage { + internal static string NativeTypeMustBePointerSizedMessage { get { - return ResourceManager.GetString("NativeTypeMustBeNonNullMessage", resourceCulture); + return ResourceManager.GetString("NativeTypeMustBePointerSizedMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type.. + /// Looks up a localized string similar to A native type for a given type must have the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type.. /// </summary> - internal static string NativeTypeMustBePointerSizedDescription { + internal static string NativeTypeMustHaveCustomTypeMarshallerAttributeDescription { get { - return ResourceManager.GetString("NativeTypeMustBePointerSizedDescription", resourceCulture); + return ResourceManager.GetString("NativeTypeMustHaveCustomTypeMarshallerAttributeDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method. + /// Looks up a localized string similar to The native type for the type '{0}' must be a type with the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type. /// </summary> - internal static string NativeTypeMustBePointerSizedMessage { + internal static string NativeTypeMustHaveCustomTypeMarshallerAttributeMessage { get { - return ResourceManager.GetString("NativeTypeMustBePointerSizedMessage", resourceCulture); + return ResourceManager.GetString("NativeTypeMustHaveCustomTypeMarshallerAttributeMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type must have at least one of the two marshalling methods to enable marshalling the managed type.. + /// Looks up a localized string similar to A 'Value' or 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'ToManaged' method that returns the managed type.. /// </summary> - internal static string NativeTypeMustHaveRequiredShapeDescription { + internal static string OutRequiresToManagedDescription { get { - return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeDescription", resourceCulture); + return ResourceManager.GetString("OutRequiresToManagedDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}'. + /// Looks up a localized string similar to The type '{0}' specifies it supports marshalling in the 'Out' direction, but it does not provide a 'ToManaged' method that returns the managed type. /// </summary> - internal static string NativeTypeMustHaveRequiredShapeMessage { + internal static string OutRequiresToManagedMessage { get { - return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeMessage", resourceCulture); + return ResourceManager.GetString("OutRequiresToManagedMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The 'Value' property must not be a 'ref' or 'readonly ref' property.. + /// Looks up a localized string similar to The 'TwoStageMarshalling' feature requires a 'void FromNativeValue(TNativeType value)' method for the 'Out' direction.. /// </summary> - internal static string RefValuePropertyUnsupportedDescription { + internal static string OutTwoStageMarshallingRequiresFromNativeValueDescription { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedDescription", resourceCulture); + return ResourceManager.GetString("OutTwoStageMarshallingRequiresFromNativeValueDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.. + /// Looks up a localized string similar to The marshaller type '{0}' supports marshalling in the 'Out' direction with the 'TwoStageMarshalling' feature, but it does not provide a 'FromNativeValue' instance method that returns 'void' and takes one parameter.. /// </summary> - internal static string RefValuePropertyUnsupportedMessage { + internal static string OutTwoStageMarshallingRequiresFromNativeValueMessage { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedMessage", resourceCulture); + return ResourceManager.GetString("OutTwoStageMarshallingRequiresFromNativeValueMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to . + /// Looks up a localized string similar to The 'Value' property must not be a 'ref' or 'readonly ref' property.. /// </summary> - internal static string RuntimeMarshallingMustBeDisabled { + internal static string RefNativeValueUnsupportedDescription { get { - return ResourceManager.GetString("RuntimeMarshallingMustBeDisabled", resourceCulture); + return ResourceManager.GetString("RefNativeValueUnsupportedDescription", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.. + /// </summary> + internal static string RefNativeValueUnsupportedMessage { + get { + return ResourceManager.GetString("RefNativeValueUnsupportedMessage", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete.. /// </summary> @@ -590,7 +824,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("SafeHandleByRefMustBeConcrete", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to P/Invoke source generation is not supported on unknown target framework v{0}. The generated source will not be compatible with other frameworks.. /// </summary> @@ -599,7 +833,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TargetFrameworkNotSupportedDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to 'LibraryImportAttribute' cannot be used for source-generated P/Invokes on an unknown target framework v{0}.. /// </summary> @@ -608,7 +842,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TargetFrameworkNotSupportedMessage", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Current target framework is not supported by source-generated P/Invokes. /// </summary> @@ -617,7 +851,43 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TargetFrameworkNotSupportedTitle", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to A marshaller type that provides a 'ToNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature.. + /// </summary> + internal static string ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription { + get { + return ResourceManager.GetString("ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The type '{0}' provides a 'ToNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified.. + /// </summary> + internal static string ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage { + get { + return ResourceManager.GetString("ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same.. + /// </summary> + internal static string TwoStageMarshallingNativeTypesMustMatchDescription { + get { + return ResourceManager.GetString("TwoStageMarshallingNativeTypesMustMatchDescription", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same. + /// </summary> + internal static string TwoStageMarshallingNativeTypesMustMatchMessage { + get { + return ResourceManager.GetString("TwoStageMarshallingNativeTypesMustMatchMessage", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.. /// </summary> @@ -626,7 +896,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TypeNotSupportedDescription", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter '{1}'.. /// </summary> @@ -635,7 +905,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TypeNotSupportedMessageParameter", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to {0} The generated source will not handle marshalling of parameter '{1}'.. /// </summary> @@ -644,7 +914,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TypeNotSupportedMessageParameterWithDetails", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of the return value of method '{1}'.. /// </summary> @@ -653,7 +923,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TypeNotSupportedMessageReturn", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to {0} The generated source will not handle marshalling of the return value of method '{1}'.. /// </summary> @@ -662,7 +932,7 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TypeNotSupportedMessageReturnWithDetails", resourceCulture); } } - + /// <summary> /// Looks up a localized string similar to Specified type is not supported by source-generated P/Invokes. /// </summary> @@ -671,40 +941,58 @@ namespace Microsoft.Interop { return ResourceManager.GetString("TypeNotSupportedTitle", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type's 'Value' property must have a getter to support marshalling from managed to native.. + /// Looks up a localized string similar to The 'UnmanagedResources' feature requires a 'void FreeNative()' method.. /// </summary> - internal static string ValuePropertyMustHaveGetterDescription { + internal static string UnmanagedResourcesRequiresFreeNativeDescription { get { - return ResourceManager.GetString("ValuePropertyMustHaveGetterDescription", resourceCulture); + return ResourceManager.GetString("UnmanagedResourcesRequiresFreeNativeDescription", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must have a getter. + /// Looks up a localized string similar to The marshaller type '{0}' supports marshalling with the 'UnmanagedResources' feature, but it does not provide a parameterless 'FreeNative' instance method that returns 'void'. /// </summary> - internal static string ValuePropertyMustHaveGetterMessage { + internal static string UnmanagedResourcesRequiresFreeNativeMessage { get { - return ResourceManager.GetString("ValuePropertyMustHaveGetterMessage", resourceCulture); + return ResourceManager.GetString("UnmanagedResourcesRequiresFreeNativeMessage", resourceCulture); } } - + /// <summary> - /// Looks up a localized string similar to The native type's 'Value' property must have a setter to support marshalling from native to managed.. + /// Looks up a localized string similar to A 'Value'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a two-parameter constructor taking the managed type and a 'Span<byte>' as parameters. /// </summary> - internal static string ValuePropertyMustHaveSetterDescription { + internal static string ValueInCallerAllocatedBufferRequiresSpanConstructorDescription { get { - return ResourceManager.GetString("ValuePropertyMustHaveSetterDescription", resourceCulture); + return ResourceManager.GetString("ValueInCallerAllocatedBufferRequiresSpanConstructorDescription", resourceCulture); } } - + + /// <summary> + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a one-parameter constructor that takes a '{1}' and 'Span<byte>' as parameters. + /// </summary> + internal static string ValueInCallerAllocatedBufferRequiresSpanConstructorMessage { + get { + return ResourceManager.GetString("ValueInCallerAllocatedBufferRequiresSpanConstructorMessage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to A 'Value'-kind native type must provide a one-parameter constructor taking the managed type as a parameter. + /// </summary> + internal static string ValueInRequiresOneParameterConstructorDescription { + get { + return ResourceManager.GetString("ValueInRequiresOneParameterConstructorDescription", resourceCulture); + } + } + /// <summary> - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must have a setter. + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a one-parameter constructor that takes a '{1}' as a parameter. /// </summary> - internal static string ValuePropertyMustHaveSetterMessage { + internal static string ValueInRequiresOneParameterConstructorMessage { get { - return ResourceManager.GetString("ValuePropertyMustHaveSetterMessage", resourceCulture); + return ResourceManager.GetString("ValueInRequiresOneParameterConstructorMessage", resourceCulture); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx index 1d6467f5daa..cb1b973807d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> <root> - <!-- - Microsoft ResX Schema - + <!-- + Microsoft ResX Schema + Version 2.0 - - The primary goals of this format is to allow a simple XML format - that is mostly human readable. The generation and parsing of the - various data types are done through the TypeConverter classes + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes associated with the data types. - + Example: - + ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> @@ -26,36 +26,36 @@ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> - - There are any number of "resheader" rows that contain simple + + There are any number of "resheader" rows that contain simple name/value pairs. - - Each data row contains a name, and value. The row also contains a - type or mimetype. Type corresponds to a .NET class that support - text/value conversion through the TypeConverter architecture. - Classes that don't support this are serialized and stored with the + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the mimetype set. - - The mimetype is used for serialized objects, and tells the - ResXResourceReader how to depersist the object. This is currently not + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: - - Note - application/x-microsoft.net.object.binary.base64 is the format - that the ResXResourceWriter will generate, however the reader can + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. - + mimetype: application/x-microsoft.net.object.binary.base64 - value : The object must be serialized with + value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. - + mimetype: application/x-microsoft.net.object.soap.base64 - value : The object must be serialized with + value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 - value : The object must be serialized into a byte array + value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> @@ -117,17 +117,11 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> - <data name="BlittableTypeMustBeBlittableDescription" xml:space="preserve"> - <value>A type marked with 'BlittableTypeAttribute' must be blittable.</value> - </data> - <data name="BlittableTypeMustBeBlittableMessage" xml:space="preserve"> - <value>Type '{0}' is marked with 'BlittableTypeAttribute' but is not blittable</value> - </data> - <data name="CallerAllocConstructorMustHaveBufferSizeConstantDescription" xml:space="preserve"> - <value>When a constructor taking a Span<byte> is specified on the native type, the type must also have a public integer constant named BufferSize to provide the size of the caller-allocated buffer.</value> + <data name="CallerAllocConstructorMustHaveBufferSizeDescription" xml:space="preserve"> + <value>When a constructor taking a 'Span<byte>' is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer.</value> </data> - <data name="CallerAllocConstructorMustHaveBufferSizeConstantMessage" xml:space="preserve"> - <value>The native type '{0}' must have a 'public const int BufferSize' field that specifies the size of the stack buffer because it has a constructor that takes a caller-allocated Span<byte></value> + <data name="CallerAllocConstructorMustHaveBufferSizeMessage" xml:space="preserve"> + <value>The native type '{0}' must set the 'BufferSize' field on the applied 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to specify the size of the caller-allocated buffer because it has a constructor that takes a caller-allocated 'Span<byte>'</value> </data> <data name="CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackDescription" xml:space="preserve"> <value>A type that supports marshalling from managed to native using a caller-allocated buffer should also support marshalling from managed to native where using a caller-allocated buffer is impossible.</value> @@ -144,18 +138,6 @@ <data name="CannotForwardToDllImportTitle" xml:space="preserve"> <value>Specified 'LibraryImportAttribute' arguments cannot be forwarded to 'DllImportAttribute'</value> </data> - <data name="CannotHaveMultipleMarshallingAttributesDescription" xml:space="preserve"> - <value>The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive.</value> - </data> - <data name="CannotHaveMultipleMarshallingAttributesMessage" xml:space="preserve"> - <value>Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes.</value> - </data> - <data name="CollectionNativeTypeMustHaveRequiredShapeDescription" xml:space="preserve"> - <value>A native type with the 'GenericContiguousCollectionMarshallerAttribute' must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.</value> - </data> - <data name="CollectionNativeTypeMustHaveRequiredShapeMessage" xml:space="preserve"> - <value>The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>'</value> - </data> <data name="ConfigurationNotSupportedDescription" xml:space="preserve"> <value>Source-generated P/Invokes will ignore any configuration that is not supported.</value> </data> @@ -178,7 +160,7 @@ <value>Specified configuration is not supported by source-generated P/Invokes.</value> </data> <data name="ConstantAndElementCountInfoDisallowed" xml:space="preserve"> - <value>Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel'</value> + <value>Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth'</value> </data> <data name="ConvertNoPreserveSigDllImportToGeneratedMayProduceInvalidCode" xml:space="preserve"> <value>Automatically converting a P/Invoke with 'PreserveSig' set to 'false' to a source-generated P/Invoke may produce invalid code</value> @@ -245,14 +227,23 @@ <data name="InvalidStringMarshallingConfigurationNotCustom" xml:space="preserve"> <value>'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified.</value> </data> - <data name="MarshallerGetPinnableReferenceRequiresValuePropertyDescription" xml:space="preserve"> - <value>The use cases for 'GetPinnableReference' are not applicable in any scenarios where a 'Value' property is not also required.</value> + <data name="MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription" xml:space="preserve"> + <value>The use cases for 'GetPinnableReference' are not applicable in any scenarios where 'TwoStageMarshalling' is not also required.</value> + </data> + <data name="MarshallerGetPinnableReferenceRequiresTwoStageMarshallingMessage" xml:space="preserve"> + <value>The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless the 'TwoStageMarshalling' feature is also supported</value> + </data> + <data name="MarshallerKindMustBeValidDescription" xml:space="preserve"> + <value>The specified marshaller kind must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum.</value> + </data> + <data name="MarshallerKindMustBeValidMessage" xml:space="preserve"> + <value>The specified custom marshaller kind for '{0}' is invalid</value> </data> - <data name="MarshallerGetPinnableReferenceRequiresValuePropertyMessage" xml:space="preserve"> - <value>The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless a 'Value' property is also provided</value> + <data name="MarshallerTypeMustSpecifyManagedTypeDescription" xml:space="preserve"> + <value>A type with a 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' must specify a managed type</value> </data> - <data name="NativeGenericTypeMustBeClosedDescription" xml:space="preserve"> - <value>The native type '{0}' must be a closed generic so the emitted code can use a specific instantiation.</value> + <data name="MarshallerTypeMustSpecifyManagedTypeMessage" xml:space="preserve"> + <value>The type '{0}' does not specify a managed type in the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' applied to the type</value> </data> <data name="NativeGenericTypeMustBeClosedOrMatchArityDescription" xml:space="preserve"> <value>The native type '{0}' must be a closed generic or have the same number of generic parameters as the managed type so the emitted code can use a specific instantiation.</value> @@ -261,16 +252,10 @@ <value>The native type '{0}' for managed type '{1}' must be a closed generic type or have the same arity as the managed type.</value> </data> <data name="NativeTypeMustBeBlittableDescription" xml:space="preserve"> - <value>A native type for a given type must be blittable.</value> + <value>A native type must be blittable.</value> </data> <data name="NativeTypeMustBeBlittableMessage" xml:space="preserve"> - <value>The native type '{0}' for the type '{1}' is not blittable</value> - </data> - <data name="NativeTypeMustBeNonNullDescription" xml:space="preserve"> - <value>A native type for a given type must be non-null.</value> - </data> - <data name="NativeTypeMustBeNonNullMessage" xml:space="preserve"> - <value>The native type for the type '{0}' is null</value> + <value>The native type '{0}' for type '{1}' must be blittable</value> </data> <data name="NativeTypeMustBePointerSizedDescription" xml:space="preserve"> <value>The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type.</value> @@ -278,21 +263,24 @@ <data name="NativeTypeMustBePointerSizedMessage" xml:space="preserve"> <value>The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method</value> </data> - <data name="NativeTypeMustHaveRequiredShapeDescription" xml:space="preserve"> - <value>The native type must have at least one of the two marshalling methods to enable marshalling the managed type.</value> + <data name="NativeTypeMustHaveCustomTypeMarshallerAttributeDescription" xml:space="preserve"> + <value>A native type for a given type must have the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type.</value> + </data> + <data name="NativeTypeMustHaveCustomTypeMarshallerAttributeMessage" xml:space="preserve"> + <value>The native type for the type '{0}' must be a type with the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type</value> + </data> + <data name="CustomMarshallerTypeMustSupportDirectionDescription" xml:space="preserve"> + <value>A native must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum</value> </data> - <data name="NativeTypeMustHaveRequiredShapeMessage" xml:space="preserve"> - <value>The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}'</value> + <data name="CustomMarshallerTypeMustSupportDirectionMessage" xml:space="preserve"> + <value>The native type '{0}' must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum</value> </data> - <data name="RefValuePropertyUnsupportedDescription" xml:space="preserve"> + <data name="RefNativeValueUnsupportedDescription" xml:space="preserve"> <value>The 'Value' property must not be a 'ref' or 'readonly ref' property.</value> </data> - <data name="RefValuePropertyUnsupportedMessage" xml:space="preserve"> + <data name="RefNativeValueUnsupportedMessage" xml:space="preserve"> <value>The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.</value> </data> - <data name="RuntimeMarshallingMustBeDisabled" xml:space="preserve"> - <value /> - </data> <data name="SafeHandleByRefMustBeConcrete" xml:space="preserve"> <value>An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete.</value> </data> @@ -329,16 +317,124 @@ <data name="TypeNotSupportedTitle" xml:space="preserve"> <value>Specified type is not supported by source-generated P/Invokes</value> </data> - <data name="ValuePropertyMustHaveGetterDescription" xml:space="preserve"> - <value>The native type's 'Value' property must have a getter to support marshalling from managed to native.</value> + <data name="InTwoStageMarshallingRequiresToNativeValueDescription" xml:space="preserve"> + <value>The 'TwoStageMarshalling' feature requires a 'TNativeType ToNativeValue()' method for the 'In' direction.</value> + </data> + <data name="InTwoStageMarshallingRequiresToNativeValueMessage" xml:space="preserve"> + <value>The marshaller type '{0}' supports marshalling in the 'In' direction with the 'TwoStageMarshalling' feature must provide a 'ToNativeValue' instance method that returns the native value</value> + </data> + <data name="OutTwoStageMarshallingRequiresFromNativeValueDescription" xml:space="preserve"> + <value>The 'TwoStageMarshalling' feature requires a 'void FromNativeValue(TNativeType value)' method for the 'Out' direction.</value> + </data> + <data name="OutTwoStageMarshallingRequiresFromNativeValueMessage" xml:space="preserve"> + <value>The marshaller type '{0}' supports marshalling in the 'Out' direction with the 'TwoStageMarshalling' feature, but it does not provide a 'FromNativeValue' instance method that returns 'void' and takes one parameter.</value> + </data> + <data name="CustomTypeMarshallerAttributeMustBeValidDescription" xml:space="preserve"> + <value>The 'CustomTypeMarshallerAttribute' attribute must be semantically valid</value> + </data> + <data name="CustomTypeMarshallerAttributeMustBeValidMessage" xml:space="preserve"> + <value>The 'CustomTypeMarshallerAttribute' on '{0}' is semantically invalid</value> + </data> + <data name="LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription" xml:space="preserve"> + <value>A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the third parameter</value> + </data> + <data name="LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage" xml:space="preserve"> + <value>The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a three-parameter constructor that takes a '{1}' , a 'Span<byte>', and an 'int'</value> + </data> + <data name="LinearCollectionInRequiresTwoParameterConstructorDescription" xml:space="preserve"> + <value>A 'LinearCollection'-kind native type must provide a two-parameter constructor taking the managed type as the first parameter and the native size of the element as the second parameter</value> + </data> + <data name="LinearCollectionInRequiresTwoParameterConstructorMessage" xml:space="preserve"> + <value>The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a two-parameter constructor that takes a '{1}' as the first parameter and an 'int' as the second parameter</value> + </data> + <data name="MarshallerDirectionMustBeValidDescription" xml:space="preserve"> + <value>The specified marshaller direction must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum.</value> + </data> + <data name="MarshallerDirectionMustBeValidMessage" xml:space="preserve"> + <value>The specified custom marshaller direction for '{0}' is invalid</value> + </data> + <data name="ValueInCallerAllocatedBufferRequiresSpanConstructorDescription" xml:space="preserve"> + <value>A 'Value'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a two-parameter constructor taking the managed type and a 'Span<byte>' as parameters</value> + </data> + <data name="ValueInCallerAllocatedBufferRequiresSpanConstructorMessage" xml:space="preserve"> + <value>The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a one-parameter constructor that takes a '{1}' and 'Span<byte>' as parameters</value> + </data> + <data name="ValueInRequiresOneParameterConstructorDescription" xml:space="preserve"> + <value>A 'Value'-kind native type must provide a one-parameter constructor taking the managed type as a parameter</value> + </data> + <data name="ValueInRequiresOneParameterConstructorMessage" xml:space="preserve"> + <value>The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a one-parameter constructor that takes a '{1}' as a parameter</value> + </data> + <data name="LinearCollectionInRequiresCollectionMethodsDescription" xml:space="preserve"> + <value>A 'LinearCollection'-kind native type that supports marshalling in the 'In' direction must provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>'.</value> + </data> + <data name="LinearCollectionInRequiresCollectionMethodsMessage" xml:space="preserve"> + <value>The type '{0}' specifies that is supports marshalling in the 'In' direction, but it does not provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>'</value> + </data> + <data name="OutRequiresToManagedDescription" xml:space="preserve"> + <value>A 'Value' or 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'ToManaged' method that returns the managed type.</value> + </data> + <data name="OutRequiresToManagedMessage" xml:space="preserve"> + <value>The type '{0}' specifies it supports marshalling in the 'Out' direction, but it does not provide a 'ToManaged' method that returns the managed type</value> + </data> + <data name="LinearCollectionOutRequiresCollectionMethodsDescription" xml:space="preserve"> + <value>A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>'.</value> + </data> + <data name="LinearCollectionOutRequiresCollectionMethodsMessage" xml:space="preserve"> + <value>The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>'</value> + </data> + <data name="LinearCollectionOutRequiresIntConstructorDescription" xml:space="preserve"> + <value>A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a constructor that takes the size of the native element as an 'int'.</value> + </data> + <data name="LinearCollectionOutRequiresIntConstructorMessage" xml:space="preserve"> + <value>The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a constructor that takes the size of the native element as an 'int'.</value> + </data> + <data name="UnmanagedResourcesRequiresFreeNativeDescription" xml:space="preserve"> + <value>The 'UnmanagedResources' feature requires a 'void FreeNative()' method.</value> + </data> + <data name="UnmanagedResourcesRequiresFreeNativeMessage" xml:space="preserve"> + <value>The marshaller type '{0}' supports marshalling with the 'UnmanagedResources' feature, but it does not provide a parameterless 'FreeNative' instance method that returns 'void'</value> + </data> + <data name="AddMissingCustomTypeMarshallerMembers" xml:space="preserve"> + <value>Add missing custom type marshaller members</value> + </data> + <data name="AddMissingFeaturesToCustomTypeMarshaller" xml:space="preserve"> + <value>Add missing features to the 'CustomTypeMarshallerAttribute' attribute</value> + </data> + <data name="CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription" xml:space="preserve"> + <value>A marshaller type that provides a constructor taking a caller-allocated 'Span<byte>' should specify that it supports the 'CallerAllocatedBuffer' feature.</value> + </data> + <data name="CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureMessage" xml:space="preserve"> + <value>The type '{0}' provides a constructor taking a caller-allocated 'Span<byte>' but does not specify that it supports the 'CallerAllocatedBuffer' feature. The constructor will not be used unless the feature is specified.</value> + </data> + <data name="FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription" xml:space="preserve"> + <value>A marshaller type that provides a 'FreeNative' method should specify that it supports the 'UnmanagedResources' feature.</value> + </data> + <data name="FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureMessage" xml:space="preserve"> + <value>The type '{0}' provides a 'FreeNative' method but does not specify that it supports the 'UnmanagedResources' feature. The method will not be used unless the feature is specified.</value> + </data> + <data name="FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription" xml:space="preserve"> + <value>A marshaller type that provides a 'FromNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature.</value> + </data> + <data name="FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage" xml:space="preserve"> + <value>The type '{0}' provides a 'FromNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified.</value> + </data> + <data name="LinearCollectionElementTypesMustMatchDescription" xml:space="preserve"> + <value>The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'.</value> + </data> + <data name="LinearCollectionElementTypesMustMatchMessage" xml:space="preserve"> + <value>The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'</value> + </data> + <data name="ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription" xml:space="preserve"> + <value>A marshaller type that provides a 'ToNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature.</value> </data> - <data name="ValuePropertyMustHaveGetterMessage" xml:space="preserve"> - <value>The 'Value' property on the native type '{0}' must have a getter</value> + <data name="ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage" xml:space="preserve"> + <value>The type '{0}' provides a 'ToNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified.</value> </data> - <data name="ValuePropertyMustHaveSetterDescription" xml:space="preserve"> - <value>The native type's 'Value' property must have a setter to support marshalling from native to managed.</value> + <data name="TwoStageMarshallingNativeTypesMustMatchDescription" xml:space="preserve"> + <value>The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same.</value> </data> - <data name="ValuePropertyMustHaveSetterMessage" xml:space="preserve"> - <value>The 'Value' property on the native type '{0}' must have a setter</value> + <data name="TwoStageMarshallingNativeTypesMustMatchMessage" xml:space="preserve"> + <value>The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same</value> </data> </root>
\ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs index f1df80186c8..3af27467e7f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs @@ -38,7 +38,7 @@ namespace Microsoft.Interop { SyntaxReference? syntaxReference = attributeData.ApplicationSyntaxReference; Location location = syntaxReference is not null - ? syntaxReference.GetSyntax().GetLocation() + ? syntaxReference.SyntaxTree.GetLocation(syntaxReference.Span) : Location.None; return location.CreateDiagnostic(descriptor, args); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/LinearCollectionElementMarshallingCodeContext.cs index 02d965c9f29..442c3ab55e3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/LinearCollectionElementMarshallingCodeContext.cs @@ -3,8 +3,9 @@ namespace Microsoft.Interop { - internal sealed class ContiguousCollectionElementMarshallingCodeContext : StubCodeContext + internal sealed class LinearCollectionElementMarshallingCodeContext : StubCodeContext { + private readonly string _managedSpanIdentifier; private readonly string _nativeSpanIdentifier; public override bool SingleFrameSpansNativeContext => false; @@ -20,13 +21,15 @@ namespace Microsoft.Interop /// <param name="indexerIdentifier">The indexer in the loop to get the element to marshal from the collection.</param> /// <param name="nativeSpanIdentifier">The identifier of the native value storage cast to the target element type.</param> /// <param name="parentContext">The parent context.</param> - public ContiguousCollectionElementMarshallingCodeContext( + public LinearCollectionElementMarshallingCodeContext( Stage currentStage, + string managedSpanIdentifier, string nativeSpanIdentifier, StubCodeContext parentContext) { CurrentStage = currentStage; IndexerIdentifier = CalculateIndexerIdentifierBasedOnParentContext(parentContext); + _managedSpanIdentifier = managedSpanIdentifier; _nativeSpanIdentifier = nativeSpanIdentifier; ParentContext = parentContext; } @@ -38,9 +41,8 @@ namespace Microsoft.Interop /// <returns>Managed and native identifiers</returns> public override (string managed, string native) GetIdentifiers(TypePositionInfo info) { - (string _, string native) = ParentContext!.GetIdentifiers(info); return ( - $"{native}.ManagedValues[{IndexerIdentifier}]", + $"{_managedSpanIdentifier}[{IndexerIdentifier}]", $"{_nativeSpanIdentifier}[{IndexerIdentifier}]" ); } @@ -55,7 +57,7 @@ namespace Microsoft.Interop int i = 0; while (parentContext is StubCodeContext context) { - if (context is ContiguousCollectionElementMarshallingCodeContext) + if (context is LinearCollectionElementMarshallingCodeContext) { i++; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs index b6343bb5d23..3b190b6ad04 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs @@ -2,39 +2,172 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; namespace Microsoft.Interop { + public readonly record struct CustomTypeMarshallerData(CustomTypeMarshallerKind Kind, CustomTypeMarshallerDirection Direction, CustomTypeMarshallerFeatures Features, int? BufferSize); + + public static class ShapeMemberNames + { + public abstract class Value + { + public const string ToNativeValue = nameof(ToNativeValue); + public const string FromNativeValue = nameof(FromNativeValue); + public const string GetPinnableReference = nameof(GetPinnableReference); + public const string FreeNative = nameof(FreeNative); + public const string ToManaged = nameof(ToManaged); + } + + public abstract class LinearCollection : Value + { + public const string GetManagedValuesDestination = nameof(GetManagedValuesDestination); + public const string GetManagedValuesSource = nameof(GetManagedValuesSource); + public const string GetNativeValuesDestination = nameof(GetNativeValuesDestination); + public const string GetNativeValuesSource = nameof(GetNativeValuesSource); + } + } public static class ManualTypeMarshallingHelper { - public const string ValuePropertyName = "Value"; - public const string GetPinnableReferenceName = "GetPinnableReference"; - public const string BufferSizeFieldName = "BufferSize"; - public const string RequiresStackBufferFieldName = "RequiresStackBuffer"; - public const string ToManagedMethodName = "ToManaged"; - public const string FreeNativeMethodName = "FreeNative"; - public const string ManagedValuesPropertyName = "ManagedValues"; - public const string NativeValueStoragePropertyName = "NativeValueStorage"; - public const string SetUnmarshalledCollectionLengthMethodName = "SetUnmarshalledCollectionLength"; + public static class CustomMarshallerAttributeFields + { + public const string BufferSize = nameof(BufferSize); + public const string Direction = nameof(Direction); + public const string Features = nameof(Features); + } public static class MarshalUsingProperties { - public const string ElementIndirectionLevel = nameof(ElementIndirectionLevel); + public const string ElementIndirectionDepth = nameof(ElementIndirectionDepth); public const string CountElementName = nameof(CountElementName); public const string ConstantElementCount = nameof(ConstantElementCount); } - public enum NativeTypeMarshallingVariant + public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshallerData? kind) GetMarshallerShapeInfo(ITypeSymbol marshallerType) { - Standard, - ContiguousCollection + var attr = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute); + if (attr is null) + { + return (false, null, null); + } + if (attr.ConstructorArguments.Length == 0) + { + return (true, null, null); + } + CustomTypeMarshallerKind kind = CustomTypeMarshallerKind.Value; + ITypeSymbol? managedType = attr.ConstructorArguments[0].Value as ITypeSymbol; + if (attr.ConstructorArguments.Length > 1) + { + if (attr.ConstructorArguments[1].Value is not int i) + { + return (true, managedType, null); + } + kind = (CustomTypeMarshallerKind)i; + } + var namedArguments = attr.NamedArguments.ToImmutableDictionary(); + int? bufferSize = namedArguments.TryGetValue(CustomMarshallerAttributeFields.BufferSize, out TypedConstant bufferSizeConstant) ? bufferSizeConstant.Value as int? : null; + CustomTypeMarshallerDirection direction = namedArguments.TryGetValue(CustomMarshallerAttributeFields.Direction, out TypedConstant directionConstant) ? (CustomTypeMarshallerDirection)directionConstant.Value : CustomTypeMarshallerDirection.Ref; + CustomTypeMarshallerFeatures features = namedArguments.TryGetValue(CustomMarshallerAttributeFields.Features, out TypedConstant featuresConstant) ? (CustomTypeMarshallerFeatures)featuresConstant.Value : CustomTypeMarshallerFeatures.None; + return (true, managedType, new CustomTypeMarshallerData(kind, direction, features, bufferSize)); + } + + /// <summary> + /// Resolve a non-<see cref="INamedTypeSymbol"/> <paramref name="managedType"/> to the correct managed type if <paramref name="marshallerType"/> is generic and <paramref name="managedType"/> is using any placeholder types. + /// </summary> + /// <param name="managedType">The non-named managed type.</param> + /// <param name="marshallerType">The marshaller type.</param> + /// <param name="compilation">The compilation to use to make new type symbols.</param> + /// <returns>The resolved managed type, or <paramref name="managedType"/> if the provided type did not have any placeholders.</returns> + public static ITypeSymbol? ResolveManagedType(ITypeSymbol? managedType, INamedTypeSymbol marshallerType, Compilation compilation) + { + if (managedType is null || !marshallerType.IsGenericType) + { + return managedType; + } + Stack<ITypeSymbol> typeStack = new(); + ITypeSymbol? innerType = managedType; + while (innerType.TypeKind is TypeKind.Array or TypeKind.Pointer) + { + if (innerType is IArrayTypeSymbol array) + { + typeStack.Push(innerType); + innerType = array.ElementType; + } + else if (innerType is IPointerTypeSymbol pointerType) + { + typeStack.Push(innerType); + innerType = pointerType.PointedAtType; + } + } + + if (innerType.ToDisplayString() != TypeNames.CustomTypeMarshallerAttributeGenericPlaceholder) + { + return managedType; + } + + ITypeSymbol resultType = marshallerType.TypeArguments[0]; + + while (typeStack.Count > 0) + { + ITypeSymbol wrapperType = typeStack.Pop(); + if (wrapperType.TypeKind == TypeKind.Pointer) + { + resultType = compilation.CreatePointerTypeSymbol(resultType); + } + else if (wrapperType.TypeKind == TypeKind.Array) + { + IArrayTypeSymbol arrayType = (IArrayTypeSymbol)wrapperType; + if (arrayType.IsSZArray) + { + resultType = compilation.CreateArrayTypeSymbol(resultType, arrayType.Rank); + } + } + } + return resultType; + } + + public static (AttributeData? attribute, INamedTypeSymbol? marshallerType) GetDefaultMarshallerInfo(ITypeSymbol managedType) + { + AttributeData? attr = managedType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.NativeMarshallingAttribute); + if (attr is null) + { + return (attr, null); + } + INamedTypeSymbol? marshallerType = null; + if (attr.ConstructorArguments.Length == 0) + { + return (attr, null); + } + + marshallerType = attr.ConstructorArguments[0].Value as INamedTypeSymbol; + if (managedType is not INamedTypeSymbol namedType || marshallerType is null) + { + return (attr, null); + } + if (namedType.TypeArguments.Length == 0) + { + return (attr, marshallerType); + } + else if (marshallerType.TypeArguments.Length != namedType.TypeArguments.Length) + { + return (attr, null); + } + else if (marshallerType.IsGenericType) + { + // Construct the marshaler type around the same type arguments as the managed type. + return (attr, marshallerType.ConstructedFrom.Construct(namedType.TypeArguments, namedType.TypeArgumentNullableAnnotations)); + } + + return (attr, marshallerType); } public static bool HasToManagedMethod(ITypeSymbol nativeType, ITypeSymbol managedType) { - return nativeType.GetMembers(ToManagedMethodName) + return nativeType.GetMembers(ShapeMemberNames.Value.ToManaged) .OfType<IMethodSymbol>() .Any(m => m.Parameters.IsEmpty && !m.ReturnsByRef @@ -46,9 +179,9 @@ namespace Microsoft.Interop public static bool IsManagedToNativeConstructor( IMethodSymbol ctor, ITypeSymbol managedType, - NativeTypeMarshallingVariant variant) + CustomTypeMarshallerKind variant) { - if (variant == NativeTypeMarshallingVariant.ContiguousCollection) + if (variant == CustomTypeMarshallerKind.LinearCollection) { return ctor.Parameters.Length == 2 && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type) @@ -62,9 +195,9 @@ namespace Microsoft.Interop IMethodSymbol ctor, ITypeSymbol managedType, ITypeSymbol spanOfByte, - NativeTypeMarshallingVariant variant) + CustomTypeMarshallerKind variant) { - if (variant == NativeTypeMarshallingVariant.ContiguousCollection) + if (variant == CustomTypeMarshallerKind.LinearCollection) { return ctor.Parameters.Length == 3 && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type) @@ -82,66 +215,81 @@ namespace Microsoft.Interop // fixed statement. We aren't supporting a GetPinnableReference extension method // (which is apparently supported in the compiler). // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/pattern-based-fixed - return type.GetMembers(GetPinnableReferenceName) + return type.GetMembers(ShapeMemberNames.Value.GetPinnableReference) .OfType<IMethodSymbol>() - .FirstOrDefault(m => m is { Parameters: { Length: 0 } } and + .FirstOrDefault(m => m is { Parameters.Length: 0 } and ({ ReturnsByRef: true } or { ReturnsByRefReadonly: true })); } - public static IPropertySymbol? FindValueProperty(ITypeSymbol type) + public static bool HasFreeNativeMethod(ITypeSymbol type) { - return type.GetMembers(ValuePropertyName) - .OfType<IPropertySymbol>() - .FirstOrDefault(p => !p.IsStatic); + return type.GetMembers(ShapeMemberNames.Value.FreeNative) + .OfType<IMethodSymbol>() + .Any(m => m is { IsStatic: false, Parameters.Length: 0, ReturnType.SpecialType: SpecialType.System_Void }); } - public static bool HasFreeNativeMethod(ITypeSymbol type) + public static IMethodSymbol? FindToNativeValueMethod(ITypeSymbol type) { - return type.GetMembers(FreeNativeMethodName) + return type.GetMembers(ShapeMemberNames.Value.ToNativeValue) .OfType<IMethodSymbol>() - .Any(m => m is { IsStatic: false, Parameters: { Length: 0 }, ReturnType: { SpecialType: SpecialType.System_Void } }); + .FirstOrDefault(m => m is { IsStatic: false, Parameters.Length: 0 }); } - public static bool TryGetManagedValuesProperty(ITypeSymbol type, out IPropertySymbol managedValuesProperty) + public static IMethodSymbol? FindFromNativeValueMethod(ITypeSymbol type) { - managedValuesProperty = type - .GetMembers(ManagedValuesPropertyName) - .OfType<IPropertySymbol>() - .FirstOrDefault(p => p is { IsStatic: false, GetMethod: not null, ReturnsByRef: false, ReturnsByRefReadonly: false }); - return managedValuesProperty is not null; + return type.GetMembers(ShapeMemberNames.Value.FromNativeValue) + .OfType<IMethodSymbol>() + .FirstOrDefault(m => m is { IsStatic: false, Parameters.Length: 1, ReturnType.SpecialType: SpecialType.System_Void }); } - public static bool TryGetElementTypeFromContiguousCollectionMarshaller(ITypeSymbol type, out ITypeSymbol elementType) + public static bool TryGetElementTypeFromLinearCollectionMarshaller(ITypeSymbol type, ITypeSymbol readOnlySpanOfT, out ITypeSymbol elementType) { - if (!TryGetManagedValuesProperty(type, out IPropertySymbol managedValuesProperty)) + if (FindGetManagedValuesSourceMethod(type, readOnlySpanOfT) is not IMethodSymbol managedValuesSourceMethod) { elementType = null!; return false; } - elementType = ((INamedTypeSymbol)managedValuesProperty.Type).TypeArguments[0]; + elementType = ((INamedTypeSymbol)managedValuesSourceMethod.ReturnType).TypeArguments[0]; return true; } - public static bool HasSetUnmarshalledCollectionLengthMethod(ITypeSymbol type) + public static IMethodSymbol? FindGetManagedValuesSourceMethod(ITypeSymbol type, ITypeSymbol readOnlySpanOfT) { - return type.GetMembers(SetUnmarshalledCollectionLengthMethodName) + return type + .GetMembers(ShapeMemberNames.LinearCollection.GetManagedValuesSource) .OfType<IMethodSymbol>() - .Any(m => m is - { - IsStatic: false, - Parameters: { Length: 1 }, - ReturnType: { SpecialType: SpecialType.System_Void } - } && m.Parameters[0].Type.SpecialType == SpecialType.System_Int32); + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 0, ReturnType: INamedTypeSymbol { ConstructedFrom: INamedTypeSymbol returnType } } + && SymbolEqualityComparer.Default.Equals(returnType, readOnlySpanOfT)); } - public static bool HasNativeValueStorageProperty(ITypeSymbol type, ITypeSymbol spanOfByte) + public static IMethodSymbol? FindGetManagedValuesDestinationMethod(ITypeSymbol type, ITypeSymbol spanOfT) { return type - .GetMembers(NativeValueStoragePropertyName) - .OfType<IPropertySymbol>() - .Any(p => p is { IsStatic: false, GetMethod: not null, ReturnsByRef: false, ReturnsByRefReadonly: false } - && SymbolEqualityComparer.Default.Equals(p.Type, spanOfByte)); + .GetMembers(ShapeMemberNames.LinearCollection.GetManagedValuesDestination) + .OfType<IMethodSymbol>() + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 1, ReturnType: INamedTypeSymbol { ConstructedFrom: INamedTypeSymbol returnType } } + && m.Parameters[0].Type.SpecialType == SpecialType.System_Int32 + && SymbolEqualityComparer.Default.Equals(returnType, spanOfT)); + } + + public static IMethodSymbol? FindGetNativeValuesDestinationMethod(ITypeSymbol type, ITypeSymbol spanOfByte) + { + return type + .GetMembers(ShapeMemberNames.LinearCollection.GetNativeValuesDestination) + .OfType<IMethodSymbol>() + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 0, ReturnType: INamedTypeSymbol returnType } + && SymbolEqualityComparer.Default.Equals(returnType, spanOfByte)); + } + + public static IMethodSymbol? FindGetNativeValuesSourceMethod(ITypeSymbol type, ITypeSymbol readOnlySpanOfByte) + { + return type + .GetMembers(ShapeMemberNames.LinearCollection.GetNativeValuesSource) + .OfType<IMethodSymbol>() + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 1, ReturnType: INamedTypeSymbol returnType } + && m.Parameters[0].Type.SpecialType == SpecialType.System_Int32 + && SymbolEqualityComparer.Default.Equals(returnType, readOnlySpanOfByte)); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs index 7b28fa14f2a..f3aac5de6a7 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -47,8 +48,8 @@ namespace Microsoft.Interop return info.MarshallingAttributeInfo switch { NativeMarshallingAttributeInfo marshalInfo when Options.RuntimeMarshallingDisabled => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), - NativeMarshallingAttributeInfo { ValuePropertyType: SpecialTypeInfo specialType } marshalInfo when specialType.SpecialType.IsAlwaysBlittable() => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), - NativeMarshallingAttributeInfo { ValuePropertyType: PointerTypeInfo } marshalInfo => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), + NativeMarshallingAttributeInfo { NativeValueType: SpecialTypeInfo specialType } marshalInfo when specialType.SpecialType.IsAlwaysBlittable() => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), + NativeMarshallingAttributeInfo { NativeValueType: PointerTypeInfo } marshalInfo => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), UnmanagedBlittableMarshallingInfo when Options.RuntimeMarshallingDisabled => s_blittable, UnmanagedBlittableMarshallingInfo or NativeMarshallingAttributeInfo when !Options.RuntimeMarshallingDisabled => throw new MarshallingNotSupportedException(info, context) @@ -93,7 +94,7 @@ namespace Microsoft.Interop for (int i = 0; i < numIndirectionLevels; i++) { - if (marshallingInfo is NativeContiguousCollectionMarshallingInfo collectionInfo) + if (marshallingInfo is NativeLinearCollectionMarshallingInfo collectionInfo) { type = collectionInfo.ElementType; marshallingInfo = collectionInfo.ElementMarshallingInfo; @@ -129,7 +130,7 @@ namespace Microsoft.Interop while (currentContext is not null) { - if (currentContext is ContiguousCollectionElementMarshallingCodeContext collectionContext) + if (currentContext is LinearCollectionElementMarshallingCodeContext collectionContext) { indexerStack.Push(collectionContext.IndexerIdentifier); } @@ -157,30 +158,34 @@ namespace Microsoft.Interop ICustomNativeTypeMarshallingStrategy marshallingStrategy = new SimpleCustomNativeTypeMarshalling(marshalInfo.NativeMarshallingType.Syntax); - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0) + if ((marshalInfo.MarshallingFeatures & CustomTypeMarshallerFeatures.CallerAllocatedBuffer) != 0) { - marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy); + if (marshalInfo.BufferSize is null) + { + throw new MarshallingNotSupportedException(info, context); + } + marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy, marshalInfo.BufferSize.Value); } - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.FreeNativeResources) != 0) + if ((marshalInfo.MarshallingFeatures & CustomTypeMarshallerFeatures.UnmanagedResources) != 0) { marshallingStrategy = new FreeNativeCleanupStrategy(marshallingStrategy); } // Collections have extra configuration, so handle them here. - if (marshalInfo is NativeContiguousCollectionMarshallingInfo collectionMarshallingInfo) + if (marshalInfo is NativeLinearCollectionMarshallingInfo collectionMarshallingInfo) { return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, marshallingStrategy); } - if (marshalInfo.ValuePropertyType is not null) + if (marshalInfo.NativeValueType is not null) { - marshallingStrategy = DecorateWithValuePropertyStrategy(marshalInfo, marshallingStrategy); + marshallingStrategy = DecorateWithTwoStageMarshallingStrategy(marshalInfo, marshallingStrategy); } IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) + if (marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType)) { return new PinnableManagedValueMarshaller(marshallingGenerator); } @@ -193,7 +198,7 @@ namespace Microsoft.Interop // The marshalling method for this type doesn't support marshalling from native to managed, // but our scenario requires marshalling from native to managed. if ((info.RefKind == RefKind.Ref || info.RefKind == RefKind.Out || info.IsManagedReturnPosition) - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeToManaged) == 0) + && !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.Out)) { throw new MarshallingNotSupportedException(info, context) { @@ -203,8 +208,10 @@ namespace Microsoft.Interop // The marshalling method for this type doesn't support marshalling from managed to native by value, // but our scenario requires marshalling from managed to native by value. else if (!info.IsByRef - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0 - && (context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & (CustomMarshallingFeatures.ManagedTypePinning | CustomMarshallingFeatures.ManagedToNativeStackalloc)) == 0)) + && context.SingleFrameSpansNativeContext + && !(marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType) + || marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer) + || marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In))) { throw new MarshallingNotSupportedException(info, context) { @@ -215,8 +222,8 @@ namespace Microsoft.Interop // but our scenario requires marshalling from managed to native by reference. // "in" byref supports stack marshalling. else if (info.RefKind == RefKind.In - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0 - && !(context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0)) + && !(context.SingleFrameSpansNativeContext && marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer)) + && !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In)) { throw new MarshallingNotSupportedException(info, context) { @@ -226,8 +233,9 @@ namespace Microsoft.Interop // The marshalling method for this type doesn't support marshalling from managed to native by reference, // but our scenario requires marshalling from managed to native by reference. // "ref" byref marshalling doesn't support stack marshalling + // The "Out" direction for "ref" was checked above else if (info.RefKind == RefKind.Ref - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0) + && !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In)) { throw new MarshallingNotSupportedException(info, context) { @@ -236,57 +244,57 @@ namespace Microsoft.Interop } } - private ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller) + private ICustomNativeTypeMarshallingStrategy DecorateWithTwoStageMarshallingStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller) { - TypeSyntax valuePropertyTypeSyntax = marshalInfo.ValuePropertyType!.Syntax; + TypeSyntax valuePropertyTypeSyntax = marshalInfo.NativeValueType!.Syntax; - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeTypePinning) != 0) + if (marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.NativeType) && marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.TwoStageMarshalling)) { return new PinnableMarshallerTypeMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); } - return new CustomNativeTypeWithValuePropertyMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); + return new CustomNativeTypeWithToFromNativeValueMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); } private IMarshallingGenerator CreateNativeCollectionMarshaller( TypePositionInfo info, StubCodeContext context, - NativeContiguousCollectionMarshallingInfo collectionInfo, + NativeLinearCollectionMarshallingInfo collectionInfo, ICustomNativeTypeMarshallingStrategy marshallingStrategy) { var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex }; IMarshallingGenerator elementMarshaller = _elementMarshallingGenerator.Create( elementInfo, - new ContiguousCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context)); + new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, string.Empty, context)); TypeSyntax elementType = elementMarshaller.AsNativeType(elementInfo); + + ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)); + if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) + { + // In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here. + numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, collectionInfo.ElementCountInfo, context); + } + bool isBlittable = elementMarshaller is BlittableMarshaller; if (isBlittable) { - marshallingStrategy = new ContiguousBlittableElementCollectionMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); + marshallingStrategy = new LinearCollectionWithBlittableElementsMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax, numElementsExpression); } else { - marshallingStrategy = new ContiguousNonBlittableElementCollectionMarshalling(marshallingStrategy, elementMarshaller, elementInfo); + marshallingStrategy = new LinearCollectionWithNonBlittableElementsMarshalling(marshallingStrategy, elementMarshaller, elementInfo, numElementsExpression); } // Explicitly insert the Value property handling here (before numElements handling) so that the numElements handling will be emitted before the Value property handling in unmarshalling. - if (collectionInfo.ValuePropertyType is not null) - { - marshallingStrategy = DecorateWithValuePropertyStrategy(collectionInfo, marshallingStrategy); - } - - ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)); - if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) + if (collectionInfo.NativeValueType is not null) { - // In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here. - numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, collectionInfo.ElementCountInfo, context); + marshallingStrategy = DecorateWithTwoStageMarshallingStrategy(collectionInfo, marshallingStrategy); } - marshallingStrategy = new NumElementsExpressionMarshalling( + marshallingStrategy = new SizeOfElementMarshalling( marshallingStrategy, - numElementsExpression, SizeOfExpression(elementType)); if (collectionInfo.UseDefaultMarshalling && info.ManagedType is SzArrayType) @@ -299,7 +307,7 @@ namespace Microsoft.Interop IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); - if ((collectionInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) + if (collectionInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType)) { return new PinnableManagedValueMarshaller(marshallingGenerator); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs index 2160d9f1be3..cb617994e3b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs @@ -89,7 +89,7 @@ namespace Microsoft.Interop InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.ToManagedMethodName))))); + IdentifierName(ShapeMemberNames.Value.ToManaged))))); } public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context) @@ -111,9 +111,9 @@ namespace Microsoft.Interop /// <summary> /// A context that redefines the 'native' identifier for a TypePositionInfo to be the marshaller identifier. /// </summary> - internal class CustomNativeTypeWithValuePropertyStubContext : StubCodeContext + internal class CustomNativeTypeWithToFromNativeValueContext : StubCodeContext { - public CustomNativeTypeWithValuePropertyStubContext(StubCodeContext parentContext) + public CustomNativeTypeWithToFromNativeValueContext(StubCodeContext parentContext) { ParentContext = parentContext; CurrentStage = parentContext.CurrentStage; @@ -130,22 +130,22 @@ namespace Microsoft.Interop } /// <summary> - /// Marshaller that enables support of a Value property on a native type. + /// Marshaller that enables support of ToNativeValue/FromNativeValue methods on a native type. /// </summary> - internal sealed class CustomNativeTypeWithValuePropertyMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class CustomNativeTypeWithToFromNativeValueMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; - private readonly TypeSyntax _valuePropertyType; + private readonly TypeSyntax _nativeValueTypeSyntax; - public CustomNativeTypeWithValuePropertyMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax valuePropertyType) + public CustomNativeTypeWithToFromNativeValueMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax nativeValueTypeSyntax) { _innerMarshaller = innerMarshaller; - _valuePropertyType = valuePropertyType; + _nativeValueTypeSyntax = nativeValueTypeSyntax; } public TypeSyntax AsNativeType(TypePositionInfo info) { - return _valuePropertyType; + return _nativeValueTypeSyntax; } public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) @@ -155,14 +155,14 @@ namespace Microsoft.Interop public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); // When temporary state does not live across stages, the marshaller state is uninitialized // in any stage other than Marshal and Unmarshal. So, we need to reinitialize it here in Cleanup // from the native value so we can safely run any cleanup functionality in the marshaller. if (!context.AdditionalTemporaryStateLivesAcrossStages) { - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, subContext)) @@ -173,41 +173,42 @@ namespace Microsoft.Interop public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); foreach (StatementSyntax statement in _innerMarshaller.GenerateMarshalStatements(info, subContext, nativeTypeConstructorArguments)) { yield return statement; } - // <nativeIdentifier> = <marshalerIdentifier>.Value; + // <nativeIdentifier> = <marshalerIdentifier>.ToNativeValue(); yield return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(context.GetIdentifiers(info).native), - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)))); + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(subContext.GetIdentifiers(info).native), + IdentifierName(ShapeMemberNames.Value.ToNativeValue)), + ArgumentList()))); } - private StatementSyntax GenerateValuePropertyAssignment(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithValuePropertyStubContext subContext) + private StatementSyntax GenerateFromNativeValueInvocation(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithToFromNativeValueContext subContext) { - // <marshalerIdentifier>.Value = <nativeIdentifier>; + // <marshalerIdentifier>.FromNativeValue(<nativeIdentifier>); return ExpressionStatement( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, + InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)), - IdentifierName(context.GetIdentifiers(info).native))); + IdentifierName(ShapeMemberNames.Value.FromNativeValue)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(context.GetIdentifiers(info).native)))))); } public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) { - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, subContext)) @@ -218,13 +219,13 @@ namespace Microsoft.Interop public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); return _innerMarshaller.GetNativeTypeConstructorArguments(info, subContext); } public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); yield return LocalDeclarationStatement( VariableDeclaration( _innerMarshaller.AsNativeType(info), @@ -240,7 +241,7 @@ namespace Microsoft.Interop public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); return _innerMarshaller.GeneratePinStatements(info, subContext); } } @@ -251,10 +252,12 @@ namespace Microsoft.Interop internal sealed class StackallocOptimizationMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; + private readonly int _bufferSize; - public StackallocOptimizationMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller) + public StackallocOptimizationMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, int bufferSize) { _innerMarshaller = innerMarshaller; + _bufferSize = bufferSize; } public TypeSyntax AsNativeType(TypePositionInfo info) @@ -271,7 +274,7 @@ namespace Microsoft.Interop { if (StackAllocOptimizationValid(info, context)) { - // byte* <managedIdentifier>__stackptr = stackalloc byte[<_nativeLocalType>.BufferSize]; + // byte* <managedIdentifier>__stackptr = stackalloc byte[<_bufferSize>]; yield return LocalDeclarationStatement( VariableDeclaration( PointerType(PredefinedType(Token(SyntaxKind.ByteKeyword))), @@ -282,9 +285,7 @@ namespace Microsoft.Interop ArrayType( PredefinedType(Token(SyntaxKind.ByteKeyword)), SingletonList(ArrayRankSpecifier(SingletonSeparatedList<ExpressionSyntax>( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - AsNativeType(info), - IdentifierName(ManualTypeMarshallingHelper.BufferSizeFieldName)) + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(_bufferSize)) )))))))))); } @@ -336,9 +337,7 @@ namespace Microsoft.Interop ArgumentList(SeparatedList(new ArgumentSyntax[] { Argument(IdentifierName(GetStackAllocPointerIdentifier(info, context))), - Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - AsNativeType(info), - IdentifierName(ManualTypeMarshallingHelper.BufferSizeFieldName))) + Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(_bufferSize))) })))); } } @@ -378,7 +377,7 @@ namespace Microsoft.Interop InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(context.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.FreeNativeMethodName)))); + IdentifierName(ShapeMemberNames.Value.FreeNative)))); } public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments) @@ -413,17 +412,17 @@ namespace Microsoft.Interop } /// <summary> - /// Marshaller that calls the GetPinnableReference method on the marshaller value and enables support for the Value property. + /// Marshaller that calls the GetPinnableReference method on the marshaller value and enables support for the ToNativeValue and FromNativeValue methods. /// </summary> internal sealed class PinnableMarshallerTypeMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; - private readonly TypeSyntax _valuePropertyType; + private readonly TypeSyntax _nativeValueType; - public PinnableMarshallerTypeMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax valuePropertyType) + public PinnableMarshallerTypeMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax nativeValueType) { _innerMarshaller = innerMarshaller; - _valuePropertyType = valuePropertyType; + _nativeValueType = nativeValueType; } private bool CanPinMarshaller(TypePositionInfo info, StubCodeContext context) @@ -433,17 +432,17 @@ namespace Microsoft.Interop public TypeSyntax AsNativeType(TypePositionInfo info) { - return _valuePropertyType; + return _nativeValueType; } public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); if (!context.AdditionalTemporaryStateLivesAcrossStages) { // <marshalerIdentifier>.Value = <nativeIdentifier>; - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, subContext)) @@ -454,46 +453,48 @@ namespace Microsoft.Interop public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); foreach (StatementSyntax statement in _innerMarshaller.GenerateMarshalStatements(info, subContext, nativeTypeConstructorArguments)) { yield return statement; } if (!CanPinMarshaller(info, context)) - yield return GenerateNativeAssignmentFromValueProperty(info, context, subContext); + yield return GenerateToNativeValueInvocation(info, context, subContext); } - private static StatementSyntax GenerateNativeAssignmentFromValueProperty(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithValuePropertyStubContext subContext) + private static StatementSyntax GenerateToNativeValueInvocation(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithToFromNativeValueContext subContext) { - // <nativeIdentifier> = <marshalerIdentifier>.Value; + // <nativeIdentifier> = <marshalerIdentifier>.ToNativeValue(); return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(context.GetIdentifiers(info).native), - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)))); + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(subContext.GetIdentifiers(info).native), + IdentifierName(ShapeMemberNames.Value.ToNativeValue)), + ArgumentList()))); } public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) { // fixed (<_nativeTypeSyntax> <ignoredIdentifier> = &<marshalerIdentifier>) // <assignment to Value property> - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); yield return FixedStatement( VariableDeclaration( - _valuePropertyType, + _nativeValueType, SingletonSeparatedList( VariableDeclarator(Identifier(context.GetAdditionalIdentifier(info, "ignored"))) .WithInitializer(EqualsValueClause( IdentifierName(subContext.GetIdentifiers(info).native))))), - GenerateNativeAssignmentFromValueProperty(info, context, subContext)); + GenerateToNativeValueInvocation(info, context, subContext)); } public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); yield return LocalDeclarationStatement( VariableDeclaration( _innerMarshaller.AsNativeType(info), @@ -507,26 +508,25 @@ namespace Microsoft.Interop } } - private StatementSyntax GenerateValuePropertyAssignment(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithValuePropertyStubContext subContext) + private StatementSyntax GenerateFromNativeValueInvocation(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithToFromNativeValueContext subContext) { - // <marshalerIdentifier>.Value = <nativeIdentifier>; + // <marshalerIdentifier>.FromNativeValue(<nativeIdentifier>); return ExpressionStatement( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, + InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)), - IdentifierName(context.GetIdentifiers(info).native))); + IdentifierName(ShapeMemberNames.Value.FromNativeValue)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(context.GetIdentifiers(info).native)))))); } public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) { // <marshalerIdentifier>.Value = <nativeIdentifier>; - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, subContext)) @@ -537,7 +537,7 @@ namespace Microsoft.Interop public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); return _innerMarshaller.GetNativeTypeConstructorArguments(info, subContext); } @@ -548,18 +548,16 @@ namespace Microsoft.Interop } /// <summary> - /// Marshaller that enables support for native types with the constructor variants that take a sizeOfElement int parameter and that have a SetUnmarshalledCollectionLength method. + /// Marshaller that enables support for native types with the constructor variants that take a sizeOfElement int parameter. /// </summary> - internal sealed class NumElementsExpressionMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class SizeOfElementMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; - private readonly ExpressionSyntax _numElementsExpression; private readonly ExpressionSyntax _sizeOfElementExpression; - public NumElementsExpressionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, ExpressionSyntax numElementsExpression, ExpressionSyntax sizeOfElementExpression) + public SizeOfElementMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, ExpressionSyntax sizeOfElementExpression) { _innerMarshaller = innerMarshaller; - _numElementsExpression = numElementsExpression; _sizeOfElementExpression = sizeOfElementExpression; } @@ -611,17 +609,6 @@ namespace Microsoft.Interop IdentifierName(marshalerIdentifier), ImplicitObjectCreationExpression().AddArgumentListArguments(Argument(_sizeOfElementExpression)))); } - - if (info.IsByRef || !info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) - { - yield return ExpressionStatement( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(marshalerIdentifier), - IdentifierName(ManualTypeMarshallingHelper.SetUnmarshalledCollectionLengthMethodName))) - .AddArgumentListArguments(Argument(_numElementsExpression))); - } } public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) @@ -658,17 +645,19 @@ namespace Microsoft.Interop } /// <summary> - /// Marshaller that enables support for marshalling blittable elements of a contiguous collection via a native type that implements the contiguous collection marshalling spec. + /// Marshaller that enables support for marshalling blittable elements of a collection via a native type that implements the LinearCollection marshalling spec. /// </summary> - internal sealed class ContiguousBlittableElementCollectionMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class LinearCollectionWithBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly TypeSyntax _elementType; + private readonly ExpressionSyntax _numElementsExpression; - public ContiguousBlittableElementCollectionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType) + public LinearCollectionWithBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType, ExpressionSyntax numElementsExpression) { _innerMarshaller = innerMarshaller; _elementType = elementType; + _numElementsExpression = numElementsExpression; } public TypeSyntax AsNativeType(TypePositionInfo info) @@ -695,15 +684,17 @@ namespace Microsoft.Interop yield break; } - // <nativeIdentifier>.ManagedValues.CopyTo(MemoryMarshal.Cast<byte, <elementType>>(<nativeIdentifier>.NativeValueStorage)); + // <nativeIdentifier>.GetManagedValuesSource().CopyTo(MemoryMarshal.Cast<byte, <elementType>>(<nativeIdentifier>.GetNativeValuesDestination())); yield return ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.ManagedValuesPropertyName)), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList()), IdentifierName("CopyTo"))) .AddArgumentListArguments( Argument( @@ -723,10 +714,12 @@ namespace Microsoft.Interop }))))) .AddArgumentListArguments( Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName))))))); + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesDestination)), + ArgumentList())))))); } public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) @@ -742,7 +735,13 @@ namespace Microsoft.Interop public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { string nativeIdentifier = context.GetIdentifiers(info).native; - // MemoryMarshal.Cast<byte, <elementType>>(<nativeIdentifier>.NativeValueStorage).CopyTo(<nativeIdentifier>.ManagedValues); + string numElementsIdentifier = context.GetAdditionalIdentifier(info, "numElements"); + yield return LocalDeclarationStatement( + VariableDeclaration( + PredefinedType(Token(SyntaxKind.IntKeyword)), + SingletonSeparatedList( + VariableDeclarator(numElementsIdentifier).WithInitializer(EqualsValueClause(_numElementsExpression))))); + // MemoryMarshal.Cast<byte, <elementType>>(<nativeIdentifier>.GetNativeValuesSource(<numElements>)).CopyTo(<nativeIdentifier>.GetManagedValuesDestination(<numElements>)); yield return ExpressionStatement( InvocationExpression( MemberAccessExpression( @@ -763,17 +762,21 @@ namespace Microsoft.Interop }))))) .AddArgumentListArguments( Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName)))), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesSource)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier))))))), IdentifierName("CopyTo"))) .AddArgumentListArguments( Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.ManagedValuesPropertyName))))); + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesDestination)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier)))))))); foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, context)) { @@ -793,24 +796,27 @@ namespace Microsoft.Interop } /// <summary> - /// Marshaller that enables support for marshalling non-blittable elements of a contiguous collection via a native type that implements the contiguous collection marshalling spec. + /// Marshaller that enables support for marshalling non-blittable elements of a collection via a native type that implements the LinearCollection marshalling spec. /// </summary> - internal sealed class ContiguousNonBlittableElementCollectionMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class LinearCollectionWithNonBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly IMarshallingGenerator _elementMarshaller; private readonly TypePositionInfo _elementInfo; + private readonly ExpressionSyntax _numElementsExpression; - public ContiguousNonBlittableElementCollectionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, + public LinearCollectionWithNonBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, IMarshallingGenerator elementMarshaller, - TypePositionInfo elementInfo) + TypePositionInfo elementInfo, + ExpressionSyntax numElementsExpression) { _innerMarshaller = innerMarshaller; _elementMarshaller = elementMarshaller; _elementInfo = elementInfo; + _numElementsExpression = numElementsExpression; } - private LocalDeclarationStatementSyntax GenerateNativeSpanDeclaration(TypePositionInfo info, StubCodeContext context) + private LocalDeclarationStatementSyntax GenerateNativeValuesDestinationDeclaration(TypePositionInfo info, StubCodeContext context) { string nativeIdentifier = context.GetIdentifiers(info).native; string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); @@ -838,28 +844,105 @@ namespace Microsoft.Interop _elementMarshaller.AsNativeType(_elementInfo).GetCompatibleGenericTypeParameterSyntax() }))))) .AddArgumentListArguments( - Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + Argument( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesDestination)), + ArgumentList())))))))); + } + + private LocalDeclarationStatementSyntax GenerateNativeValuesSourceDeclaration(TypePositionInfo info, StubCodeContext context, string numElementsIdentifier) + { + string nativeIdentifier = context.GetIdentifiers(info).native; + string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); + return LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_ReadOnlySpan), + TypeArgumentList( + SingletonSeparatedList(_elementMarshaller.AsNativeType(_elementInfo).GetCompatibleGenericTypeParameterSyntax())) + ), + SingletonSeparatedList( + VariableDeclarator(Identifier(nativeSpanIdentifier)) + .WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), + GenericName( + Identifier("Cast")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList( + new[] + { + PredefinedType(Token(SyntaxKind.ByteKeyword)), + _elementMarshaller.AsNativeType(_elementInfo).GetCompatibleGenericTypeParameterSyntax() + }))))) + .AddArgumentListArguments( + Argument( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesSource)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier)))))))))))); + } + + private LocalDeclarationStatementSyntax GeneratedManagedValuesSourceDeclaration(TypePositionInfo info, StubCodeContext context) + { + string nativeIdentifier = context.GetIdentifiers(info).native; + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); + return LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_ReadOnlySpan), + TypeArgumentList( + SingletonSeparatedList(_elementInfo.ManagedType.Syntax)) + ), + SingletonSeparatedList( + VariableDeclarator(Identifier(managedSpanIdentifier)) + .WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName))))))))); + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList())))))); } - private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo info, StubCodeContext context, bool useManagedSpanForLength) + private LocalDeclarationStatementSyntax GeneratedManagedValuesDestinationDeclaration(TypePositionInfo info, StubCodeContext context, string numElementsIdentifier) { string nativeIdentifier = context.GetIdentifiers(info).native; + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); + return LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList( + SingletonSeparatedList(_elementInfo.ManagedType.Syntax)) + ), + SingletonSeparatedList( + VariableDeclarator(Identifier(managedSpanIdentifier)) + .WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesDestination)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier)))))))))); + } + + private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo info, StubCodeContext context, ExpressionSyntax lengthExpression) + { + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); - var elementSetupSubContext = new ContiguousCollectionElementMarshallingCodeContext( + var elementSetupSubContext = new LinearCollectionElementMarshallingCodeContext( StubCodeContext.Stage.Setup, + managedSpanIdentifier, nativeSpanIdentifier, context); - var elementSubContext = new ContiguousCollectionElementMarshallingCodeContext( + var elementSubContext = new LinearCollectionElementMarshallingCodeContext( context.CurrentStage, + managedSpanIdentifier, nativeSpanIdentifier, context); - string collectionIdentifierForLength = useManagedSpanForLength - ? $"{nativeIdentifier}.{ManualTypeMarshallingHelper.ManagedValuesPropertyName}" - : nativeSpanIdentifier; - TypePositionInfo localElementInfo = _elementInfo with { InstanceIdentifier = info.InstanceIdentifier, @@ -883,10 +966,8 @@ namespace Microsoft.Interop } // Iterate through the elements of the native collection to unmarshal them - return Block( - GenerateNativeSpanDeclaration(info, context), - MarshallerHelpers.GetForLoop(collectionIdentifierForLength, elementSubContext.IndexerIdentifier) - .WithStatement(marshallingStatement)); + return MarshallerHelpers.GetForLoop(lengthExpression, elementSubContext.IndexerIdentifier) + .WithStatement(marshallingStatement); } return EmptyStatement(); } @@ -898,7 +979,18 @@ namespace Microsoft.Interop public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: false); + StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context, + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)), + IdentifierName("Length"))); + + if (!contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) + { + yield return Block( + GenerateNativeValuesDestinationDeclaration(info, context), + contentsCleanupStatements); + } + foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context)) { yield return statement; @@ -916,21 +1008,29 @@ namespace Microsoft.Interop { // If the parameter is marshalled by-value [Out], then we don't marshal the contents of the collection. // We do clear the span, so that if the invoke target doesn't fill it, we aren't left with undefined content. - // <nativeIdentifier>.NativeValueStorage.Clear(); + // <nativeIdentifier>.GetNativeValuesDestination().Clear(); string nativeIdentifier = context.GetIdentifiers(info).native; yield return ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName)), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesDestination)), + ArgumentList()), IdentifierName("Clear")))); yield break; } - yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: true); + yield return Block( + GeneratedManagedValuesSourceDeclaration(info, context), + GenerateNativeValuesDestinationDeclaration(info, context), + GenerateContentsMarshallingStatement(info, context, + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(MarshallerHelpers.GetManagedSpanIdentifier(info, context)), + IdentifierName("Length")))); } public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) @@ -943,9 +1043,101 @@ namespace Microsoft.Interop return _innerMarshaller.GenerateSetupStatements(info, context); } + private StatementSyntax GenerateByValueUnmarshalStatement(TypePositionInfo info, StubCodeContext context) + { + // Use ManagedSource and NativeDestination spans for by-value marshalling since we're just marshalling back the contents, + // not the array itself. + // This code is ugly since we're now enforcing readonly safety with ReadOnlySpan for all other scenarios, + // but this is an uncommon case so we don't want to design the API around enabling just it. + var (_, nativeIdentifier) = context.GetIdentifiers(info); + string numElementsIdentifier = context.GetAdditionalIdentifier(info, "numElements"); + // int <numElements> = <nativeIdentifier>.GetManagedValuesSource().Length; + LocalDeclarationStatementSyntax numElementsDeclaration = LocalDeclarationStatement( + VariableDeclaration( + PredefinedType(Token(SyntaxKind.IntKeyword)), + SingletonSeparatedList( + VariableDeclarator(numElementsIdentifier).WithInitializer(EqualsValueClause( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList()), + IdentifierName("Length"))))))); + + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); + // Span<TElement> <managedSpan> = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(ref <nativeIdentifier>.GetManagedValuesSource().GetPinnableReference(), <numElements>)); + LocalDeclarationStatementSyntax managedValuesDeclaration = LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList( + SingletonSeparatedList(_elementInfo.ManagedType.Syntax)) + ), + SingletonSeparatedList(VariableDeclarator(managedSpanIdentifier).WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), + IdentifierName("CreateSpan"))) + .WithArgumentList( + ArgumentList( + SeparatedList( + new[] + { + Argument( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe), + IdentifierName("AsRef")), + ArgumentList(SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList()), + IdentifierName("GetPinnableReference")), + ArgumentList())) + .WithRefKindKeyword( + Token(SyntaxKind.InKeyword)))))) + .WithRefKindKeyword( + Token(SyntaxKind.RefKeyword)), + Argument( + IdentifierName(numElementsIdentifier)) + })))))))); + + LocalDeclarationStatementSyntax nativeValuesDeclaration = GenerateNativeValuesDestinationDeclaration(info, context); + + return Block(numElementsDeclaration, + managedValuesDeclaration, + nativeValuesDeclaration, + GenerateContentsMarshallingStatement(info, context, + IdentifierName(numElementsIdentifier))); + } + public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { - yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: false); + string numElementsIdentifier = context.GetAdditionalIdentifier(info, "numElements"); + if (!info.IsByRef && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + yield return GenerateByValueUnmarshalStatement(info, context); + } + else + { + yield return Block(LocalDeclarationStatement( + VariableDeclaration( + PredefinedType(Token(SyntaxKind.IntKeyword)), + SingletonSeparatedList( + VariableDeclarator(numElementsIdentifier).WithInitializer(EqualsValueClause(_numElementsExpression))))), + GeneratedManagedValuesDestinationDeclaration(info, context, numElementsIdentifier), + GenerateNativeValuesSourceDeclaration(info, context, numElementsIdentifier), + GenerateContentsMarshallingStatement(info, context, + IdentifierName(numElementsIdentifier))); + } + foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, context)) { yield return statement; @@ -992,6 +1184,16 @@ namespace Microsoft.Interop return node; } + + public override SyntaxNode? VisitArgument(ArgumentSyntax node) + { + if (node.Expression.ToString() == _nativeIdentifier) + { + return node.WithExpression( + CastExpression(_nativeType, node.Expression)); + } + return node; + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 01b0c4aae2b..dc1a04c6981 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -22,9 +22,9 @@ namespace Microsoft.Interop public static readonly TypeSyntax SystemIntPtrType = ParseTypeName(TypeNames.System_IntPtr); - public static ForStatementSyntax GetForLoop(string collectionIdentifier, string indexerIdentifier) + public static ForStatementSyntax GetForLoop(ExpressionSyntax lengthExpression, string indexerIdentifier) { - // for(int <indexerIdentifier> = 0; <indexerIdentifier> < <collectionIdentifier>.Length; ++<indexerIdentifier>) + // for(int <indexerIdentifier> = 0; <indexerIdentifier> < <lengthIdentifier>; ++<indexerIdentifier>) // ; return ForStatement(EmptyStatement()) .WithDeclaration( @@ -32,7 +32,7 @@ namespace Microsoft.Interop PredefinedType( Token(SyntaxKind.IntKeyword))) .WithVariables( - SingletonSeparatedList<VariableDeclaratorSyntax>( + SingletonSeparatedList( VariableDeclarator( Identifier(indexerIdentifier)) .WithInitializer( @@ -44,10 +44,7 @@ namespace Microsoft.Interop BinaryExpression( SyntaxKind.LessThanExpression, IdentifierName(indexerIdentifier), - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(collectionIdentifier), - IdentifierName("Length")))) + lengthExpression)) .WithIncrementors( SingletonSeparatedList<ExpressionSyntax>( PrefixUnaryExpression( @@ -102,6 +99,11 @@ namespace Microsoft.Interop return context.GetAdditionalIdentifier(info, "marshaller"); } + public static string GetManagedSpanIdentifier(TypePositionInfo info, StubCodeContext context) + { + return context.GetAdditionalIdentifier(info, "managedSpan"); + } + public static string GetNativeSpanIdentifier(TypePositionInfo info, StubCodeContext context) { return context.GetAdditionalIdentifier(info, "nativeSpan"); @@ -232,7 +234,7 @@ namespace Microsoft.Interop public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInfo( MarshallingInfo elementMarshallingInfo) { - if (elementMarshallingInfo is NativeContiguousCollectionMarshallingInfo nestedCollection) + if (elementMarshallingInfo is NativeLinearCollectionMarshallingInfo nestedCollection) { if (nestedCollection.ElementCountInfo is CountElementCountInfo { ElementInfo: TypePositionInfo nestedCountElement }) { diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs index 6cd7da3f430..8de5682ac01 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs @@ -118,7 +118,7 @@ namespace Microsoft.Interop { CountInfo countInfo; MarshallingInfo elementMarshallingInfo; - if (info.MarshallingAttributeInfo is NativeContiguousCollectionMarshallingInfo collectionMarshalling + if (info.MarshallingAttributeInfo is NativeLinearCollectionMarshallingInfo collectionMarshalling && collectionMarshalling.UseDefaultMarshalling && collectionMarshalling.ElementCountInfo is NoCountInfo or SizeAndParamIndexInfo && collectionMarshalling.ElementMarshallingInfo is NoMarshallingInfo or MarshalAsInfo { UnmanagedType: not UnmanagedType.CustomMarshaler } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs index 0000772d544..86bab61b220 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs @@ -98,15 +98,11 @@ namespace Microsoft.Interop public sealed record UnmanagedBlittableMarshallingInfo : MarshallingInfo; [Flags] - public enum CustomMarshallingFeatures + public enum CustomTypeMarshallerPinning { None = 0, - ManagedToNative = 0x1, - NativeToManaged = 0x2, - ManagedToNativeStackalloc = 0x4, - ManagedTypePinning = 0x8, - NativeTypePinning = 0x10, - FreeNativeResources = 0x20, + ManagedType = 0x1, + NativeType = 0x2 } public abstract record CountInfo @@ -142,9 +138,12 @@ namespace Microsoft.Interop /// </summary> public record NativeMarshallingAttributeInfo( ManagedTypeInfo NativeMarshallingType, - ManagedTypeInfo? ValuePropertyType, - CustomMarshallingFeatures MarshallingFeatures, - bool UseDefaultMarshalling) : MarshallingInfo; + ManagedTypeInfo? NativeValueType, + CustomTypeMarshallerDirection Direction, + CustomTypeMarshallerFeatures MarshallingFeatures, + CustomTypeMarshallerPinning PinningFeatures, + bool UseDefaultMarshalling, + int? BufferSize) : MarshallingInfo; /// <summary> /// User-applied System.Runtime.InteropServices.GeneratedMarshallingAttribute @@ -162,18 +161,24 @@ namespace Microsoft.Interop /// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute /// with a contiguous collection marshaller /// </summary> - public sealed record NativeContiguousCollectionMarshallingInfo( + public sealed record NativeLinearCollectionMarshallingInfo( ManagedTypeInfo NativeMarshallingType, - ManagedTypeInfo? ValuePropertyType, - CustomMarshallingFeatures MarshallingFeatures, + ManagedTypeInfo? NativeValueType, + CustomTypeMarshallerDirection Direction, + CustomTypeMarshallerFeatures MarshallingFeatures, + CustomTypeMarshallerPinning PinningFeatures, bool UseDefaultMarshalling, + int? BufferSize, CountInfo ElementCountInfo, ManagedTypeInfo ElementType, MarshallingInfo ElementMarshallingInfo) : NativeMarshallingAttributeInfo( NativeMarshallingType, - ValuePropertyType, + NativeValueType, + Direction, MarshallingFeatures, - UseDefaultMarshalling + PinningFeatures, + UseDefaultMarshalling, + BufferSize ); @@ -223,36 +228,36 @@ namespace Microsoft.Interop IEnumerable<AttributeData> useSiteAttributes, ImmutableHashSet<string> inspectedElements) { - Dictionary<int, AttributeData> marshallingAttributesByIndirectionLevel = new(); + Dictionary<int, AttributeData> marshallingAttributesByIndirectionDepth = new(); int maxIndirectionLevelDataProvided = 0; foreach (AttributeData attribute in useSiteAttributes) { if (TryGetAttributeIndirectionLevel(attribute, out int indirectionLevel)) { - if (marshallingAttributesByIndirectionLevel.ContainsKey(indirectionLevel)) + if (marshallingAttributesByIndirectionDepth.ContainsKey(indirectionLevel)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attribute, nameof(Resources.DuplicateMarshallingInfo), indirectionLevel.ToString()); return NoMarshallingInfo.Instance; } - marshallingAttributesByIndirectionLevel.Add(indirectionLevel, attribute); + marshallingAttributesByIndirectionDepth.Add(indirectionLevel, attribute); maxIndirectionLevelDataProvided = Math.Max(maxIndirectionLevelDataProvided, indirectionLevel); } } - int maxIndirectionLevelUsed = 0; + int maxIndirectionDepthUsed = 0; MarshallingInfo info = GetMarshallingInfo( managedType, - marshallingAttributesByIndirectionLevel, + marshallingAttributesByIndirectionDepth, indirectionLevel: 0, inspectedElements, - ref maxIndirectionLevelUsed); - if (maxIndirectionLevelUsed < maxIndirectionLevelDataProvided) + ref maxIndirectionDepthUsed); + if (maxIndirectionDepthUsed < maxIndirectionLevelDataProvided) { _diagnostics.ReportInvalidMarshallingAttributeInfo( - marshallingAttributesByIndirectionLevel[maxIndirectionLevelDataProvided], + marshallingAttributesByIndirectionDepth[maxIndirectionLevelDataProvided], nameof(Resources.ExtraneousMarshallingInfo), maxIndirectionLevelDataProvided.ToString(), - maxIndirectionLevelUsed.ToString()); + maxIndirectionDepthUsed.ToString()); } return info; } @@ -262,9 +267,9 @@ namespace Microsoft.Interop Dictionary<int, AttributeData> useSiteAttributes, int indirectionLevel, ImmutableHashSet<string> inspectedElements, - ref int maxIndirectionLevelUsed) + ref int maxIndirectionDepthUsed) { - maxIndirectionLevelUsed = Math.Max(indirectionLevel, maxIndirectionLevelUsed); + maxIndirectionDepthUsed = Math.Max(indirectionLevel, maxIndirectionDepthUsed); CountInfo parsedCountInfo = NoCountInfo.Instance; if (useSiteAttributes.TryGetValue(indirectionLevel, out AttributeData useSiteAttribute)) @@ -275,7 +280,7 @@ namespace Microsoft.Interop && SymbolEqualityComparer.Default.Equals(_compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute), attributeClass)) { // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute - return CreateInfoFromMarshalAs(type, useSiteAttribute, inspectedElements, ref maxIndirectionLevelUsed); + return CreateInfoFromMarshalAs(type, useSiteAttribute, inspectedElements, ref maxIndirectionDepthUsed); } else if (SymbolEqualityComparer.Default.Equals(_compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute), attributeClass)) { @@ -296,7 +301,7 @@ namespace Microsoft.Interop parsedCountInfo, useSiteAttributes, inspectedElements, - ref maxIndirectionLevelUsed); + ref maxIndirectionDepthUsed); } } } @@ -318,7 +323,7 @@ namespace Microsoft.Interop parsedCountInfo, useSiteAttributes, inspectedElements, - ref maxIndirectionLevelUsed); + ref maxIndirectionDepthUsed); } else if (attributeClass.ToDisplayString() == TypeNames.GeneratedMarshallingAttribute) { @@ -334,7 +339,7 @@ namespace Microsoft.Interop indirectionLevel, useSiteAttributes, inspectedElements, - ref maxIndirectionLevelUsed, + ref maxIndirectionDepthUsed, out MarshallingInfo infoMaybe)) { return infoMaybe; @@ -467,7 +472,7 @@ namespace Microsoft.Interop ITypeSymbol type, AttributeData attrData, ImmutableHashSet<string> inspectedElements, - ref int maxIndirectionLevelUsed) + ref int maxIndirectionDepthUsed) { object unmanagedTypeObj = attrData.ConstructorArguments[0].Value!; UnmanagedType unmanagedType = unmanagedTypeObj is short unmanagedTypeAsShort @@ -547,8 +552,8 @@ namespace Microsoft.Interop } else { - maxIndirectionLevelUsed = 1; - elementMarshallingInfo = GetMarshallingInfo(elementType, new Dictionary<int, AttributeData>(), 1, ImmutableHashSet<string>.Empty, ref maxIndirectionLevelUsed); + maxIndirectionDepthUsed = 1; + elementMarshallingInfo = GetMarshallingInfo(elementType, new Dictionary<int, AttributeData>(), 1, ImmutableHashSet<string>.Empty, ref maxIndirectionDepthUsed); } INamedTypeSymbol? arrayMarshaller; @@ -568,13 +573,20 @@ namespace Microsoft.Interop return new MissingSupportCollectionMarshallingInfo(arraySizeInfo, elementMarshallingInfo); } - ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; + var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(arrayMarshaller); + + Debug.Assert(customTypeMarshallerData is not null); - return new NativeContiguousCollectionMarshallingInfo( + ITypeSymbol? nativeValueType = ManualTypeMarshallingHelper.FindToNativeValueMethod(arrayMarshaller)?.ReturnType; + + return new NativeLinearCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), - ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, - MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, + NativeValueType: nativeValueType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeValueType) : null, + Direction: customTypeMarshallerData.Value.Direction, + MarshallingFeatures: customTypeMarshallerData.Value.Features, + PinningFeatures: CustomTypeMarshallerPinning.NativeType, UseDefaultMarshalling: true, + BufferSize: customTypeMarshallerData.Value.BufferSize, ElementCountInfo: arraySizeInfo, ElementType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), ElementMarshallingInfo: elementMarshallingInfo); @@ -589,16 +601,9 @@ namespace Microsoft.Interop CountInfo parsedCountInfo, Dictionary<int, AttributeData> useSiteAttributes, ImmutableHashSet<string> inspectedElements, - ref int maxIndirectionLevelUsed) + ref int maxIndirectionDepthUsed) { - CustomMarshallingFeatures features = CustomMarshallingFeatures.None; - - if (!isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null) - { - features |= CustomMarshallingFeatures.ManagedTypePinning; - } - - ITypeSymbol spanOfByte = _compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!.Construct(_compilation.GetSpecialType(SpecialType.System_Byte)); + INamedTypeSymbol readOnlySpanOfT = _compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!; if (nativeType.IsUnboundGenericType) { @@ -626,93 +631,55 @@ namespace Microsoft.Interop } } - ITypeSymbol contiguousCollectionMarshalerAttribute = _compilation.GetTypeByMetadataName(TypeNames.GenericContiguousCollectionMarshallerAttribute)!; - - bool isContiguousCollectionMarshaller = nativeType.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, contiguousCollectionMarshalerAttribute)); - IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType); - - ManualTypeMarshallingHelper.NativeTypeMarshallingVariant marshallingVariant = isContiguousCollectionMarshaller - ? ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.ContiguousCollection - : ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard; - - bool hasInt32Constructor = false; - foreach (IMethodSymbol ctor in nativeType.Constructors) - { - if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallingVariant) && (valueProperty is null or { GetMethod: not null })) - { - features |= CustomMarshallingFeatures.ManagedToNative; - } - else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, marshallingVariant) - && (valueProperty is null or { GetMethod: not null })) - { - features |= CustomMarshallingFeatures.ManagedToNativeStackalloc; - } - else if (ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.SpecialType == SpecialType.System_Int32) - { - hasInt32Constructor = true; - } - } - - // The constructor that takes only the native element size is required for collection marshallers - // in the native-to-managed scenario. - if ((!isContiguousCollectionMarshaller - || (hasInt32Constructor && ManualTypeMarshallingHelper.HasSetUnmarshalledCollectionLengthMethod(nativeType))) - && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type) - && (valueProperty is null or { SetMethod: not null })) + var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(nativeType); + if (customTypeMarshallerData is null) { - features |= CustomMarshallingFeatures.NativeToManaged; - } - - if (features == CustomMarshallingFeatures.None) - { - _diagnostics.ReportInvalidMarshallingAttributeInfo( - attrData, - isContiguousCollectionMarshaller - ? nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage) - : nameof(Resources.NativeTypeMustHaveRequiredShapeMessage), - nativeType.ToDisplayString()); return NoMarshallingInfo.Instance; } - if (ManualTypeMarshallingHelper.HasFreeNativeMethod(nativeType)) + CustomTypeMarshallerPinning pinning = CustomTypeMarshallerPinning.None; + + if (!isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null) { - features |= CustomMarshallingFeatures.FreeNativeResources; + pinning |= CustomTypeMarshallerPinning.ManagedType; } if (ManualTypeMarshallingHelper.FindGetPinnableReference(nativeType) is not null) { - features |= CustomMarshallingFeatures.NativeTypePinning; + pinning |= CustomTypeMarshallerPinning.NativeType; } - if (isContiguousCollectionMarshaller) - { - if (!ManualTypeMarshallingHelper.HasNativeValueStorageProperty(nativeType, spanOfByte)) - { - _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); - return NoMarshallingInfo.Instance; - } + IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(nativeType); - if (!ManualTypeMarshallingHelper.TryGetElementTypeFromContiguousCollectionMarshaller(nativeType, out ITypeSymbol elementType)) + if (customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection) + { + if (!ManualTypeMarshallingHelper.TryGetElementTypeFromLinearCollectionMarshaller(nativeType, readOnlySpanOfT, out ITypeSymbol elementType)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); return NoMarshallingInfo.Instance; } - return new NativeContiguousCollectionMarshallingInfo( + return new NativeLinearCollectionMarshallingInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), - valueProperty is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valueProperty.Type) : null, - features, + toNativeValueMethod is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(toNativeValueMethod.ReturnType) : null, + customTypeMarshallerData.Value.Direction, + customTypeMarshallerData.Value.Features, + pinning, UseDefaultMarshalling: !isMarshalUsingAttribute, + customTypeMarshallerData.Value.BufferSize, parsedCountInfo, ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), - GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); + GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionDepthUsed)); } return new NativeMarshallingAttributeInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), - valueProperty is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valueProperty.Type) : null, - features, - UseDefaultMarshalling: !isMarshalUsingAttribute); + toNativeValueMethod is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(toNativeValueMethod.ReturnType) : null, + customTypeMarshallerData.Value.Direction, + customTypeMarshallerData.Value.Features, + pinning, + UseDefaultMarshalling: !isMarshalUsingAttribute, + customTypeMarshallerData.Value.BufferSize); } private bool TryCreateTypeBasedMarshallingInfo( @@ -721,7 +688,7 @@ namespace Microsoft.Interop int indirectionLevel, Dictionary<int, AttributeData> useSiteAttributes, ImmutableHashSet<string> inspectedElements, - ref int maxIndirectionLevelUsed, + ref int maxIndirectionDepthUsed, out MarshallingInfo marshallingInfo) { // Check for an implicit SafeHandle conversion. @@ -767,20 +734,24 @@ namespace Microsoft.Interop if (arrayMarshaller is null) { // If the array marshaler type is not available, then we cannot marshal arrays but indicate it is missing. - marshallingInfo = new MissingSupportCollectionMarshallingInfo(parsedCountInfo, GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); + marshallingInfo = new MissingSupportCollectionMarshallingInfo(parsedCountInfo, GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionDepthUsed)); return true; } - ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; + var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(arrayMarshaller); + ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindToNativeValueMethod(arrayMarshaller)?.ReturnType; - marshallingInfo = new NativeContiguousCollectionMarshallingInfo( + marshallingInfo = new NativeLinearCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), - ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, - MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, + NativeValueType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, + Direction: CustomTypeMarshallerDirection.Ref, + MarshallingFeatures: CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, + PinningFeatures: CustomTypeMarshallerPinning.NativeType, UseDefaultMarshalling: true, + customTypeMarshallerData.Value.BufferSize, ElementCountInfo: parsedCountInfo, ElementType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), - ElementMarshallingInfo: GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); + ElementMarshallingInfo: GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionDepthUsed)); return true; } @@ -793,7 +764,7 @@ namespace Microsoft.Interop AttributeData attrData = _contextSymbol is IMethodSymbol ? _contextSymbol.GetAttributes().First(a => a.AttributeClass.ToDisplayString() == TypeNames.LibraryImportAttribute) : default; - marshallingInfo = CreateNativeMarshallingInfo(type, _defaultInfo.StringMarshallingCustomType, attrData, true, indirectionLevel, parsedCountInfo, useSiteAttributes, inspectedElements, ref maxIndirectionLevelUsed); + marshallingInfo = CreateNativeMarshallingInfo(type, _defaultInfo.StringMarshallingCustomType, attrData, true, indirectionLevel, parsedCountInfo, useSiteAttributes, inspectedElements, ref maxIndirectionDepthUsed); return true; } @@ -862,7 +833,7 @@ namespace Microsoft.Interop foreach (KeyValuePair<string, TypedConstant> arg in attrData.NamedArguments) { - if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionLevel) + if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionDepth) { indirectionLevel = (int)arg.Value.Value!; return true; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj index a542f34e721..8c9dbccb9f1 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj @@ -5,6 +5,7 @@ <Nullable>enable</Nullable> <RootNamespace>Microsoft.Interop</RootNamespace> <RunAnalyzers>true</RunAnalyzers> + <DefineConstants>$(DefineConstants);LIBRARYIMPORT_GENERATOR_TEST</DefineConstants> </PropertyGroup> <ItemGroup> @@ -12,6 +13,12 @@ DependentUpon="Resources.resx" DesignTime="True" AutoGen="True" /> + <Compile Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\CustomTypeMarshallerKind.cs" + Link="Common\System\Runtime\InteropServices\CustomTypeMarshallerKind.cs" /> + <Compile Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\CustomTypeMarshallerDirection.cs" + Link="Common\System\Runtime\InteropServices\CustomTypeMarshallerDirection.cs" /> + <Compile Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\CustomTypeMarshallerFeatures.cs" + Link="Common\System\Runtime\InteropServices\CustomTypeMarshallerFeatures.cs" /> </ItemGroup> <ItemGroup> diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs index 90e95ecef5c..626e683b776 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs @@ -124,7 +124,7 @@ namespace Microsoft.Interop { } /// <summary> - /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel'. + /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth'. /// </summary> internal static string ConstantAndElementCountInfoDisallowed { get { @@ -178,7 +178,7 @@ namespace Microsoft.Interop { } /// <summary> - /// Looks up a localized string similar to Marshalling info was specified for 'ElementIndirectionLevel' {0}, but marshalling info was only needed for {1} level(s) of indirection. + /// Looks up a localized string similar to Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection. /// </summary> internal static string ExtraneousMarshallingInfo { get { @@ -223,7 +223,7 @@ namespace Microsoft.Interop { } /// <summary> - /// Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.. + /// Looks up a localized string similar to Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.. /// </summary> internal static string MarshallingBoolAsUndefinedNotSupported { get { @@ -297,18 +297,18 @@ namespace Microsoft.Interop { /// <summary> /// Looks up a localized string similar to The 'Value' property must not be a 'ref' or 'readonly ref' property.. /// </summary> - internal static string RefValuePropertyUnsupportedDescription { + internal static string RefNativeValueUnsupportedDescription { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedDescription", resourceCulture); + return ResourceManager.GetString("RefNativeValueUnsupportedDescription", resourceCulture); } } /// <summary> /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.. /// </summary> - internal static string RefValuePropertyUnsupportedMessage { + internal static string RefNativeValueUnsupportedMessage { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedMessage", resourceCulture); + return ResourceManager.GetString("RefNativeValueUnsupportedMessage", resourceCulture); } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx index abac39ff660..2097f8a82ec 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx @@ -165,33 +165,12 @@ <data name="OutByValueNotSupportedMessage" xml:space="preserve"> <value>The '[Out]' attribute is not supported on the '{0}' parameter.</value> </data> - <data name="RefValuePropertyUnsupportedDescription" xml:space="preserve"> - <value>The 'Value' property must not be a 'ref' or 'readonly ref' property.</value> - </data> - <data name="RefValuePropertyUnsupportedMessage" xml:space="preserve"> - <value>The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.</value> - </data> <data name="SafeHandleByRefMustBeConcrete" xml:space="preserve"> <value>An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete.</value> </data> <data name="TypeNotSupportedTitle" xml:space="preserve"> <value>Specified type is not supported by source-generated P/Invokes</value> </data> - <data name="ValuePropertyMarshallingRequiresAdditionalState" xml:space="preserve"> - <value>Marshalling a value between managed and native with a native type with a 'Value' property requires extra state, which is not supported in this context.</value> - </data> - <data name="ValuePropertyMustHaveGetterDescription" xml:space="preserve"> - <value>The native type's 'Value' property must have a getter to support marshalling from managed to native.</value> - </data> - <data name="ValuePropertyMustHaveGetterMessage" xml:space="preserve"> - <value>The 'Value' property on the native type '{0}' must have a getter</value> - </data> - <data name="ValuePropertyMustHaveSetterDescription" xml:space="preserve"> - <value>The native type's 'Value' property must have a setter to support marshalling from native to managed.</value> - </data> - <data name="ValuePropertyMustHaveSetterMessage" xml:space="preserve"> - <value>The 'Value' property on the native type '{0}' must have a setter</value> - </data> <data name="CyclicalCountInfo" xml:space="preserve"> <value>This element cannot depend on '{0}' for collection size information without creating a dependency cycle</value> </data> @@ -202,17 +181,11 @@ <value>Multiple marshalling attributes per element per indirection level is unsupported, but duplicate information was provided for indirection level {0}</value> </data> <data name="ExtraneousMarshallingInfo" xml:space="preserve"> - <value>Marshalling info was specified for 'ElementIndirectionLevel' {0}, but marshalling info was only needed for {1} level(s) of indirection</value> + <value>Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection</value> </data> <data name="NativeGenericTypeMustBeClosedOrMatchArityMessage" xml:space="preserve"> <value>The native type '{0}' for managed type '{1}' must be a closed generic type or have the same arity as the managed type.</value> </data> - <data name="NativeTypeMustHaveRequiredShapeDescription" xml:space="preserve"> - <value>The native type must have at least one of the two marshalling methods to enable marshalling the managed type.</value> - </data> - <data name="NativeTypeMustHaveRequiredShapeMessage" xml:space="preserve"> - <value>The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}'</value> - </data> <data name="CollectionNativeTypeMustHaveRequiredShapeDescription" xml:space="preserve"> <value>A native type with the 'GenericContiguousCollectionMarshallerAttribute' must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.</value> </data> @@ -220,7 +193,7 @@ <value>The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>'</value> </data> <data name="ConstantAndElementCountInfoDisallowed" xml:space="preserve"> - <value>Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel'</value> + <value>Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth'</value> </data> <data name="RuntimeMarshallingMustBeDisabled" xml:space="preserve"> <value>Runtime marshalling must be disabled in this project by applying the 'System.Runtime.InteropServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.</value> diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs index 1a812d49696..ba09ebee867 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs @@ -15,7 +15,9 @@ namespace Microsoft.Interop public const string MarshalUsingAttribute = "System.Runtime.InteropServices.MarshalUsingAttribute"; - public const string GenericContiguousCollectionMarshallerAttribute = "System.Runtime.InteropServices.GenericContiguousCollectionMarshallerAttribute"; + public const string CustomTypeMarshallerAttribute = "System.Runtime.InteropServices.CustomTypeMarshallerAttribute"; + + public const string CustomTypeMarshallerAttributeGenericPlaceholder = "System.Runtime.InteropServices.CustomTypeMarshallerAttribute.GenericPlaceholder"; public const string LCIDConversionAttribute = "System.Runtime.InteropServices.LCIDConversionAttribute"; @@ -24,6 +26,8 @@ namespace Microsoft.Interop public const string UnmanagedCallConvAttribute = "System.Runtime.InteropServices.UnmanagedCallConvAttribute"; public const string System_Span_Metadata = "System.Span`1"; public const string System_Span = "System.Span"; + public const string System_ReadOnlySpan_Metadata = "System.ReadOnlySpan`1"; + public const string System_ReadOnlySpan = "System.ReadOnlySpan"; public const string System_IntPtr = "System.IntPtr"; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs index c03e880f124..bb10cf31c12 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs @@ -28,16 +28,7 @@ namespace Microsoft.Interop { if (!field.IsStatic) { - bool fieldBlittable = field switch - { - // Assume that type parameters that can be blittable are blittable. - // We'll re-evaluate blittability for generic fields of generic types at instantation time. - { Type: ITypeParameterSymbol { IsReferenceType: false } } => true, - { Type.IsUnmanagedType: false } => false, - _ => IsConsideredBlittable(field.Type, seenTypes.Add(type)) - }; - - if (!fieldBlittable) + if (!IsConsideredBlittable(field.Type, seenTypes.Add(type))) { return false; } @@ -51,6 +42,12 @@ namespace Microsoft.Interop private static bool IsConsideredBlittable(this ITypeSymbol type, ImmutableHashSet<ITypeSymbol> seenTypes) { + // Assume that type parameters that can be blittable are blittable. + // We'll re-evaluate blittability for generic fields of generic types at instantation time. + if (type.TypeKind == TypeKind.TypeParameter && !type.IsReferenceType) + { + return true; + } if (!type.IsUnmanagedType || type.IsAutoLayout()) { return false; 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; |