Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md187
-rw-r--r--docs/design/libraries/LibraryImportGenerator/StructMarshalling.md170
-rw-r--r--docs/project/list-of-diagnostics.md27
-rw-r--r--eng/generators.targets9
-rw-r--r--src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs54
-rw-r--r--src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/MarshalAsAttribute.cs26
-rw-r--r--src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs2
-rw-r--r--src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs5
-rw-r--r--src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs4
-rw-r--r--src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj14
-rw-r--r--src/libraries/Common/src/Interop/Interop.Ldap.cs15
-rw-r--r--src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs1
-rw-r--r--src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs2
-rw-r--r--src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs1
-rw-r--r--src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs5
-rw-r--r--src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs1
-rw-r--r--src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs1
-rw-r--r--src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs97
-rw-r--r--src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs37
-rw-r--r--src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs34
-rw-r--r--src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs28
-rw-r--r--src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs66
-rw-r--r--src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs3
-rw-r--r--src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs18
-rw-r--r--src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs1
-rw-r--r--src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs3
-rw-r--r--src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs3
-rw-r--r--src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs3
-rw-r--r--src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs9
-rw-r--r--src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs3
-rw-r--r--src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs30
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs909
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs404
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs535
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs67
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs636
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx258
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/LinearCollectionElementMarshallingCodeContext.cs (renamed from src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs)12
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs246
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs82
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs454
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs18
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs205
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj7
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs14
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx31
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs6
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs17
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj3
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs264
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs12
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/SetLastErrorTests.cs1
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs5
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs6
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs256
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs4
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs1769
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs1240
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs28
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs126
65 files changed, 5337 insertions, 3147 deletions
diff --git a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md
index eccefc62e87..b787c172e6c 100644
--- a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md
+++ b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md
@@ -24,11 +24,11 @@ We have decided to match the managed semantics of `(ReadOnly)Span<T>` to provide
As part of this design, we would also want to include some in-box marshallers that follow the design laid out in the [Struct Marshalling design doc](./StructMarshalling.md) to support some additional scenarios:
-- A marshaler that marshals an empty span as a non-null pointer.
+- A marshaller that marshals an empty span as a non-null pointer.
- This marshaller would only support empty spans as it cannot correctly represent non-empty spans of non-blittable types.
-- A marshaler that marshals out a pointer to the native memory as a Span instead of copying the data into a managed array.
+- A marshaller that marshals out a pointer to the native memory as a Span instead of copying the data into a managed array.
- This marshaller would only support blittable spans by design.
- - This marshaler will require the user to manually release the memory. Since this will be an opt-in marshaler, this scenario is already advanced and that additional requirement should be understandable to users who use this marshaler.
+ - This marshaller will require the user to manually release the memory. Since this will be an opt-in marshaller, this scenario is already advanced and that additional requirement should be understandable to users who use this marshaller.
- Since there is no mechansim to provide a collection length, the question of how to provide the span's length in this case is still unresolved. One option would be to always provide a length 1 span and require the user to create a new span with the correct size, but that feels like a bad design.
### Pros/Cons of Design 1
@@ -40,7 +40,7 @@ Pros:
Cons:
-- Defining custom marshalers for non-empty spans of non-blittable types generically is impossible since the marshalling rules of the element's type cannot be known.
+- Defining custom marshallers for non-empty spans of non-blittable types generically is impossible since the marshalling rules of the element's type cannot be known.
- Custom non-default marshalling of the span element types is impossible for non-built-in types.
- Inlining the span marshalling fully into the stub increases on-disk IL size.
- This design does not enable developers to easily define custom marshalling support for their own collection types, which may be desireable.
@@ -55,43 +55,86 @@ Span marshalling would still be implemented with similar semantics as mentioned
### Proposed extension to the custom type marshalling design
-Introduce a new attribute named `GenericContiguousCollectionMarshallerAttribute`. This attribute would have the following shape:
+Introduce a marshaller kind named `LinearCollection`.
-```csharp
+```diff
namespace System.Runtime.InteropServices
-{
- [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
- public sealed class GenericContiguousCollectionMarshallerAttribute : Attribute
- {
- public GenericContiguousCollectionMarshallerAttribute();
- }
+{
+ [AttributeUsage(AttributeTargets.Struct)]
+ public sealed class CustomTypeMarshallerAttribute : Attribute
+ {
++ /// <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
++ {
++ }
+ }
+
+ public enum CustomTypeMarshallerKind
+ {
+ Value,
++ LinearCollection
+ }
}
```
The attribute would be used with a collection type like `Span<T>` as follows:
```csharp
-[NativeTypeMarshalling(typeof(DefaultSpanMarshaler<>))]
+[NativeTypeMarshalling(typeof(DefaultSpanMarshaller<>))]
public ref struct Span<T>
{
...
}
-[GenericContiguousCollectionMarshaller]
-public ref struct DefaultSpanMarshaler<T>
+[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection)]
+public ref struct DefaultSpanMarshaller<T>
{
...
}
```
-The `GenericContiguousCollectionMarshallerAttribute` attribute is applied to a generic marshaler type with the "collection marshaller" shape described below. Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` constructor as long as they have the same arity as the type the attribute is applied to and generic parameters provided to the applied-to type can also be used to construct the type passed as a parameter.
+The `CustomTypeMarshallerKind.LinearCollection` kind is applied to a generic marshaller type with the "LinearCollection marshaller shape" described below.
+
+#### Supporting generics
+
+Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` and the `CustomTypeMarshallerAttribute` as long as they have the same arity as the type with the attribute and generic parameters provided to the type with the attribute can also be used to construct the type passed as a parameter.
+
+If a `CustomTypeMarshaller`-attributed type is a marshaller for a type for a pointer, an array, or a combination of pointers and arrays, the `CustomTypeMarshallerAttribute.GenericPlaceholder` type can be used in the place of the first generic parameter of the marshaller type.
+
+For example:
+
+```csharp
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder), Direction = CustomTypeMarshallerDirection.In)]
+struct Marshaller<T>
+{
+ public Marshaller(T managed);
+}
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), Direction = CustomTypeMarshallerDirection.In)]
+struct Marshaller<T>
+{
+ public Marshaller(T[] managed);
+}
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*), Direction = CustomTypeMarshallerDirection.In)]
+struct Marshaller<T> where T : unmanaged
+{
+ public Marshaller(T* managed);
+}
+[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), Direction = CustomTypeMarshallerDirection.In)]
+struct Marshaller<T> where T : unmanaged
+{
+ public Marshaller(T*[] managed);
+}
+```
-#### Generic collection marshaller shape
+#### LinearCollection marshaller shape
-A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaler types used with the `NativeTypeMarshallingAttribute`, excluding the constructors.
+A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaller types used with the `CustomTypeMarshallerKind.Value` shape, excluding the constructors.
```csharp
-[GenericContiguousCollectionMarshaller]
+[CustomTypeMarshaller(typeof(GenericCollection<, , ,...>), CustomTypeMarshallerKind.LinearCollection)]
public struct GenericContiguousCollectionMarshallerImpl<T, U, V,...>
{
// this constructor is required if marshalling from native to managed is supported.
@@ -100,29 +143,33 @@ public struct GenericContiguousCollectionMarshallerImpl<T, U, V,...>
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, int nativeSizeOfElement);
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, Span<byte> stackSpace, int nativeSizeOfElement); // optional
- public const int StackBufferSize = /* */; // required if the span-based constructor is supplied.
-
/// <summary>
- /// A span that points to the memory where the managed values of the collection are stored (in the marshalling case) or should be stored (in the unmarshalling case).
+ /// A span that points to the memory where the managed values of the collection are stored.
/// </summary>
- public Span<TCollectionElement> ManagedValues { get; }
-
+ public ReadOnlySpan<TCollectionElement> GetManagedValuesSource();
/// <summary>
- /// Set the expected length of the managed collection based on the parameter/return value/field marshalling information.
- /// Required only when unmarshalling is supported.
+ /// A span that points to the memory where the unmarshalled managed values of the collection should be stored.
/// </summary>
- public void SetUnmarshalledCollectionLength(int length);
-
- public IntPtr Value { get; set; }
+ public Span<TCollectionElement> GetManagedValuesDestination(int length);
+ /// <summary>
+ /// A span that points to the memory where the native values of the collection are stored after the native call.
+ /// </summary>
+ public ReadOnlySpan<TCollectionElement> GetNativeValuesSource(int length);
+ /// <summary>
+ /// A span that points to the memory where the native values of the collection should be stored.
+ /// </summary>
+ public Span<TCollectionElement> GetNativeValuesDestination();
/// <summary>
/// A span that points to the memory where the native values of the collection should be stored.
/// </summary>
public unsafe Span<byte> NativeValueStorage { get; }
- // The requirements on the Value property are the same as when used with `NativeTypeMarshallingAttribute`.
+ // The requirements on the TNative type are the same as when used with `NativeTypeMarshallingAttribute`.
// The property is required with the generic collection marshalling.
- public TNative Value { get; set; }
+ public TNative ToNativeValue();
+
+ public void FromNativeValue(TNative value);
}
```
@@ -162,7 +209,7 @@ To support supplying information about collection element counts, a parameterles
The `ElementIndirectionLevel` property is added to support supplying marshalling info for element types in a collection. For example, if the user is passing a `List<List<Foo>>` from managed to native code, they could provide the following attributes to specify marshalling rules for the outer and inner lists and `Foo` separately:
```csharp
-private static partial void Bar([MarshalUsing(typeof(ListAsArrayMarshaller<List<Foo>>), CountElementName = nameof(count)), MarshalUsing(ConstantElementCount = 10, ElementIndirectionLevel = 1), MarshalUsing(typeof(FooMarshaler), ElementIndirectionLevel = 2)] List<List<Foo>> foos, int count);
+private static partial void Bar([MarshalUsing(typeof(ListAsArrayMarshaller<List<Foo>>), CountElementName = nameof(count)), MarshalUsing(ConstantElementCount = 10, ElementIndirectionLevel = 1), MarshalUsing(typeof(FooMarshaller), ElementIndirectionLevel = 2)] List<List<Foo>> foos, int count);
```
Multiple `MarshalUsing` attributes can only be supplied on the same parameter or return value if the `ElementIndirectionLevel` property is set to distinct values. One `MarshalUsing` attribute per parameter or return value can leave the `ElementIndirectionLevel` property unset. This attribute controls the marshalling of the collection object passed in as the parameter. The sequence of managed types for `ElementIndirectionLevel` is based on the elements of the `ManagedValues` span on the collection marshaller of the previous indirection level. For example, for the marshalling info for `ElementIndirectionLevel = 1` above, the managed type is the type of the following C# expression: `ListAsArrayMarshaller<List<Foo>>.ManagedValues[0]`.
@@ -174,14 +221,16 @@ Alternatively, the `MarshalUsingAttribute` could provide a `Type ElementNativeTy
This design could be used to provide a default marshaller for spans and arrays. Below is an example simple marshaller for `Span<T>`. This design does not include all possible optimizations, such as stack allocation, for simpilicity of the example.
```csharp
-[GenericContiguousCollectionMarshaller]
-public ref struct SpanMarshaler<T>
+[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
+public ref struct SpanMarshaller<T>
{
private Span<T> managedCollection;
private int nativeElementSize;
+
+ private IntPtr Value { get; set; }
- public SpanMarshaler(Span<T> collection, int nativeSizeOfElement)
+ public SpanMarshaller(Span<T> collection, int nativeSizeOfElement)
{
managedCollection = collection;
Value = Marshal.AllocCoTaskMem(collection.Length * nativeSizeOfElement);
@@ -189,19 +238,20 @@ public ref struct SpanMarshaler<T>
nativeElementSize = nativeSizeOfElement;
}
- public Span<T> ManagedValues => managedCollection;
-
- public void SetUnmarshalledCollectionLength(int length)
- {
- managedCollection = new T[value];
- }
+ public ReadOnlySpan<T> GetManagedValuesSource() => managedCollection;
+
+ public Span<T> GetManagedValuesDestination(int length) => managedCollection = new T[length];
- public IntPtr Value { get; set; }
+ public unsafe Span<byte> GetNativeValuesDestination() => MemoryMarshal.CreateSpan(ref *(byte*)(Value), managedCollection.Length);
- public unsafe Span<byte> NativeValueStorage => MemoryMarshal.CreateSpan(ref *(byte*)(Value), Length);
+ public unsafe Span<byte> GetNativeValuesSource(int length) => MemoryMarshal.CreateSpan(ref *(byte*)(Value), length);
public Span<T> ToManaged() => managedCollection;
+ public IntPtr ToNativeValue() => Value;
+
+ public void FromNativeValue(IntPtr value) => Value = value;
+
public void FreeNative()
{
if (Value != IntPtr.Zero)
@@ -235,19 +285,20 @@ public static partial Span<int> DuplicateValues([MarshalUsing(typeof(WrappedInt)
public static partial unsafe Span<int> DuplicateValues(Span<int> values, int length)
{
SpanMarshaller<int> __values_marshaller = new SpanMarshaller<int>(values, sizeof(WrappedInt));
- for (int i = 0; i < __values_marshaller.ManagedValues.Length; ++i)
{
- WrappedInt native = new WrappedInt(__values_marshaller.ManagedValues[i]);
- MemoryMarshal.Write(__values_marshaller.NativeValueStorage.Slice(sizeof(WrappedInt) * i), ref native);
+ ReadOnlySpan<int> __values_managedSpan = __values_marshaller.GetManagedValuesSource();
+ Span<byte> __values_nativeSpan = __values_marshaller.GetNativeValuesDestination();
+ for (int i = 0; i < __values_managedSpan.Length; ++i)
+ {
+ WrappedInt native = new WrappedInt(__values_managedSpan[i]);
+ MemoryMarshal.Write(__values_nativeSpan.Slice(sizeof(WrappedInt) * i), ref native);
+ }
}
- IntPtr __retVal_native = __PInvoke__(__values_marshaller.Value, length);
- SpanMarshaller<int> __retVal_marshaller = new
- {
- Value = __retVal_native
- };
- __retVal_marshaller.SetUnmarshalledCollectionLength(length);
- MemoryMarshal.Cast<byte, int>(__retVal_marshaller.NativeValueStorage).CopyTo(__retVal_marshaller.ManagedValues);
+ IntPtr __retVal_native = __PInvoke__(__values_marshaller.ToNativeValue(), length);
+ SpanMarshaller<int> __retVal_marshaller = new();
+ __retVal_marshaller.FromNativeValue(__retVal_native);
+ MemoryMarshal.Cast<byte, int>(__retVal_marshaller.GetNativeValuesSource(length)).CopyTo(__retVal_marshaller.GetManagedValuesDestination(length));
return __retVal_marshaller.ToManaged();
[DllImport("Native", EntryPoint="DuplicateValues")]
@@ -261,30 +312,24 @@ This design could also be applied to support the built-in array marshalling if i
If a managed or native representation of a collection has a non-contiguous element layout, then developers currently will need to convert to or from array/span types at the interop boundary. This section proposes an API that would enable developers to convert directly between a managed and native non-contiguous collection layout as part of marshalling.
-A new attribute named `GenericCollectionMarshaller` attribute could be added that would specify that the collection is noncontiguous in either managed or native representations. Then additional methods should be added to the generic collection model, and some methods would be removed:
+A new marshaller kind named `GenericCollection` could be added that would specify that the collection is noncontiguous in either managed or native representations. Then additional methods should be added to the generic collection model, and some methods would be removed:
```diff
-- [GenericContiguousCollectionMarshaller]
-+ [GenericCollectionMarshaller]
+- [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection)]
++ [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.GenericCollection)]
public struct GenericContiguousCollectionMarshallerImpl<T, U, V,...>
{
// these constructors are required if marshalling from managed to native is supported.
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, int nativeSizeOfElements);
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, Span<byte> stackSpace, int nativeSizeOfElements); // optional
- public const int StackBufferSize = /* */; // required if the span-based constructor is supplied.
+ public TNative ToNativeValue();
+ public void FromNativeValue(TNative value);
-- public Span<TCollectionElement> ManagedValues { get; }
-
-- public void SetUnmarshalledCollectionLength(int length);
-
- public IntPtr Value { get; set; }
-
-- public unsafe Span<byte> NativeValueStorage { get; }
-
- // The requirements on the Value property are the same as when used with `NativeTypeMarshallingAttribute`.
- // The property is required with the generic collection marshalling.
- public TNative Value { get; set; }
+- public ReadOnlySpan<TCollectionElement> GetManagedValuesSource();
+- public Span<TCollectionElement> GetManagedValuesDestination(int length);
+- public ReadOnlySpan<TCollectionElement> GetNativeValuesSource(int length);
+- public Span<TCollectionElement> GetNativeValuesDestination();
+ public ref byte GetOffsetForNativeValueAtIndex(int index);
+ public TCollectionElement GetManagedValueAtIndex(int index);
@@ -293,9 +338,9 @@ public struct GenericContiguousCollectionMarshallerImpl<T, U, V,...>
}
```
-The `GetManagedValueAtIndex` method and `Count` getter are used in the process of marshalling from managed to native. The generated code will iterate through `Count` elements (retrieved through `GetManagedValueAtIndex`) and assign their marshalled result to the address represented by `GetOffsetForNativeValueAtIndex` called with the same index. Then either the `Value` property getter will be called or the marshaller's `GetPinnableReference` method will be called, depending on if pinning is supported in the current scenario.
+The `GetManagedValueAtIndex` method and `Count` getter are used in the process of marshalling from managed to native. The generated code will iterate through `Count` elements (retrieved through `GetManagedValueAtIndex`) and assign their marshalled result to the address represented by `GetOffsetForNativeValueAtIndex` called with the same index. Then the `ToNativeValue` method will be called to get the value to pass to native code.
-The `SetManagedValueAtIndex` method and the `Count` setter are used in the process of marshalling from native to managed. The `Count` property will be set to the number of elements that the native collection contains, and the `Value` property will be assigned the result value from native code. Then the stub will iterate through the native collection `Count` times, calling `GetOffsetForNativeValueAtIndex` to get the offset of the native value and calling `SetManagedValueAtIndex` to set the unmarshalled managed value at that index.
+The `SetManagedValueAtIndex` method and the `Count` setter are used in the process of marshalling from native to managed. The `Count` property will be set to the number of elements that the native collection contains, and the `FromNativeValue` property will be called with the result value from native code. Then the stub will iterate through the native collection `Count` times, calling `GetOffsetForNativeValueAtIndex` to get the offset of the native value and calling `SetManagedValueAtIndex` to set the unmarshalled managed value at that index.
### Pros/Cons of Design 2
@@ -312,6 +357,6 @@ Cons:
- Introduces more attribute types into the BCL.
- Introduces more complexity in the marshalling type model.
- It may be worth describing the required members (other than constructors) in interfaces just to simplify the mental load of which members are required for which scenarios.
- - A set of interfaces (one for managed-to-native members, one for native-to-managed members, and one for the sequential-specific members) could replace the `GenericContiguousCollectionMarshaller` attribute.
+ - A set of interfaces (one for managed-to-native members, one for native-to-managed members, and one for the sequential-specific members) could replace the new marshaller kind.
- The base proposal only supports contiguous collections.
- The feeling at time of writing is that we are okay asking developers to convert to/from arrays or spans at the interop boundary.
diff --git a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md
index 63781d6d77d..7028227fedd 100644
--- a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md
+++ b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md
@@ -25,89 +25,119 @@ All design options would use these attributes:
```csharp
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
-public class GeneratedMarshallingAttribute : Attribute {}
+public sealed class GeneratedMarshallingAttribute : Attribute {}
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
-public class NativeMarshallingAttribute : Attribute
+public sealed class NativeMarshallingAttribute : Attribute
{
public NativeMarshallingAttribute(Type nativeType) {}
}
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field)]
-public class MarshalUsingAttribute : Attribute
+public sealed class MarshalUsingAttribute : Attribute
{
public MarshalUsingAttribute(Type nativeType) {}
}
-```
-
-The `NativeMarshallingAttribute` and `MarshalUsingAttribute` attributes would require that the provided native type `TNative` is a `struct` that does not require any marshalling and has a subset of three methods with the following names and shapes (with the managed type named TManaged):
-```csharp
-partial struct TNative
+[AttributeUsage(AttributeTargets.Struct)]
+public sealed class CustomTypeMarshallerAttribute : Attribute
{
- public TNative(TManaged managed) {}
- public TManaged ToManaged() {}
+ public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind marshallerKind = CustomTypeMarshallerKind.Value)
+ {
+ ManagedType = managedType;
+ MarshallerKind = marshallerKind;
+ }
- public void FreeNative() {}
+ public Type ManagedType { get; }
+ public CustomTypeMarshallerKind MarshallerKind { get; }
+ public int BufferSize { get; set; }
+ public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref;
+ public CustomTypeMarshallerFeatures Features { get; set; }
}
-```
-
-The analyzer will report an error if neither the constructor nor the `ToManaged` method is defined. When one of those two methods is missing, the direction of marshalling (managed to native/native to managed) that relies on the missing method is considered unsupported for the corresponding managed type. The `FreeNative` method is only required when there are resources that need to be released.
-
-
-> :question: Does this API surface and shape work for all marshalling scenarios we plan on supporting? It may have issues with the current "layout class" by-value `[Out]` parameter marshalling where the runtime updates a `class` typed object in place. We already recommend against using classes for interop for performance reasons and a struct value passed via `ref` or `out` with the same members would cover this scenario.
-
-If the native type `TNative` also has a public `Value` property, then the value of the `Value` property will be passed to native code instead of the `TNative` value itself. As a result, the type `TNative` will be allowed to require marshalling and the type of the `Value` property will be required be passable to native code without any additional marshalling. If the `Value` property is settable, then when marshalling in the native-to-managed direction, a default value of `TNative` will have its `Value` property set to the native value. If `Value` does not have a setter, then marshalling from native to managed is not supported.
-If a `Value` property is provided, the developer may also provide a ref-returning or readonly-ref-returning `GetPinnableReference` method. The `GetPinnableReference` method will be called before the `Value` property getter is called. The ref returned by `GetPinnableReference` will be pinned with a `fixed` statement, but the pinned value will not be used (it acts exclusively as a side-effect).
-
-A `ref` or `ref readonly` typed `Value` property is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type to pin the desired `ref` to return and then use `System.Runtime.CompilerServices.Unsafe.AsPointer` to get a pointer from the `ref` that will have already been pinned by the time the `Value` getter is called.
+public enum CustomTypeMarshallerKind
+{
+ Value
+}
-```csharp
-[NativeMarshalling(typeof(TMarshaler))]
-public struct TManaged
+[Flags]
+public enum CustomTypeMarshallerFeatures
{
- // ...
+ 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
}
+[Flags]
+public 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,
+}
+```
+
+The `NativeMarshallingAttribute` and `MarshalUsingAttribute` attributes would require that the provided native type `TNative` is a `struct` that does not require any marshalling and has the `CustomTypeMarshallerAttribute` with the first parameter being a `typeof()` of the managed type (with the managed type named TManaged in this example), an optional `CustomTypeMarshallerKind`, `CustomTypeMarshallerDirection`, and optional `CustomTypeMarshallerFeatures`:
-public struct TMarshaler
+```csharp
+[CustomTypeMarshaller(typeof(TManaged), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.Ref, Features = CustomTypeMarshallerFeatures.None)]
+partial struct TNative
{
- public TMarshaler(TManaged managed) {}
+ public TNative(TManaged managed) {}
public TManaged ToManaged() {}
+}
+```
- public void FreeNative() {}
-
- public ref TNative GetPinnableReference() {}
+If the attribute specifies the `Direction` is either `In` or `Ref`, then the example constructor above must be provided. If the attribute specifies the `Direction` is `Out` or `Ref`, then the `ToManaged` method must be provided. If the `Direction` property is unspecified, then it will be treated as if the user provided `CustomTypeMarshallerDirection.Ref`. The analyzer will report an error if the attribute provides `CustomTypeMarshallerDirection.None`, as a marshaller that supports no direction is unusable.
- public TNative* Value { get; set; }
-}
+If the attribute provides the `CustomTypeMarshallerFeatures.UnmanagedResources` flag to the `Features` property then a `void`-returning parameterless instance method name `FreeNative` must be provided. This method can be used to release any non-managed resources used during marshalling.
-```
+> :question: Does this API surface and shape work for all marshalling scenarios we plan on supporting? It may have issues with the current "layout class" by-value `[Out]` parameter marshalling where the runtime updates a `class` typed object in place. We already recommend against using classes for interop for performance reasons and a struct value passed via `ref` or `out` with the same members would cover this scenario.
### Performance features
#### Pinning
-Since C# 7.3 added a feature to enable custom pinning logic for user types, we should also add support for custom pinning logic. If the user provides a `GetPinnableReference` method on the managed type that matches the requirements to be used in a `fixed` statement and the pointed-to type would not require any additional marshalling, then we will support using pinning to marshal the managed value when possible. The analyzer should issue a warning when the pointed-to type would not match the final native type, accounting for the `Value` property on the native type. Since `MarshalUsingAttribute` is applied at usage time instead of at type authoring time, we will not enable the pinning feature since the implementation of `GetPinnableReference` is likely designed to match the default marshalling rules provided by the type author, not the rules provided by the marshaller provided by the `MarshalUsingAttribute`.
+Since C# 7.3 added a feature to enable custom pinning logic for user types, we should also add support for custom pinning logic. If the user provides a `GetPinnableReference` method on the managed type that matches the requirements to be used in a `fixed` statement and the pointed-to type would not require any additional marshalling, then we will support using pinning to marshal the managed value when possible. The analyzer should issue a warning when the pointed-to type would not match the final native type, accounting for any optional features used by the custom marshaller type. Since `MarshalUsingAttribute` is applied at usage time instead of at type authoring time, we will not enable the pinning feature since the implementation of `GetPinnableReference` is likely designed to match the default marshalling rules provided by the type author, not the rules provided by the marshaller provided by the `MarshalUsingAttribute`.
#### Caller-allocated memory
-Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `TNative` type provides additional members with the following signatures, then it will opt in to using a caller-allocated buffer:
+Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `[CustomTypeMarshaller]` attribute sets the `Features` property to value with the `CustomTypeMarshallerFeatures.CallerAllocatedBuffer` flag, then `TNative` type must provide additional constructor with the following signature and set the `BufferSize` field on the `CustomTypeMarshallerAttribute`. It will then be opted-in to using a caller-allocated buffer:
```csharp
+[CustomTypeMarshaller(typeof(TManaged), BufferSize = /* */, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)]
partial struct TNative
{
public TNative(TManaged managed, Span<byte> buffer) {}
-
- public const int BufferSize = /* */;
-
- public const bool RequiresStackBuffer = /* */;
}
```
-When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. If a stack-allocated buffer is a requirement, the `RequiresStackBuffer` field should be set to `true` and the `buffer` will be guaranteed to be allocated on the stack. Setting the `RequiresStackBuffer` field to `false` is the same as omitting the field definition. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. This may also be provided by providing a two-parameter constructor with a default value for the second parameter.
+When these `CallerAllocatedBuffer` feature flag is present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios.
-Type authors can pass down the `buffer` pointer to native code by defining a `Value` property that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. If `RequiresStackBuffer` is not provided or set to `false`, the `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span.
+Type authors can pass down the `buffer` pointer to native code by using the `TwoStageMarshalling` feature to provide a `ToNativeValue` method that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. The `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span.
### Determining if a type is doesn't need marshalling
@@ -238,7 +268,7 @@ All generated stubs will be marked with [`SkipLocalsInitAttribute`](https://docs
### Special case: Transparent Structures
-There has been discussion about Transparent Structures, structure types that are treated as their underlying types when passed to native code. The support for a `Value` property on a generated marshalling type supports the transparent struct support. For example, we could support strongly typed `HRESULT` returns with this model as shown below:
+There has been discussion about Transparent Structures, structure types that are treated as their underlying types when passed to native code. The source-generated model supports this design through the `TwoStageMarshalling` feature flag on the `CustomTypeMarshaller` attribute.
```csharp
[NativeMarshalling(typeof(HRESULT))]
@@ -251,46 +281,78 @@ struct HResult
public readonly int Result;
}
+[CustomTypeMarshaller(typeof(HResult), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
struct HRESULT
{
+ private HResult managed;
public HRESULT(HResult hr)
{
- Value = hr;
+ managed = hr;
}
- public HResult ToManaged() => new HResult(Value);
- public int Value { get; set; }
+ public HResult ToManaged() => managed;
+ public int ToNativeValue() => managed.Result;
}
```
+For the more detailed specification, we will use the example below:
+
+```csharp
+[NativeMarshalling(typeof(TMarshaller))]
+public struct TManaged
+{
+ // ...
+}
+
+[CustomTypeMarshaller(typeof(TManaged))]
+public struct TMarshaller
+{
+ public TMarshaller(TManaged managed) {}
+ public TManaged ToManaged() {}
+
+ public ref T GetPinnableReference() {}
+
+ public unsafe TNative* ToNativeValue();
+ public unsafe void FromNativeValue(TNative*);
+}
+
+```
+
In this case, the underlying native type would actually be an `int`, but the user could use the strongly-typed `HResult` type as the public surface area.
-> :question: Should we support transparent structures on manually annotated types that wouldn't need marshalling otherwise? If we do, we should do so in an opt-in manner to make it possible to have a `Value` property on the type without assuming that it is for interop in all cases.
+If a type `TMarshaller` with the `CustomTypeMarshaller` attribute specifies the `TwoStageMarshalling` feature, then it must provide the `ToNativeValue` feature if it supports the `In` direction, and the `FromNativeValue` method if it supports the `Out` direction. The return value of the `ToNativeValue` method will be passed to native code instead of the `TMarshaller` value itself. As a result, the type `TMarshaller` will be allowed to require marshalling and the return type of the `ToNativeValue` method, will be required be passable to native code without any additional marshalling. When marshalling in the native-to-managed direction, a default value of `TMarshaller` will have the `FromNativeValu` method called with the native value. If we are marshalling a scenario where a single frame covers the whole native call and we are marshalling in and out, then the same instance of the marshller will be reused.
+
+If the `TwoStageMarshalling` feature is specified, the developer may also provide a ref-returning or readonly-ref-returning `GetPinnableReference` method. The `GetPinnableReference` method will be called before the `ToNativeValue` method is called. The ref returned by `GetPinnableReference` will be pinned with a `fixed` statement, but the pinned value will not be used (it acts exclusively as a side-effect). As a result, `GetPinnableReference` can return a `ref` to any `T` that can be used in a fixed statement (a C# `unmanaged` type).
+
+A `ref` or `ref readonly` typed `ToNativeValue` method is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type to pin the desired `ref` to return and then use `System.Runtime.CompilerServices.Unsafe.AsPointer` to get a pointer from the `ref` that will have already been pinned by the time the `ToNativeValue` method is called.
+
+> :question: Should we support transparent structures on manually annotated types that wouldn't need marshalling otherwise? If we do, we should do so in an opt-in manner to make it possible to have a `ToNativeValue` method on the type without assuming that it is for interop in all cases.
#### Example: ComWrappers marshalling with Transparent Structures
Building on this Transparent Structures support, we can also support ComWrappers marshalling with this proposal via the manually-decorated types approach:
```csharp
-[NativeMarshalling(typeof(ComWrappersMarshaler<Foo, FooComWrappers>))]
+[NativeMarshalling(typeof(ComWrappersMarshaler<Foo, FooComWrappers>), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
class Foo
{}
-struct ComWrappersMarshaler<TClass, TComWrappers>
- where TComWrappers : ComWrappers, new()
+struct FooComWrappersMarshaler
{
- private static readonly TComWrappers ComWrappers = new TComWrappers();
+ private static readonly FooComWrappers ComWrappers = new FooComWrappers();
private IntPtr nativeObj;
- public ComWrappersMarshaler(TClass obj)
+ public ComWrappersMarshaler(Foo obj)
{
nativeObj = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.None);
}
- public IntPtr Value { get => nativeObj; set => nativeObj = value; }
+ public IntPtr ToNativeValue() => nativeObj;
+
+ public void FromNativeValue(IntPtr value) => nativeObj = value;
- public TClass ToManaged() => (TClass)ComWrappers.GetOrCreateObjectForComInstance(nativeObj, CreateObjectFlags.None);
+ public Foo ToManaged() => (Foo)ComWrappers.GetOrCreateObjectForComInstance(nativeObj, CreateObjectFlags.None);
public unsafe void FreeNative()
{
diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md
index 4e2b859114d..edd42ecefb5 100644
--- a/docs/project/list-of-diagnostics.md
+++ b/docs/project/list-of-diagnostics.md
@@ -158,18 +158,15 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| __`SYSLIB1052`__ | Specified configuration is not supported by source-generated P/Invokes |
| __`SYSLIB1053`__ | Current target framework is not supported by source-generated P/Invokes |
| __`SYSLIB1054`__ | Specified LibraryImportAttribute arguments cannot be forwarded to DllImportAttribute |
-| __`SYSLIB1055`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1056`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1057`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1058`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1059`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1060`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1061`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1062`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1063`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1064`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1065`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1066`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1067`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1068`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
-| __`SYSLIB1069`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1055`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1056`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1057`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1058`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1059`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1060`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1061`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1062`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1063`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1064`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1065`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
+| __`SYSLIB1066`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* |
diff --git a/eng/generators.targets b/eng/generators.targets
index 24c5c2db8d8..4b55dfbaafe 100644
--- a/eng/generators.targets
+++ b/eng/generators.targets
@@ -52,7 +52,14 @@
Include="$(CoreLibSharedDir)System\Runtime\InteropServices\StringMarshalling.cs" />
<!-- Only add the following files if we are on the latest TFM (that is, net7). -->
- <Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)'" Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\GeneratedMarshallingAttribute.cs" />
+ <Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)'"
+ Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\GeneratedMarshallingAttribute.cs" />
+ <Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)'"
+ Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\CustomTypeMarshallerKind.cs" />
+ <Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)'"
+ Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\CustomTypeMarshallerDirection.cs" />
+ <Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)'"
+ Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\CustomTypeMarshallerFeatures.cs" />
<!-- Only add the following files if we are on the latest TFM (that is, net7) and the project is SPCL or has references to System.Runtime.CompilerServices.Unsafe and System.Memory -->
<Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)'
diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs
index e8c53bf15da..9cfde639cb6 100644
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs
+++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs
@@ -46,7 +46,7 @@ namespace System.Runtime
IDynamicCastableGetInterfaceImplementation = 9,
}
- internal static partial class InternalCalls
+ internal static class InternalCalls
{
//
// internalcalls for System.GC.
@@ -59,9 +59,9 @@ namespace System.Runtime
RhpCollect(generation, mode);
}
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- private static partial void RhpCollect(int generation, InternalGCCollectionMode mode);
+ private static extern void RhpCollect(int generation, InternalGCCollectionMode mode);
[RuntimeExport("RhGetGcTotalMemory")]
internal static long RhGetGcTotalMemory()
@@ -69,14 +69,14 @@ namespace System.Runtime
return RhpGetGcTotalMemory();
}
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- private static partial long RhpGetGcTotalMemory();
+ private static extern long RhpGetGcTotalMemory();
[RuntimeExport("RhStartNoGCRegion")]
internal static int RhStartNoGCRegion(long totalSize, bool hasLohSize, long lohSize, bool disallowFullBlockingGC)
{
- return RhpStartNoGCRegion(totalSize, hasLohSize, lohSize, disallowFullBlockingGC);
+ return RhpStartNoGCRegion(totalSize, hasLohSize ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, lohSize, disallowFullBlockingGC ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);
}
[RuntimeExport("RhEndNoGCRegion")]
@@ -282,53 +282,55 @@ namespace System.Runtime
// These either do not need to be called in cooperative mode or, in some cases, MUST be called in preemptive
// mode. Note that they must use the Cdecl calling convention due to a limitation in our .obj file linking
// support.
+ // We use DllImport here instead of DllImport as we don't want to add a dependency on source-generated
+ // interop support to Test.CoreLib.
//------------------------------------------------------------------------------------------------------------
// Block the current thread until at least one object needs to be finalized (returns true) or
// memory is low (returns false and the finalizer thread should initiate a garbage collection).
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial uint RhpWaitForFinalizerRequest();
+ internal static extern uint RhpWaitForFinalizerRequest();
// Indicate that the current round of finalizations is complete.
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial void RhpSignalFinalizationComplete();
+ internal static extern void RhpSignalFinalizationComplete();
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial void RhpAcquireCastCacheLock();
+ internal static extern void RhpAcquireCastCacheLock();
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial void RhpReleaseCastCacheLock();
+ internal static extern void RhpReleaseCastCacheLock();
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial ulong RhpGetTickCount64();
+ internal static extern ulong RhpGetTickCount64();
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial void RhpAcquireThunkPoolLock();
+ internal static extern void RhpAcquireThunkPoolLock();
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial void RhpReleaseThunkPoolLock();
+ internal static extern void RhpReleaseThunkPoolLock();
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial IntPtr RhAllocateThunksMapping();
+ internal static extern IntPtr RhAllocateThunksMapping();
// Enters a no GC region, possibly doing a blocking GC if there is not enough
// memory available to satisfy the caller's request.
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial int RhpStartNoGCRegion(long totalSize, [MarshalAs(UnmanagedType.Bool)] bool hasLohSize, long lohSize, [MarshalAs(UnmanagedType.Bool)] bool disallowFullBlockingGC);
+ internal static extern int RhpStartNoGCRegion(long totalSize, Interop.BOOL hasLohSize, long lohSize, Interop.BOOL disallowFullBlockingGC);
// Exits a no GC region, possibly doing a GC to clean up the garbage that
// the caller allocated.
- [LibraryImport(Redhawk.BaseName)]
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- internal static partial int RhpEndNoGCRegion();
+ internal static extern int RhpEndNoGCRegion();
}
}
diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/MarshalAsAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/MarshalAsAttribute.cs
deleted file mode 100644
index f88624501e3..00000000000
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/MarshalAsAttribute.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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
-{
- [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)]
- public sealed partial class MarshalAsAttribute : Attribute
- {
- public MarshalAsAttribute(UnmanagedType unmanagedType)
- {
- Value = unmanagedType;
- }
- public MarshalAsAttribute(short unmanagedType)
- {
- Value = (UnmanagedType)unmanagedType;
- }
-
- public UnmanagedType Value { get; }
-
- // Fields used with SubType = ByValArray and LPArray.
- // Array size = parameter(PI) * PM + C
- public UnmanagedType ArraySubType;
- public short SizeParamIndex; // param index PI
- public int SizeConst; // constant C
- }
-}
diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs
index af496123251..8d75a17eb40 100644
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs
+++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs
@@ -3,8 +3,8 @@
namespace System.Runtime.InteropServices
{
+ // Included because the C# compiler requires it for the unmanaged constraint
public enum UnmanagedType
{
- Bool = 0x2, // 4 byte boolean value (true != 0, false == 0)
}
}
diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs
index c77d833498c..cfb9d20e832 100644
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs
+++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs
@@ -300,9 +300,10 @@ namespace System.Runtime
return RhpGetCurrentThreadStackTrace(pOutputBuffer, (uint)((outputBuffer != null) ? outputBuffer.Length : 0), new UIntPtr(&pOutputBuffer));
}
- [LibraryImport(Redhawk.BaseName)]
+ // Use DllImport here instead of LibraryImport because this file is used by Test.CoreLib.
+ [DllImport(Redhawk.BaseName)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
- private static unsafe partial int RhpGetCurrentThreadStackTrace(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame);
+ private static unsafe extern int RhpGetCurrentThreadStackTrace(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame);
// Worker for RhGetCurrentThreadStackTrace. RhGetCurrentThreadStackTrace just allocates a transition
// frame that will be used to seed the stack trace and this method does all the real work.
diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs
index 015151e3ea1..b6901b1ce50 100644
--- a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs
+++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs
@@ -85,8 +85,8 @@ namespace System.Runtime
internal static unsafe Array RhNewArray(EETypePtr pEEType, int length)
=> RhNewArray(pEEType.ToPointer(), length);
- [LibraryImport(RuntimeLibrary)]
- internal static unsafe partial void RhAllocateNewObject(IntPtr pEEType, uint flags, void* pResult);
+ [DllImport(RuntimeLibrary)]
+ internal static unsafe extern void RhAllocateNewObject(IntPtr pEEType, uint flags, void* pResult);
[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpFallbackFailFast")]
diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
index 0cba6dfc1f5..9d53870e996 100644
--- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
+++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
@@ -3,11 +3,6 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<TargetFramework>netstandard2.0</TargetFramework>
- <EnableLibraryImportGenerator>true</EnableLibraryImportGenerator>
- <!--
- SYSLIB1053: LibraryImportGenerator Target Framework Not Supported.
- -->
- <NoWarn>$(NoWarn);SYSLIB1053</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>FEATURE_GC_STRESS;$(DefineConstants)</DefineConstants>
@@ -33,6 +28,9 @@
<IntermediatesDir>$(ArtifactsObjDir)\coreclr\$(TargetOS).$(TargetArchitecture).$(CoreCLRConfiguration)</IntermediatesDir>
<IntermediatesDir Condition="'$(Ninja)' == 'false' and $([MSBuild]::IsOsPlatform('Windows'))">$(IntermediatesDir)\ide</IntermediatesDir>
</PropertyGroup>
+ <PropertyGroup>
+ <CommonPath>$([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common', 'src'))</CommonPath>
+ </PropertyGroup>
<ItemGroup Condition="'$(InPlaceRuntime)' == 'true'">
<Compile Include="..\..\Runtime.Base\src\System\Runtime\CachedInterfaceDispatch.cs">
<Link>Runtime.Base\src\System\Runtime\CachedInterfaceDispatch.cs</Link>
@@ -67,9 +65,6 @@
<Compile Include="..\..\Runtime.Base\src\System\Runtime\TypeCast.cs">
<Link>Runtime.Base\src\System\Runtime\TypeCast.cs</Link>
</Compile>
- <Compile Include="..\..\Runtime.Base\src\System\Runtime\InteropServices\MarshalAsAttribute.cs">
- <Link>Runtime.Base\src\System\Runtime\InteropServices\MarshalAsAttribute.cs</Link>
- </Compile>
<Compile Include="..\..\Runtime.Base\src\System\Runtime\InteropServices\UnsafeGCHandle.cs">
<Link>Runtime.Base\src\System\Runtime\InteropServices\UnsafeGCHandle.cs</Link>
</Compile>
@@ -82,6 +77,9 @@
<Compile Include="$(AotCommonPath)\Internal\Runtime\TransitionBlock.cs">
<Link>Common\TransitionBlock.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\Interop.BOOL.cs">
+ <Link>Common\Interop\Windows\Interop.BOOL.cs</Link>
+ </Compile>
</ItemGroup>
<ItemGroup Condition="'$(InPlaceRuntime)' == 'true'">
<Compile Include="$(IntermediatesDir)\nativeaot\Runtime\Full\AsmOffsets.cs" />
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 &apos;BlittableTypeAttribute&apos; 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 &apos;{0}&apos; is marked with &apos;BlittableTypeAttribute&apos; but is not blittable.
+ /// Looks up a localized string similar to Add missing features to the &apos;CustomTypeMarshallerAttribute&apos; 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&lt;byte&gt; 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 &apos;Span&lt;byte&gt;&apos; should specify that it supports the &apos;CallerAllocatedBuffer&apos; 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 &apos;{0}&apos; must have a &apos;public const int BufferSize&apos; field that specifies the size of the stack buffer because it has a constructor that takes a caller-allocated Span&lt;byte&gt;.
+ /// Looks up a localized string similar to The type &apos;{0}&apos; provides a constructor taking a caller-allocated &apos;Span&lt;byte&gt;&apos; but does not specify that it supports the &apos;CallerAllocatedBuffer&apos; 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 &apos;Span&lt;byte&gt;&apos; is specified on the native type, the type must set the BufferSize field on the &apos;System.Runtime.InteropServices.CustomTypeMarshallerAttribute&apos; 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 &apos;{0}&apos; must set the &apos;BufferSize&apos; field on the applied &apos;System.Runtime.InteropServices.CustomTypeMarshallerAttribute&apos; to specify the size of the caller-allocated buffer because it has a constructor that takes a caller-allocated &apos;Span&lt;byte&gt;&apos;.
+ /// </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 &apos;{0}&apos; 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 &apos;DllImportAttribute&apos; will not have a value corresponding to &apos;{0}&apos;..
/// </summary>
@@ -122,7 +140,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("CannotForwardToDllImportDescription", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to &apos;{0}&apos; has no equivalent in &apos;DllImportAtttribute&apos; 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 &apos;LibraryImportAttribute&apos; arguments cannot be forwarded to &apos;DllImportAttribute&apos;.
/// </summary>
@@ -140,43 +158,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("CannotForwardToDllImportTitle", resourceCulture);
}
}
-
- /// <summary>
- /// Looks up a localized string similar to The &apos;BlittableTypeAttribute&apos; and &apos;NativeMarshallingAttribute&apos; attributes are mutually exclusive..
- /// </summary>
- internal static string CannotHaveMultipleMarshallingAttributesDescription {
- get {
- return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesDescription", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Type &apos;{0}&apos; is marked with &apos;BlittableTypeAttribute&apos; and &apos;NativeMarshallingAttribute&apos;. 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 &apos;GenericContiguousCollectionMarshallerAttribute&apos; must have at least one of the two marshalling methods as well as a &apos;ManagedValues&apos; property of type &apos;Span&lt;T&gt;&apos; for some &apos;T&apos; and a &apos;NativeValueStorage&apos; property of type &apos;Span&lt;byte&gt;&apos; 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 &apos;{0}&apos; must be a value type and have a constructor that takes two parameters, one of type &apos;{1}&apos; and an &apos;int&apos;, or have a parameterless instance method named &apos;ToManaged&apos; that returns &apos;{1}&apos; as well as a &apos;ManagedValues&apos; property of type &apos;Span&lt;T&gt;&apos; for some &apos;T&apos; and a &apos;NativeValueStorage&apos; property of type &apos;Span&lt;byte&gt;&apos;.
- /// </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 &apos;{0}&apos; 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 &apos;{0}&apos; configuration for parameter &apos;{1}&apos; 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 &apos;{0}&apos; configuration for the return value of method &apos;{1}&apos; 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 &apos;{0}&apos; for &apos;{1}&apos; 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 &apos;ConstantElementCount&apos; or &apos;ElementCountInfo&apos; may be used in a &apos;MarshalUsingAttribute&apos; for a given &apos;ElementIndirectionLevel&apos;.
+ /// Looks up a localized string similar to Only one of &apos;ConstantElementCount&apos; or &apos;ElementCountInfo&apos; may be used in a &apos;MarshalUsingAttribute&apos; for a given &apos;ElementIndirectionDepth&apos;.
/// </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 &apos;PreserveSig&apos; set to &apos;false&apos; 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 &apos;LibraryImport&apos;.
/// </summary>
@@ -266,7 +248,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("ConvertToLibraryImport", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to Use &apos;LibraryImportAttribute&apos; instead of &apos;DllImportAttribute&apos; 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 &apos;{0}&apos; with &apos;LibraryImportAttribute&apos; instead of &apos;DllImportAttribute&apos; 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 &apos;LibraryImportAttribute&apos; instead of &apos;DllImportAttribute&apos; 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 &apos;LibraryImport&apos; 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 &apos;LibraryImport&apos; with &apos;{0}&apos; 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 &apos;Direction&apos; property on the &apos;CustomTypeMarshallerAttribute&apos; to a value that sets at least one known flag value on the &apos;CustomTypeMarshallerDirection&apos; enum.
+ /// </summary>
+ internal static string CustomMarshallerTypeMustSupportDirectionDescription {
+ get {
+ return ResourceManager.GetString("CustomMarshallerTypeMustSupportDirectionDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The native type &apos;{0}&apos; must set the &apos;Direction&apos; property on the &apos;CustomTypeMarshallerAttribute&apos; to a value that sets at least one known flag value on the &apos;CustomTypeMarshallerDirection&apos; enum.
+ /// </summary>
+ internal static string CustomMarshallerTypeMustSupportDirectionMessage {
+ get {
+ return ResourceManager.GetString("CustomMarshallerTypeMustSupportDirectionMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The &apos;CustomTypeMarshallerAttribute&apos; 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 &apos;CustomTypeMarshallerAttribute&apos; on &apos;{0}&apos; 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 &apos;{0}&apos; 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 &apos;{0}&apos; 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 &apos;FreeNative&apos; method should specify that it supports the &apos;UnmanagedResources&apos; feature..
+ /// </summary>
+ internal static string FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription {
+ get {
+ return ResourceManager.GetString("FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; provides a &apos;FreeNative&apos; method but does not specify that it supports the &apos;UnmanagedResources&apos; 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 &apos;FromNativeValue&apos; method should specify that it supports the &apos;TwoStageMarshalling&apos; feature..
+ /// </summary>
+ internal static string FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription {
+ get {
+ return ResourceManager.GetString("FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; provides a &apos;FromNativeValue&apos; method but does not specify that it supports the &apos;TwoStageMarshalling&apos; 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 &apos;GetPinnableReference&apos; (after accounting for &apos;ref&apos;) 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 &apos;GetPinnableReference&apos; 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 &apos;{0}&apos; has a &apos;GetPinnableReference&apos; 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 &apos;TwoStageMarshalling&apos; feature requires a &apos;TNativeType ToNativeValue()&apos; method for the &apos;In&apos; direction..
+ /// </summary>
+ internal static string InTwoStageMarshallingRequiresToNativeValueDescription {
+ get {
+ return ResourceManager.GetString("InTwoStageMarshallingRequiresToNativeValueDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The marshaller type &apos;{0}&apos; supports marshalling in the &apos;In&apos; direction with the &apos;TwoStageMarshalling&apos; feature must provide a &apos;ToNativeValue&apos; 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 &apos;{0}&apos; is contained in a type &apos;{1}&apos; that is not marked &apos;partial&apos;. P/Invoke source generation will ignore method &apos;{0}&apos;..
/// </summary>
@@ -374,7 +446,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("InvalidAttributedMethodContainingTypeMissingModifiersMessage", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to Methods marked with &apos;LibraryImportAttribute&apos; should be &apos;static&apos;, &apos;partial&apos;, and non-generic. P/Invoke source generation will ignore methods that are non-&apos;static&apos;, non-&apos;partial&apos;, or generic..
/// </summary>
@@ -383,7 +455,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("InvalidAttributedMethodDescription", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to Method &apos;{0}&apos; should be &apos;static&apos;, &apos;partial&apos;, and non-generic when marked with &apos;LibraryImportAttribute&apos;. P/Invoke source generation will ignore method &apos;{0}&apos;..
/// </summary>
@@ -392,7 +464,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("InvalidAttributedMethodSignatureMessage", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to Invalid &apos;LibraryImportAttribute&apos; 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 &apos;StringMarshalling&apos; and &apos;StringMarshallingCustomType&apos; 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 &apos;StringMarshalling&apos; and &apos;StringMarshallingCustomType&apos; on method &apos;{0}&apos; is invalid. {1}.
/// </summary>
@@ -419,7 +491,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("InvalidStringMarshallingConfigurationMessage", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to &apos;StringMarshallingCustomType&apos; must be specified when &apos;StringMarshalling&apos; is set to &apos;StringMarshalling.Custom&apos;..
/// </summary>
@@ -428,7 +500,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("InvalidStringMarshallingConfigurationMissingCustomType", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to &apos;StringMarshalling&apos; should be set to &apos;StringMarshalling.Custom&apos; when &apos;StringMarshallingCustomType&apos; 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 &apos;GetPinnableReference&apos; are not applicable in any scenarios where a &apos;Value&apos; property is not also required..
+ /// Looks up a localized string similar to The element type of the &apos;ReadOnlySpan&apos; returned by &apos;GetManagedValuesSource&apos; must be the same as the element type returned by &apos;GetManagedValuesDestination&apos;..
/// </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 &apos;GetPinnableReference&apos; method cannot be provided on the native type &apos;{0}&apos; unless a &apos;Value&apos; property is also provided.
+ /// Looks up a localized string similar to The element type of the &apos;ReadOnlySpan&apos; returned by &apos;GetManagedValuesSource&apos; must be the same as the element type returned by &apos;GetManagedValuesDestination&apos;.
/// </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 &apos;{0}&apos; must be a closed generic so the emitted code can use a specific instantiation..
+ /// Looks up a localized string similar to A &apos;LinearCollection&apos;-kind native type that supports the &apos;CallerAllocatedBuffer&apos; feature must provide a three-parameter constructor taking the managed type as the first parameter, a &apos;Span&lt;byte&gt;&apos; 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 &apos;{0}&apos; specifies that it supports &apos;In&apos; marshalling with the &apos;CallerAllocatedBuffer&apos; feature for &apos;{1}&apos; but does not provide a three-parameter constructor that takes a &apos;{1}&apos; , a &apos;Span&lt;byte&gt;&apos;, and an &apos;int&apos;.
+ /// </summary>
+ internal static string LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage {
+ get {
+ return ResourceManager.GetString("LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A &apos;LinearCollection&apos;-kind native type that supports marshalling in the &apos;In&apos; direction must provide a &apos;GetManagedValuesSource&apos; that returns a &apos;ReadOnlySpan&lt;&gt;&apos; and a &apos;GetNativeValuesDestination&apos; method that returns a &apos;Span&lt;byte&gt;&apos;..
+ /// </summary>
+ internal static string LinearCollectionInRequiresCollectionMethodsDescription {
+ get {
+ return ResourceManager.GetString("LinearCollectionInRequiresCollectionMethodsDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; specifies that is supports marshalling in the &apos;In&apos; direction, but it does not provide a &apos;GetManagedValuesSource&apos; that returns a &apos;ReadOnlySpan&lt;&gt;&apos; and a &apos;GetNativeValuesDestination&apos; method that returns a &apos;Span&lt;byte&gt;&apos;.
+ /// </summary>
+ internal static string LinearCollectionInRequiresCollectionMethodsMessage {
+ get {
+ return ResourceManager.GetString("LinearCollectionInRequiresCollectionMethodsMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A &apos;LinearCollection&apos;-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 &apos;{0}&apos; specifies that it supports &apos;In&apos; marshalling of &apos;{1}&apos; but does not provide a two-parameter constructor that takes a &apos;{1}&apos; as the first parameter and an &apos;int&apos; as the second parameter.
+ /// </summary>
+ internal static string LinearCollectionInRequiresTwoParameterConstructorMessage {
+ get {
+ return ResourceManager.GetString("LinearCollectionInRequiresTwoParameterConstructorMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A &apos;LinearCollection&apos;-kind native type that supports marshalling in the &apos;Out&apos; direction must provide a &apos;GetManagedValuesDestination&apos; that takes an &apos;int&apos; and returns a &apos;Span&lt;&gt;&apos; and a &apos;GetNativeValuesSource&apos; method that takes an &apos;int&apos; and rreturns a &apos;ReadOnlySpan&lt;byte&gt;&apos;..
+ /// </summary>
+ internal static string LinearCollectionOutRequiresCollectionMethodsDescription {
+ get {
+ return ResourceManager.GetString("LinearCollectionOutRequiresCollectionMethodsDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; specifies that it supports marshalling in the &apos;Out&apos; direction, but it does not provide a &apos;GetManagedValuesDestination&apos; that takes an &apos;int&apos; and returns a &apos;Span&lt;&gt;&apos; and a &apos;GetNativeValuesSource&apos; method that takes an &apos;int&apos; and rreturns a &apos;ReadOnlySpan&lt;byte&gt;&apos;.
+ /// </summary>
+ internal static string LinearCollectionOutRequiresCollectionMethodsMessage {
+ get {
+ return ResourceManager.GetString("LinearCollectionOutRequiresCollectionMethodsMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A &apos;LinearCollection&apos;-kind native type that supports marshalling in the &apos;Out&apos; direction must provide a constructor that takes the size of the native element as an &apos;int&apos;..
+ /// </summary>
+ internal static string LinearCollectionOutRequiresIntConstructorDescription {
+ get {
+ return ResourceManager.GetString("LinearCollectionOutRequiresIntConstructorDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; specifies that it supports marshalling in the &apos;Out&apos; direction, but it does not provide a constructor that takes the size of the native element as an &apos;int&apos;..
+ /// </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 &apos;System.Runtime.InteropServices.CustomMarshallerDirection&apos; 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 &apos;{0}&apos; 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 &apos;GetPinnableReference&apos; are not applicable in any scenarios where &apos;TwoStageMarshalling&apos; is not also required..
+ /// </summary>
+ internal static string MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription {
+ get {
+ return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The &apos;GetPinnableReference&apos; method cannot be provided on the native type &apos;{0}&apos; unless the &apos;TwoStageMarshalling&apos; 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 &apos;System.Runtime.InteropServices.CustomMarshallerKind&apos; 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 &apos;{0}&apos; 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 &apos;System.Runtime.InteropServices.CustomTypeMarshallerAttribute&apos; 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 &apos;{0}&apos; does not specify a managed type in the &apos;System.Runtime.InteropServices.CustomTypeMarshallerAttribute&apos; 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 &apos;{0}&apos; 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 &apos;{0}&apos; for managed type &apos;{1}&apos; 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 &apos;{0}&apos; for the type &apos;{1}&apos; is not blittable.
+ /// Looks up a localized string similar to The native type &apos;{0}&apos; for type &apos;{1}&apos; 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 &apos;GetPinnableReference&apos; 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 &apos;{0}&apos; is null.
+ /// Looks up a localized string similar to The native type &apos;{0}&apos; must be pointer sized because the managed type &apos;{1}&apos; has a &apos;GetPinnableReference&apos; 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 &apos;GetPinnableReference&apos; can be cast to the native type..
+ /// Looks up a localized string similar to A native type for a given type must have the &apos;System.Runtime.InteropServices.CustomTypeMarshallerAttribute&apos; 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 &apos;{0}&apos; must be pointer sized because the managed type &apos;{1}&apos; has a &apos;GetPinnableReference&apos; method.
+ /// Looks up a localized string similar to The native type for the type &apos;{0}&apos; must be a type with the &apos;System.Runtime.InteropServices.CustomTypeMarshallerAttribute&apos; 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 &apos;Value&apos; or &apos;LinearCollection&apos;-kind native type that supports marshalling in the &apos;Out&apos; direction must provide a &apos;ToManaged&apos; 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 &apos;{0}&apos; must be a value type and have a constructor that takes one parameter of type &apos;{1}&apos; or a parameterless instance method named &apos;ToManaged&apos; that returns &apos;{1}&apos;.
+ /// Looks up a localized string similar to The type &apos;{0}&apos; specifies it supports marshalling in the &apos;Out&apos; direction, but it does not provide a &apos;ToManaged&apos; 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 &apos;Value&apos; property must not be a &apos;ref&apos; or &apos;readonly ref&apos; property..
+ /// Looks up a localized string similar to The &apos;TwoStageMarshalling&apos; feature requires a &apos;void FromNativeValue(TNativeType value)&apos; method for the &apos;Out&apos; 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 &apos;Value&apos; property on the native type &apos;{0}&apos; must not be a &apos;ref&apos; or &apos;readonly ref&apos; property..
+ /// Looks up a localized string similar to The marshaller type &apos;{0}&apos; supports marshalling in the &apos;Out&apos; direction with the &apos;TwoStageMarshalling&apos; feature, but it does not provide a &apos;FromNativeValue&apos; instance method that returns &apos;void&apos; 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 &apos;Value&apos; property must not be a &apos;ref&apos; or &apos;readonly ref&apos; 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 &apos;Value&apos; property on the native type &apos;{0}&apos; must not be a &apos;ref&apos; or &apos;readonly ref&apos; 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 &apos;SafeHandle&apos; 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 &apos;LibraryImportAttribute&apos; 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 &apos;ToNativeValue&apos; method should specify that it supports the &apos;TwoStageMarshalling&apos; feature..
+ /// </summary>
+ internal static string ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription {
+ get {
+ return ResourceManager.GetString("ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; provides a &apos;ToNativeValue&apos; method but does not specify that it supports the &apos;TwoStageMarshalling&apos; 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 &apos;ToNativeValue&apos; and the parameter type of &apos;FromNativeValue&apos; 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 &apos;ToNativeValue&apos; and the parameter type of &apos;FromNativeValue&apos; 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 &apos;{0}&apos; is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter &apos;{1}&apos;..
/// </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 &apos;{1}&apos;..
/// </summary>
@@ -644,7 +914,7 @@ namespace Microsoft.Interop {
return ResourceManager.GetString("TypeNotSupportedMessageParameterWithDetails", resourceCulture);
}
}
-
+
/// <summary>
/// Looks up a localized string similar to The type &apos;{0}&apos; is not supported by source-generated P/Invokes. The generated source will not handle marshalling of the return value of method &apos;{1}&apos;..
/// </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 &apos;{1}&apos;..
/// </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&apos;s &apos;Value&apos; property must have a getter to support marshalling from managed to native..
+ /// Looks up a localized string similar to The &apos;UnmanagedResources&apos; feature requires a &apos;void FreeNative()&apos; 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 &apos;Value&apos; property on the native type &apos;{0}&apos; must have a getter.
+ /// Looks up a localized string similar to The marshaller type &apos;{0}&apos; supports marshalling with the &apos;UnmanagedResources&apos; feature, but it does not provide a parameterless &apos;FreeNative&apos; instance method that returns &apos;void&apos;.
/// </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&apos;s &apos;Value&apos; property must have a setter to support marshalling from native to managed..
+ /// Looks up a localized string similar to A &apos;Value&apos;-kind native type that supports the &apos;CallerAllocatedBuffer&apos; feature must provide a two-parameter constructor taking the managed type and a &apos;Span&lt;byte&gt;&apos; 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 &apos;{0}&apos; specifies that it supports &apos;In&apos; marshalling with the &apos;CallerAllocatedBuffer&apos; feature for &apos;{1}&apos; but does not provide a one-parameter constructor that takes a &apos;{1}&apos; and &apos;Span&lt;byte&gt;&apos; as parameters.
+ /// </summary>
+ internal static string ValueInCallerAllocatedBufferRequiresSpanConstructorMessage {
+ get {
+ return ResourceManager.GetString("ValueInCallerAllocatedBufferRequiresSpanConstructorMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A &apos;Value&apos;-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 &apos;Value&apos; property on the native type &apos;{0}&apos; must have a setter.
+ /// Looks up a localized string similar to The type &apos;{0}&apos; specifies that it supports &apos;In&apos; marshalling of &apos;{1}&apos; but does not provide a one-parameter constructor that takes a &apos;{1}&apos; 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&lt;byte&gt; 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&lt;byte&gt;' 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&lt;byte&gt;</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&lt;byte&gt;'</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&lt;T&gt;' for some 'T' and a 'NativeValueStorage' property of type 'Span&lt;byte&gt;' 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&lt;T&gt;' for some 'T' and a 'NativeValueStorage' property of type 'Span&lt;byte&gt;'</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&lt;byte&gt;' 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&lt;byte&gt;', 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&lt;byte&gt;' 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&lt;byte&gt;' 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&lt;&gt;' and a 'GetNativeValuesDestination' method that returns a 'Span&lt;byte&gt;'.</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&lt;&gt;' and a 'GetNativeValuesDestination' method that returns a 'Span&lt;byte&gt;'</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&lt;&gt;' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan&lt;byte&gt;'.</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&lt;&gt;' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan&lt;byte&gt;'</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&lt;byte&gt;' 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&lt;byte&gt;' 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 &apos;ConstantElementCount&apos; or &apos;ElementCountInfo&apos; may be used in a &apos;MarshalUsingAttribute&apos; for a given &apos;ElementIndirectionLevel&apos;.
+ /// Looks up a localized string similar to Only one of &apos;ConstantElementCount&apos; or &apos;ElementCountInfo&apos; may be used in a &apos;MarshalUsingAttribute&apos; for a given &apos;ElementIndirectionDepth&apos;.
/// </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 &apos;ElementIndirectionLevel&apos; {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 &apos;ElementIndirectionDepth&apos; {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 &apos;MarshalUsingAttribute&apos; or &apos;MarshalAsAttribute&apos;..
+ /// Looks up a localized string similar to Marshalling bool without explicit marshalling information is not supported. Specify either &apos;MarshalUsingAttribute&apos; or &apos;MarshalAsAttribute&apos;..
/// </summary>
internal static string MarshallingBoolAsUndefinedNotSupported {
get {
@@ -297,18 +297,18 @@ namespace Microsoft.Interop {
/// <summary>
/// Looks up a localized string similar to The &apos;Value&apos; property must not be a &apos;ref&apos; or &apos;readonly ref&apos; 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 &apos;Value&apos; property on the native type &apos;{0}&apos; must not be a &apos;ref&apos; or &apos;readonly ref&apos; 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&lt;T&gt;' for some 'T' and a 'NativeValueStorage' property of type 'Span&lt;byte&gt;' 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&lt;T&gt;' for some 'T' and a 'NativeValueStorage' property of type 'Span&lt;byte&gt;'</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;