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:
authorElinor Fung <elfung@microsoft.com>2022-07-01 22:34:46 +0300
committerGitHub <noreply@github.com>2022-07-01 22:34:46 +0300
commit19811c279a5f97bbda203530a26d9e7244faeaa4 (patch)
treebd26b291a2edf2d9a7fadc0c2c69bd78f588c9e7
parent6d8a6c25768eab0fda32365e10812ef4ca68ff39 (diff)
Basic support for stateless linear collection marshalling (#71473)
Basic stateless linear collection marshalling for blittable elements Not handled: - caller-allocated buffer - guaranteed unmarshal - pinnable reference - non-blittable element marshalling - element scenarios on custom marshallers
-rw-r--r--docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md54
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs6
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs154
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallerShape.cs307
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs62
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs280
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs79
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/Strings.resx3
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.cs.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.de.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.es.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.fr.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.it.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ja.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ko.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pl.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pt-BR.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ru.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.tr.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hans.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hant.xlf5
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs9
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ElementUnmanagedTypeAttribute.cs17
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.Custom.cs142
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.V1.cs246
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs140
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs386
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs7
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs133
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.V1.cs32
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs89
32 files changed, 1759 insertions, 454 deletions
diff --git a/docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md b/docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md
index 072a97ab18e..ee1da9ff467 100644
--- a/docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md
+++ b/docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md
@@ -41,7 +41,7 @@ namespace System.Runtime.InteropServices.Marshalling;
- ManagedType = managedType;
- MarshallerKind = marshallerKind;
- }
--
+-
- public Type ManagedType { get; }
- public CustomTypeMarshallerKind MarshallerKind { get; }
- public int BufferSize { get; set; }
@@ -51,13 +51,13 @@ namespace System.Runtime.InteropServices.Marshalling;
- {
- }
- }
--
+-
- public enum CustomTypeMarshallerKind
- {
- Value,
- LinearCollection
- }
--
+-
- [Flags]
- public enum CustomTypeMarshallerFeatures
- {
@@ -108,8 +108,8 @@ namespace System.Runtime.InteropServices.Marshalling;
+ /// </summary>
+ public int BufferSize { get; set; }
+ }
-+
-+
++
++
+ /// <summary>
+ /// Base class attribute for custom marshaller attributes.
+ /// </summary>
@@ -125,7 +125,7 @@ namespace System.Runtime.InteropServices.Marshalling;
+ /// </summary>
+ public sealed class GenericPlaceholder { }
+ }
-+
++
+ /// <summary>
+ /// Specify marshallers used in the managed to unmanaged direction (that is, P/Invoke)
+ /// </summary>
@@ -137,23 +137,23 @@ namespace System.Runtime.InteropServices.Marshalling;
+ /// </summary>
+ /// <param name="managedType">Managed type to marshal</param>
+ public ManagedToUnmanagedMarshallersAttribute(Type managedType) { }
-+
++
+ /// <summary>
+ /// Marshaller to use when a parameter of the managed type is passed by-value or with the <c>in</c> keyword.
+ /// </summary>
+ public Type? InMarshaller { get; set; }
-+
++
+ /// <summary>
+ /// Marshaller to use when a parameter of the managed type is passed by-value or with the <c>ref</c> keyword.
+ /// </summary>
+ public Type? RefMarshaller { get; set; }
-+
++
+ /// <summary>
+ /// Marshaller to use when a parameter of the managed type is passed by-value or with the <c>out</c> keyword.
+ /// </summary>
+ public Type? OutMarshaller { get; set; }
+ }
-+
++
+ /// <summary>
+ /// Specify marshallers used in the unmanaged to managed direction (that is, Reverse P/Invoke)
+ /// </summary>
@@ -165,23 +165,23 @@ namespace System.Runtime.InteropServices.Marshalling;
+ /// </summary>
+ /// <param name="managedType">Managed type to marshal</param>
+ public UnmanagedToManagedMarshallersAttribute(Type managedType) { }
-+
++
+ /// <summary>
+ /// Marshaller to use when a parameter of the managed type is passed by-value or with the <c>in</c> keyword.
+ /// </summary>
+ public Type? InMarshaller { get; set; }
-+
++
+ /// <summary>
+ /// Marshaller to use when a parameter of the managed type is passed by-value or with the <c>ref</c> keyword.
+ /// </summary>
+ public Type? RefMarshaller { get; set; }
-+
++
+ /// <summary>
+ /// Marshaller to use when a parameter of the managed type is passed by-value or with the <c>out</c> keyword.
+ /// </summary>
+ public Type? OutMarshaller { get; set; }
+ }
-+
++
+ /// <summary>
+ /// Specify marshaller for array-element marshalling and default struct field marshalling.
+ /// </summary>
@@ -195,7 +195,7 @@ namespace System.Runtime.InteropServices.Marshalling;
+ /// <param name="elementMarshaller">Marshaller type to use for marshalling <paramref name="managedType"/>.</param>
+ public ElementMarshallerAttribute(Type managedType, Type elementMarshaller) { }
+ }
-+
++
+ /// <summary>
+ /// Specifies that a particular generic parameter is the collection element's unmanaged type.
+ /// </summary>
@@ -470,10 +470,10 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
public static class ManagedToNative
{
public static TNative AllocateContainerForUnmanagedElements(TCollection managed, out int numElements); // Can throw exceptions
-
+
public static ReadOnlySpan<TManagedElement> GetManagedValuesSource(TCollection managed); // Can throw exceptions
- public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative nativeValue, int numElements); // Can throw exceptions
+ public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative unmanaged, int numElements); // Can throw exceptions
public static ref TOther GetPinnableReference(TManaged managed); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
@@ -495,10 +495,10 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
public static class ManagedToNative
{
public static TNative AllocateContainerForUnmanagedElements(TCollection managed, Span<TOther> buffer, out int numElements); // Can throw exceptions
-
+
public static ReadOnlySpan<TManagedElement> GetManagedValuesSource(TCollection managed); // Can throw exceptions
- public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative nativeValue, int numElements); // Can throw exceptions
+ public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative unmanaged, int numElements); // Can throw exceptions
public static ref TOther GetPinnableReference(TManaged managed); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.
@@ -517,11 +517,11 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
{
public static class NativeToManaged
{
- public static TCollection AllocateContainerForManagedElements(int length); // Can throw exceptions
+ public static TCollection AllocateContainerForManagedElements(TNative unmanaged, int length); // Can throw exceptions
public static Span<TManagedElement> GetManagedValuesDestination(T[] managed) => managed; // Can throw exceptions
- public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative nativeValue, int numElements); // Can throw exceptions
+ public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative unmanaged, int numElements); // Can throw exceptions
public static void Free(TNative native); // Optional. Should not throw exceptions.
}
@@ -540,11 +540,11 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
{
public static class NativeToManaged
{
- public static TCollection AllocateContainerForManagedElementsGuaranteed(int length); // Should not throw exceptions other than OutOfMemoryException.
+ public static TCollection AllocateContainerForManagedElementsGuaranteed(TNative unmanaged, int length); // Should not throw exceptions other than OutOfMemoryException.
public static Span<TManagedElement> GetManagedValuesDestination(T[] managed) => managed; // Can throw exceptions
- public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative nativeValue, int numElements); // Can throw exceptions
+ public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative unmanaged, int numElements); // Can throw exceptions
public static void Free(TNative native); // Optional. Should not throw exceptions.
}
@@ -584,7 +584,7 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
public ReadOnlySpan<TManagedElement> GetManagedValuesSource(); // Can throw exceptions.
- public Span<byte> GetNativeValuesDestination(); // Can throw exceptions.
+ public Span<byte> GetUnmanagedValuesDestination(); // Can throw exceptions.
public ref TIgnored GetPinnableReference(); // Optional. Can throw exceptions.
@@ -615,7 +615,7 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
public ReadOnlySpan<TManagedElement> GetManagedValuesSource(); // Can throw exceptions.
- public Span<byte> GetNativeValuesDestination(); // Can throw exceptions.
+ public Span<byte> GetUnmanagedValuesDestination(); // Can throw exceptions.
public ref TIgnored GetPinnableReference(); // Optional. Can throw exceptions.
@@ -642,7 +642,7 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
public void FromUnmanaged(TNative value); // Should not throw exceptions.
- public ReadOnlySpan<TUnmanagedElement> GetNativeValuesSource(int length); // Can throw exceptions.
+ public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int length); // Can throw exceptions.
public Span<TManagedElement> GetManagedValuesDestination(int length); // Can throw exceptions.
@@ -667,7 +667,7 @@ static class TMarshaller<T, U, V..., [ElementUnmanagedType] TUnmanagedElement> w
public void FromUnmanaged(TNative value); // Should not throw exceptions.
- public ReadOnlySpan<TUnmanagedElement> GetNativeValuesSource(int length); // Can throw exceptions.
+ public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int length); // Can throw exceptions.
public Span<TManagedElement> GetManagedValuesDestination(int length); // Can throw exceptions.
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs
index 1eb6bd51082..6f2347900df 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs
@@ -45,6 +45,10 @@ namespace Microsoft.Interop
{
return new DelegateTypeInfo(typeName, diagonsticFormattedName);
}
+ if (type.TypeKind == TypeKind.TypeParameter)
+ {
+ return new TypeParameterTypeInfo(typeName, diagonsticFormattedName);
+ }
if (type.IsValueType)
{
return new ValueTypeInfo(typeName, diagonsticFormattedName, type.IsRefLikeType);
@@ -80,6 +84,8 @@ namespace Microsoft.Interop
public sealed record DelegateTypeInfo(string FullTypeName, string DiagnosticFormattedName) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName);
+ public sealed record TypeParameterTypeInfo(string FullTypeName, string DiagnosticFormattedName) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName);
+
public sealed record ValueTypeInfo(string FullTypeName, string DiagnosticFormattedName, bool IsByRefLike) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName);
public sealed record ReferenceTypeInfo(string FullTypeName, string DiagnosticFormattedName) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName);
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 7a0cbac591a..2f45d23393e 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
@@ -19,7 +19,9 @@ namespace Microsoft.Interop
bool HasState,
MarshallerShape Shape,
bool IsStrictlyBlittable,
- ManagedTypeInfo? BufferElementType);
+ ManagedTypeInfo? BufferElementType,
+ ManagedTypeInfo? CollectionElementType,
+ MarshallingInfo? CollectionElementMarshallingInfo);
public readonly record struct CustomTypeMarshallers(
ImmutableDictionary<Scenario, CustomTypeMarshallerData> Scenarios)
@@ -68,10 +70,10 @@ namespace Microsoft.Interop
Bidirectional = ManagedToUnmanaged | UnmanagedToManaged
}
- public static bool IsLinearCollectionEntryPoint(ITypeSymbol entryPointType)
+ public static bool IsLinearCollectionEntryPoint(INamedTypeSymbol entryPointType)
{
- // TODO: Check for linear collection marshaller - ElementUnmanagedType attribute on last generic parameter
- return false;
+ return entryPointType.IsGenericType
+ && entryPointType.TypeParameters.Last().GetAttributes().Any(attr => attr.AttributeClass.ToDisplayString() == TypeNames.ElementUnmanagedTypeAttribute);
}
public static bool HasEntryPointMarshallerAttribute(ITypeSymbol entryPointType)
@@ -79,11 +81,31 @@ namespace Microsoft.Interop
return entryPointType.GetAttributes().Any(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomMarshallerAttribute);
}
- public static bool TryGetMarshallersFromEntryType(
+ public static bool TryGetValueMarshallersFromEntryType(
+ INamedTypeSymbol entryPointType,
+ ITypeSymbol managedType,
+ Compilation compilation,
+ out CustomTypeMarshallers? marshallers)
+ {
+ return TryGetMarshallersFromEntryType(entryPointType, managedType, isLinearCollectionMarshalling: false, compilation, getMarshallingInfoForElement: null, out marshallers);
+ }
+
+ public static bool TryGetLinearCollectionMarshallersFromEntryType(
+ INamedTypeSymbol entryPointType,
+ ITypeSymbol managedType,
+ Compilation compilation,
+ Func<ITypeSymbol, MarshallingInfo> getMarshallingInfo,
+ out CustomTypeMarshallers? marshallers)
+ {
+ return TryGetMarshallersFromEntryType(entryPointType, managedType, isLinearCollectionMarshalling: true, compilation, getMarshallingInfo, out marshallers);
+ }
+
+ private static bool TryGetMarshallersFromEntryType(
INamedTypeSymbol entryPointType,
ITypeSymbol managedType,
bool isLinearCollectionMarshalling,
Compilation compilation,
+ Func<ITypeSymbol, MarshallingInfo> getMarshallingInfoForElement,
out CustomTypeMarshallers? marshallers)
{
marshallers = null;
@@ -91,6 +113,9 @@ namespace Microsoft.Interop
if (attrs is null || attrs.Length == 0)
return false;
+ // We expect a callback for getting the element marshalling info when handling linear collection marshalling
+ Debug.Assert(!isLinearCollectionMarshalling || getMarshallingInfoForElement is not null);
+
Dictionary<Scenario, CustomTypeMarshallerData> scenarios = new();
foreach (AttributeData attr in attrs)
{
@@ -98,11 +123,6 @@ namespace Microsoft.Interop
// Verify the defined marshaller is for the managed type.
ITypeSymbol? managedTypeOnAttr = attr.ConstructorArguments[0].Value as ITypeSymbol;
- if (!SymbolEqualityComparer.Default.Equals(managedType, managedTypeOnAttr)
- && !compilation.HasImplicitConversion(managedType, managedTypeOnAttr))
- {
- continue;
- }
// Verify any instantiation of Generic parameters is provided by entry point.
// TODO: Hard failure based on previous implementation
@@ -112,7 +132,8 @@ namespace Microsoft.Interop
// Verify any instantiated managed types are derived properly.
// TODO: Hard failure based on previous implementation
- if (!TypeSymbolsConstructedFromEqualTypes(managedType, managedTypeInst))
+ if (!managedType.IsConstructedFromEqualTypes(managedTypeInst)
+ && !compilation.HasImplicitConversion(managedType, managedTypeInst))
return false;
var marshallerScenario = (Scenario)attr.ConstructorArguments[1].Value!;
@@ -121,6 +142,31 @@ namespace Microsoft.Interop
if (marshallerTypeOnAttr is null)
continue;
+ ITypeSymbol marshallerType = marshallerTypeOnAttr;
+ if (isLinearCollectionMarshalling && marshallerTypeOnAttr is INamedTypeSymbol namedMarshallerType)
+ {
+ // Update the marshaller type with resolved type arguments based on the entry point type
+ // We expect the entry point to already have its type arguments updated based on the managed type
+ Stack<string> nestedTypeNames = new Stack<string>();
+ INamedTypeSymbol currentType = namedMarshallerType;
+ while (currentType is not null)
+ {
+ if (currentType.IsConstructedFromEqualTypes(entryPointType))
+ break;
+
+ nestedTypeNames.Push(currentType.Name);
+ currentType = currentType.ContainingType;
+ }
+
+ currentType = entryPointType;
+ foreach (string name in nestedTypeNames)
+ {
+ currentType = currentType.GetTypeMembers(name).First();
+ }
+
+ marshallerType = currentType;
+ }
+
// TODO: We can probably get rid of MarshallingDirection and just use Scenario instead
MarshallingDirection direction = marshallerScenario switch
{
@@ -149,7 +195,7 @@ namespace Microsoft.Interop
// TODO: Report invalid shape for scenario
// Skip checking for bidirectional support for Default scenario - always take / store marshaller data
- CustomTypeMarshallerData? data = GetMarshallerDataForType(marshallerTypeOnAttr, direction, managedTypeOnAttr, compilation);
+ CustomTypeMarshallerData? data = GetMarshallerDataForType(marshallerType, direction, managedType, isLinearCollectionMarshalling, compilation, getMarshallingInfoForElement);
// TODO: Should we fire a diagnostic for duplicated scenarios or just take the last one?
if (data is null
@@ -170,15 +216,6 @@ namespace Microsoft.Interop
};
return true;
-
- 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)
- };
- }
}
/// <summary>
@@ -212,7 +249,8 @@ namespace Microsoft.Interop
}
}
- if (innerType.ToDisplayString() != TypeNames.CustomTypeMarshallerAttributeGenericPlaceholder)
+ if (innerType.ToDisplayString() != TypeNames.CustomTypeMarshallerAttributeGenericPlaceholder
+ && innerType.ToDisplayString() != TypeNames.CustomMarshallerAttributeGenericPlaceholder)
{
return managedType;
}
@@ -292,11 +330,17 @@ namespace Microsoft.Interop
({ ReturnsByRef: true } or { ReturnsByRefReadonly: true }));
}
- private static CustomTypeMarshallerData? GetMarshallerDataForType(ITypeSymbol marshallerType, MarshallingDirection direction, ITypeSymbol managedType, Compilation compilation)
+ private static CustomTypeMarshallerData? GetMarshallerDataForType(
+ ITypeSymbol marshallerType,
+ MarshallingDirection direction,
+ ITypeSymbol managedType,
+ bool isLinearCollectionMarshaller,
+ Compilation compilation,
+ Func<ITypeSymbol, MarshallingInfo> getMarshallingInfo)
{
if (marshallerType is { IsStatic: true, TypeKind: TypeKind.Class })
{
- return GetStatelessMarshallerDataForType(marshallerType, direction, managedType, compilation);
+ return GetStatelessMarshallerDataForType(marshallerType, direction, managedType, isLinearCollectionMarshaller, compilation, getMarshallingInfo);
}
if (marshallerType.IsValueType)
{
@@ -305,24 +349,31 @@ namespace Microsoft.Interop
return null;
}
- private static CustomTypeMarshallerData? GetStatelessMarshallerDataForType(ITypeSymbol marshallerType, MarshallingDirection direction, ITypeSymbol managedType, Compilation compilation)
+ private static CustomTypeMarshallerData? GetStatelessMarshallerDataForType(ITypeSymbol marshallerType, MarshallingDirection direction, ITypeSymbol managedType, bool isLinearCollectionMarshaller, Compilation compilation, Func<ITypeSymbol, MarshallingInfo>? getMarshallingInfo)
{
- (MarshallerShape shape, Dictionary<MarshallerShape, IMethodSymbol> methodsByShape) = StatelessMarshallerShapeHelper.GetShapeForType(marshallerType, managedType, compilation);
+ (MarshallerShape shape, StatelessMarshallerShapeHelper.MarshallerMethods methods) = StatelessMarshallerShapeHelper.GetShapeForType(marshallerType, managedType, isLinearCollectionMarshaller, compilation);
+ ITypeSymbol? collectionElementType = null;
ITypeSymbol? nativeType = null;
if (direction.HasFlag(MarshallingDirection.ManagedToUnmanaged))
{
if (!shape.HasFlag(MarshallerShape.CallerAllocatedBuffer) && !shape.HasFlag(MarshallerShape.ToUnmanaged))
return null;
- IMethodSymbol method;
- if (methodsByShape.TryGetValue(MarshallerShape.CallerAllocatedBuffer, out method))
+ if (isLinearCollectionMarshaller)
{
- nativeType = method.ReturnType;
+ // Element type is the type parameter of the ReadOnlySpan returned by GetManagedValuesSource
+ collectionElementType = ((INamedTypeSymbol)methods.ManagedValuesSource.ReturnType).TypeArguments[0];
}
- else if (methodsByShape.TryGetValue(MarshallerShape.ToUnmanaged, out method))
+
+ // Native type is the return type of ConvertToUnmanaged / AllocateContainerForUnmanagedElement
+ if (methods.ToUnmanagedWithBuffer is not null)
{
- nativeType = method.ReturnType;
+ nativeType = methods.ToUnmanagedWithBuffer.ReturnType;
+ }
+ else if (methods.ToUnmanaged is not null)
+ {
+ nativeType = methods.ToUnmanaged.ReturnType;
}
}
@@ -331,14 +382,25 @@ namespace Microsoft.Interop
if (!shape.HasFlag(MarshallerShape.GuaranteedUnmarshal) && !shape.HasFlag(MarshallerShape.ToManaged))
return null;
- IMethodSymbol method;
- if (methodsByShape.TryGetValue(MarshallerShape.GuaranteedUnmarshal, out method))
+ if (isLinearCollectionMarshaller)
{
- nativeType = method.Parameters[0].Type;
+ // Native type is the first parameter of GetUnmanagedValuesSource
+ nativeType = methods.UnmanagedValuesSource.Parameters[0].Type;
+
+ // Element type is the type parameter of the Span returned by GetManagedValuesDestination
+ collectionElementType = ((INamedTypeSymbol)methods.ManagedValuesDestination.ReturnType).TypeArguments[0];
}
- else if (methodsByShape.TryGetValue(MarshallerShape.ToManaged, out method))
+ else
{
- nativeType = method.Parameters[0].Type;
+ // Native type is the first parameter of ConvertToManaged or ConvertToManagedGuaranteed
+ if (methods.ToManagedGuaranteed is not null)
+ {
+ nativeType = methods.ToManagedGuaranteed.Parameters[0].Type;
+ }
+ else if (methods.ToManaged is not null)
+ {
+ nativeType = methods.ToManaged.Parameters[0].Type;
+ }
}
}
@@ -350,9 +412,17 @@ namespace Microsoft.Interop
return null;
ManagedTypeInfo bufferElementType = null;
- if (methodsByShape.TryGetValue(MarshallerShape.CallerAllocatedBuffer, out IMethodSymbol methodWithBuffer))
+ if (methods.ToUnmanagedWithBuffer is not null)
+ {
+ bufferElementType = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(((INamedTypeSymbol)methods.ToUnmanagedWithBuffer.Parameters[1].Type).TypeArguments[0]);
+ }
+
+ ManagedTypeInfo? collectionElementTypeInfo = null;
+ MarshallingInfo? collectionElementMarshallingInfo = null;
+ if (collectionElementType is not null)
{
- bufferElementType = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(((INamedTypeSymbol)methodWithBuffer.Parameters[1].Type).TypeArguments[0]);
+ collectionElementTypeInfo = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(collectionElementType);
+ collectionElementMarshallingInfo = getMarshallingInfo(collectionElementType);
}
return new CustomTypeMarshallerData(
@@ -361,7 +431,9 @@ namespace Microsoft.Interop
HasState: false,
shape,
nativeType.IsStrictlyBlittable(),
- bufferElementType);
+ bufferElementType,
+ collectionElementTypeInfo,
+ collectionElementMarshallingInfo);
}
private static CustomTypeMarshallerData? GetStatefulMarshallerDataForType(ITypeSymbol marshallerType, MarshallingDirection direction, ITypeSymbol managedType, Compilation compilation)
@@ -410,7 +482,9 @@ namespace Microsoft.Interop
HasState: true,
shape,
nativeType.IsStrictlyBlittable(),
- bufferElementType);
+ bufferElementType,
+ CollectionElementType: null,
+ CollectionElementMarshallingInfo: null);
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallerShape.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallerShape.cs
index d115e7fbb93..ec37715128b 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallerShape.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallerShape.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
@@ -92,47 +93,116 @@ namespace Microsoft.Interop
public static class StatelessMarshallerShapeHelper
{
- public static (MarshallerShape, Dictionary<MarshallerShape, IMethodSymbol>) GetShapeForType(ITypeSymbol marshallerType, ITypeSymbol managedType, Compilation compilation)
+ public record MarshallerMethods
{
- MarshallerShape shape = MarshallerShape.None;
- var methodsByShape = new Dictionary<MarshallerShape, IMethodSymbol>();
+ public IMethodSymbol? ToUnmanaged;
+ public IMethodSymbol? ToUnmanagedWithBuffer;
+ public IMethodSymbol? ToManaged;
+ public IMethodSymbol? ToManagedGuaranteed;
+
+ // Linear collection
+ public IMethodSymbol? ManagedValuesSource;
+ public IMethodSymbol? UnmanagedValuesDestination;
+ public IMethodSymbol? ManagedValuesDestination;
+ public IMethodSymbol? UnmanagedValuesSource;
+ }
- IMethodSymbol? method = GetConvertToUnmanagedMethod(marshallerType, managedType);
- if (method is not null)
- AddMethod(MarshallerShape.ToUnmanaged, method);
+ public static (MarshallerShape, MarshallerMethods) GetShapeForType(ITypeSymbol marshallerType, ITypeSymbol managedType, bool isLinearCollectionMarshaller, Compilation compilation)
+ {
+ MarshallerShape shape = MarshallerShape.None;
+ MarshallerMethods methods = new();
INamedTypeSymbol spanOfT = compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!;
- method = GetConvertToUnmanagedWithCallerAllocatedBufferMethod(marshallerType, managedType, spanOfT, out _);
- if (method is not null)
- AddMethod(MarshallerShape.CallerAllocatedBuffer, method);
-
- method = GetConvertToManagedMethod(marshallerType, managedType);
- if (method is not null)
- AddMethod(MarshallerShape.ToManaged, method);
+ if (isLinearCollectionMarshaller)
+ {
+ // Managed -> Unmanaged
+ INamedTypeSymbol readOnlySpanOfT = compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!;
+ IMethodSymbol? allocateUnmanaged = LinearCollection.AllocateContainerForUnmanagedElements(marshallerType, managedType);
+ IMethodSymbol? allocateUnmanagedWithBuffer = LinearCollection.AllocateContainerForUnmanagedElementsWithCallerAllocatedBuffer(marshallerType, managedType, spanOfT);
+ IMethodSymbol? managedSource = LinearCollection.GetManagedValuesSource(marshallerType, managedType, readOnlySpanOfT);
+ IMethodSymbol? unmanagedDestination = LinearCollection.GetUnmanagedValuesDestination(marshallerType, spanOfT);
+ if ((allocateUnmanaged is not null || allocateUnmanagedWithBuffer is not null)
+ && managedSource is not null
+ && unmanagedDestination is not null)
+ {
+ if (allocateUnmanaged is not null)
+ shape |= MarshallerShape.ToUnmanaged;
- method = GetConvertToManagedGuaranteedMethod(marshallerType, managedType);
- if (method is not null)
- AddMethod(MarshallerShape.GuaranteedUnmarshal, method);
+ if (allocateUnmanagedWithBuffer is not null)
+ shape |= MarshallerShape.CallerAllocatedBuffer;
- method = GetStatelessGetPinnableReference(marshallerType, managedType);
- if (method is not null)
- AddMethod(MarshallerShape.StatelessPinnableReference, method);
+ methods = methods with
+ {
+ ToUnmanaged = allocateUnmanaged,
+ ToUnmanagedWithBuffer = allocateUnmanagedWithBuffer,
+ ManagedValuesSource = managedSource,
+ UnmanagedValuesDestination = unmanagedDestination
+ };
+ }
- method = GetStatelessFree(marshallerType);
- if (method is not null)
- AddMethod(MarshallerShape.Free, method);
+ // Unmanaged -> Managed
+ IMethodSymbol? allocateManaged = LinearCollection.AllocateContainerForManagedElements(marshallerType, managedType);
+ IMethodSymbol? allocateManagedGuaranteed = LinearCollection.AllocateContainerForManagedElementsGuaranteed(marshallerType, managedType, spanOfT);
+ IMethodSymbol? managedDestination = LinearCollection.GetManagedValuesDestination(marshallerType, managedType, spanOfT);
+ IMethodSymbol? unmanagedSource = LinearCollection.GetUnmanagedValuesSource(marshallerType, readOnlySpanOfT);
+ if ((allocateManaged is not null || allocateManagedGuaranteed is not null)
+ && managedDestination is not null
+ && unmanagedSource is not null)
+ {
+ if (allocateManaged is not null)
+ shape |= MarshallerShape.ToManaged;
- return (shape, methodsByShape);
+ if (allocateManagedGuaranteed is not null)
+ shape |= MarshallerShape.GuaranteedUnmarshal;
- void AddMethod(MarshallerShape shapeToAdd, IMethodSymbol methodToAdd)
+ methods = methods with
+ {
+ ToManaged = allocateManaged,
+ ToManagedGuaranteed = allocateManagedGuaranteed,
+ ManagedValuesDestination = managedDestination,
+ UnmanagedValuesSource = unmanagedSource
+ };
+ }
+ }
+ else
{
- methodsByShape.Add(shapeToAdd, methodToAdd);
- shape |= shapeToAdd;
+ IMethodSymbol? toUnmanaged = Value.ConvertToUnmanaged(marshallerType, managedType);
+ if (toUnmanaged is not null)
+ shape |= MarshallerShape.ToUnmanaged;
+
+ IMethodSymbol? toUnmanagedWithBuffer = Value.ConvertToUnmanagedWithCallerAllocatedBuffer(marshallerType, managedType, spanOfT);
+ if (toUnmanagedWithBuffer is not null)
+ shape |= MarshallerShape.CallerAllocatedBuffer;
+
+ IMethodSymbol? toManaged = Value.ConvertToManaged(marshallerType, managedType);
+ if (toManaged is not null)
+ shape |= MarshallerShape.ToManaged;
+
+ IMethodSymbol? toManagedGuaranteed = Value.ConvertToManagedGuaranteed(marshallerType, managedType);
+ if (toManagedGuaranteed is not null)
+ shape |= MarshallerShape.GuaranteedUnmarshal;
+
+ methods = methods with
+ {
+ ToUnmanaged = toUnmanaged,
+ ToUnmanagedWithBuffer = toUnmanagedWithBuffer,
+ ToManaged = toManaged,
+ ToManagedGuaranteed = toManagedGuaranteed
+ };
}
+
+ if (GetStatelessGetPinnableReference(marshallerType, managedType) is not null)
+ shape |= MarshallerShape.StatelessPinnableReference;
+
+ if (GetStatelessFree(marshallerType) is not null)
+ shape |= MarshallerShape.Free;
+
+ return (shape, methods);
}
private static IMethodSymbol? GetStatelessFree(ITypeSymbol type)
{
+ // static void Free(TNative unmanaged)
return type.GetMembers(ShapeMemberNames.Free)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: true });
@@ -140,6 +210,9 @@ namespace Microsoft.Interop
private static IMethodSymbol? GetStatelessGetPinnableReference(ITypeSymbol type, ITypeSymbol managedType)
{
+ // static ref TOther GetPinnableReference(TManaged managed)
+ // or
+ // static ref readonly TOther GetPinnableReference(TManaged managed)
return type.GetMembers(ShapeMemberNames.GetPinnableReference)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1 } and
@@ -147,66 +220,164 @@ namespace Microsoft.Interop
&& SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, managedType));
}
- private static IMethodSymbol? GetConvertToUnmanagedMethod(ITypeSymbol type, ITypeSymbol managedType)
+ private static bool IsSpanOfUnmanagedType(ITypeSymbol typeToCheck, ITypeSymbol spanOfT)
{
- return type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToUnmanaged)
- .OfType<IMethodSymbol>()
- .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false }
- && SymbolEqualityComparer.Default.Equals(managedType, m.Parameters[0].Type));
+ if (typeToCheck is INamedTypeSymbol namedType
+ && SymbolEqualityComparer.Default.Equals(spanOfT, namedType.ConstructedFrom)
+ && namedType.TypeArguments.Length == 1
+ && namedType.TypeArguments[0].IsUnmanagedType)
+ {
+ return true;
+ }
+
+ return false;
}
- private static IMethodSymbol? GetConvertToUnmanagedWithCallerAllocatedBufferMethod(
- ITypeSymbol type,
- ITypeSymbol managedType,
- ITypeSymbol spanOfT,
- out ITypeSymbol? spanElementType)
+ private static class Value
{
- spanElementType = null;
- IEnumerable<IMethodSymbol> methods = type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToUnmanaged)
- .OfType<IMethodSymbol>()
- .Where(m => m is { IsStatic: true, Parameters.Length: 2, ReturnsVoid: false }
- && SymbolEqualityComparer.Default.Equals(managedType, m.Parameters[0].Type));
+ internal static IMethodSymbol? ConvertToUnmanaged(ITypeSymbol type, ITypeSymbol managedType)
+ {
+ // static TNative ConvertToUnmanaged(TManaged managed)
+ return type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToUnmanaged)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false }
+ && SymbolEqualityComparer.Default.Equals(managedType, m.Parameters[0].Type));
+ }
- foreach (IMethodSymbol method in methods)
+ internal static IMethodSymbol? ConvertToUnmanagedWithCallerAllocatedBuffer(
+ ITypeSymbol type,
+ ITypeSymbol managedType,
+ ITypeSymbol spanOfT)
{
- if (IsSpanOfUnmanagedType(method.Parameters[1].Type, spanOfT, out spanElementType))
+ // static TNative ConvertToUnmanaged(TManaged managed, Span<TUnmanagedElement> buffer)
+ IEnumerable<IMethodSymbol> methods = type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToUnmanaged)
+ .OfType<IMethodSymbol>()
+ .Where(m => m is { IsStatic: true, Parameters.Length: 2, ReturnsVoid: false }
+ && SymbolEqualityComparer.Default.Equals(managedType, m.Parameters[0].Type));
+
+ foreach (IMethodSymbol method in methods)
{
- return method;
+ if (IsSpanOfUnmanagedType(method.Parameters[1].Type, spanOfT))
+ {
+ return method;
+ }
}
+
+ return null;
}
- return null;
+ internal static IMethodSymbol? ConvertToManaged(ITypeSymbol type, ITypeSymbol managedType)
+ {
+ // static TManaged ConvertToManaged(TNative unmanaged)
+ return type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToManaged)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false }
+ && SymbolEqualityComparer.Default.Equals(managedType, m.ReturnType));
+ }
- static bool IsSpanOfUnmanagedType(ITypeSymbol typeToCheck, ITypeSymbol spanOfT, out ITypeSymbol? typeArgument)
+ internal static IMethodSymbol? ConvertToManagedGuaranteed(ITypeSymbol type, ITypeSymbol managedType)
{
- typeArgument = null;
- if (typeToCheck is INamedTypeSymbol namedType
- && SymbolEqualityComparer.Default.Equals(spanOfT, namedType.ConstructedFrom)
- && namedType.TypeArguments.Length == 1
- && namedType.TypeArguments[0].IsUnmanagedType)
+ // static TManaged ConvertToManagedGuaranteed(TNative unmanaged)
+ return type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToManagedGuaranteed)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false }
+ && SymbolEqualityComparer.Default.Equals(managedType, m.ReturnType));
+ }
+ }
+
+ private static class LinearCollection
+ {
+ internal static IMethodSymbol? AllocateContainerForUnmanagedElements(ITypeSymbol type, ITypeSymbol managedType)
+ {
+ // static TNative AllocateContainerForUnmanagedElements(TCollection managed, out int numElements)
+ return type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForUnmanagedElements)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 2, ReturnsVoid: false }
+ && managedType.IsConstructedFromEqualTypes(m.Parameters[0].Type)
+ && m.Parameters[1].Type.SpecialType == SpecialType.System_Int32
+ && m.Parameters[1].RefKind == RefKind.Out);
+ }
+
+ internal static IMethodSymbol? AllocateContainerForUnmanagedElementsWithCallerAllocatedBuffer(ITypeSymbol type, ITypeSymbol managedType, ITypeSymbol spanOfT)
+ {
+ // static TNative AllocateContainerForUnmanagedElements(TCollection managed, Span<TOther> buffer, out int numElements)
+ IEnumerable<IMethodSymbol> methods = type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForUnmanagedElements)
+ .OfType<IMethodSymbol>()
+ .Where(m => m is { IsStatic: true, Parameters.Length: 3, ReturnsVoid: false }
+ && managedType.IsConstructedFromEqualTypes(m.Parameters[0].Type)
+ && m.Parameters[2].Type.SpecialType == SpecialType.System_Int32
+ && m.Parameters[2].RefKind == RefKind.Out);
+
+ foreach (IMethodSymbol method in methods)
{
- typeArgument = namedType.TypeArguments[0];
- return true;
+ if (IsSpanOfUnmanagedType(method.Parameters[1].Type, spanOfT))
+ {
+ return method;
+ }
}
- return false;
+ return null;
}
- }
- private static IMethodSymbol? GetConvertToManagedMethod(ITypeSymbol type, ITypeSymbol managedType)
- {
- return type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToManaged)
- .OfType<IMethodSymbol>()
- .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false }
- && SymbolEqualityComparer.Default.Equals(managedType, m.ReturnType));
- }
+ internal static IMethodSymbol? GetManagedValuesSource(ITypeSymbol type, ITypeSymbol managedType, ITypeSymbol readOnlySpanOfT)
+ {
+ // static ReadOnlySpan<TManagedElement> GetManagedValuesSource(TCollection managed)
+ return type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesSource)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false, ReturnType: INamedTypeSymbol returnType }
+ && managedType.IsConstructedFromEqualTypes(m.Parameters[0].Type)
+ && SymbolEqualityComparer.Default.Equals(readOnlySpanOfT, returnType.ConstructedFrom));
+ }
- private static IMethodSymbol? GetConvertToManagedGuaranteedMethod(ITypeSymbol type, ITypeSymbol managedType)
- {
- return type.GetMembers(ShapeMemberNames.Value.Stateless.ConvertToManagedGuaranteed)
- .OfType<IMethodSymbol>()
- .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false }
- && SymbolEqualityComparer.Default.Equals(managedType, m.ReturnType));
+ internal static IMethodSymbol? GetUnmanagedValuesDestination(ITypeSymbol type, ITypeSymbol spanOfT)
+ {
+ // static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative unmanaged, int numElements)
+ return type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesDestination)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 2, ReturnsVoid: false, ReturnType: INamedTypeSymbol returnType }
+ && m.Parameters[1].Type.SpecialType == SpecialType.System_Int32
+ && SymbolEqualityComparer.Default.Equals(spanOfT, returnType.ConstructedFrom));
+ }
+
+ internal static IMethodSymbol? AllocateContainerForManagedElements(ITypeSymbol type, ITypeSymbol managedType)
+ {
+ // static TCollection AllocateContainerForManagedElements(TNative unmanaged, int length);
+ return type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForManagedElements)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 2, ReturnsVoid: false }
+ && m.Parameters[1].Type.SpecialType == SpecialType.System_Int32
+ && managedType.IsConstructedFromEqualTypes(m.ReturnType));
+ }
+
+ internal static IMethodSymbol? AllocateContainerForManagedElementsGuaranteed(ITypeSymbol type, ITypeSymbol managedType, ITypeSymbol spanOfT)
+ {
+ // static TCollection AllocateContainerForManagedElementsGuaranteed(TNative unmanaged, int length);
+ return type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForManagedElements)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 2, ReturnsVoid: false }
+ && m.Parameters[1].Type.SpecialType == SpecialType.System_Int32
+ && managedType.IsConstructedFromEqualTypes(m.ReturnType));
+ }
+
+ internal static IMethodSymbol? GetManagedValuesDestination(ITypeSymbol type, ITypeSymbol managedType, ITypeSymbol spanOfT)
+ {
+ // static Span<TManagedElement> GetManagedValuesDestination(TCollection managed)
+ return type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesDestination)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 1, ReturnsVoid: false, ReturnType: INamedTypeSymbol returnType }
+ && managedType.IsConstructedFromEqualTypes(m.Parameters[0].Type)
+ && SymbolEqualityComparer.Default.Equals(spanOfT, returnType.ConstructedFrom));
+ }
+
+ internal static IMethodSymbol? GetUnmanagedValuesSource(ITypeSymbol type, ITypeSymbol readOnlySpanOfT)
+ {
+ // static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative nativeValue, int numElements)
+ return type.GetMembers(ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesSource)
+ .OfType<IMethodSymbol>()
+ .FirstOrDefault(m => m is { IsStatic: true, Parameters.Length: 2, ReturnsVoid: false, ReturnType: INamedTypeSymbol returnType }
+ && m.Parameters[1].Type.SpecialType == SpecialType.System_Int32
+ && SymbolEqualityComparer.Default.Equals(readOnlySpanOfT, returnType.ConstructedFrom));
+ }
}
}
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 5800716f2bd..b1bcb725d55 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
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
@@ -244,6 +245,10 @@ namespace Microsoft.Interop
}
else
{
+ // Collections have extra configuration, so handle them separately.
+ if (marshalInfo is NativeLinearCollectionMarshallingInfo collectionMarshallingInfo)
+ return CreateNativeCollectionMarshaller(info, context, marshallerData, collectionMarshallingInfo);
+
marshallingStrategy = new StatelessValueMarshalling(marshallerData.MarshallerType.Syntax, marshallerData.NativeType.Syntax, marshallerData.Shape);
if (marshallerData.Shape.HasFlag(MarshallerShape.CallerAllocatedBuffer))
marshallingStrategy = new StatelessCallerAllocatedBufferMarshalling(marshallingStrategy, marshallerData.MarshallerType.Syntax, marshallerData.BufferElementType.Syntax);
@@ -256,6 +261,59 @@ namespace Microsoft.Interop
: marshallingGenerator;
}
+ private IMarshallingGenerator CreateNativeCollectionMarshaller(
+ TypePositionInfo info,
+ StubCodeContext context,
+ CustomTypeMarshallerData marshallerData,
+ NativeLinearCollectionMarshallingInfo marshalInfo)
+ {
+ var elementInfo = new TypePositionInfo(marshallerData.CollectionElementType, marshallerData.CollectionElementMarshallingInfo) { ManagedIndex = info.ManagedIndex };
+ IMarshallingGenerator elementMarshaller = _elementMarshallingGenerator.Create(
+ elementInfo,
+ new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, string.Empty, context));
+
+ 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, marshalInfo.ElementCountInfo, context);
+ }
+
+ // Insert the unmanaged element type into the marshaller type
+ TypeSyntax unmanagedElementType = elementMarshaller.AsNativeType(elementInfo).GetCompatibleGenericTypeParameterSyntax();
+ TypeSyntax marshallerTypeSyntax = marshallerData.MarshallerType.Syntax;
+ marshallerTypeSyntax = marshallerTypeSyntax.ReplaceNodes(
+ marshallerTypeSyntax.DescendantNodesAndSelf().OfType<TypeSyntax>().Where(t => t.IsEquivalentTo(marshalInfo.PlaceholderTypeParameter.Syntax)),
+ (_, _) => unmanagedElementType);
+
+ ICustomTypeMarshallingStrategy marshallingStrategy;
+ bool elementIsBlittable = elementMarshaller is BlittableMarshaller;
+ if (elementIsBlittable)
+ {
+ marshallingStrategy = new StatelessLinearCollectionMarshalling(marshallerTypeSyntax, marshallerData.NativeType.Syntax, marshallerData.Shape, marshallerData.CollectionElementType.Syntax, unmanagedElementType, numElementsExpression);
+ }
+ else
+ {
+ // TODO: Handle linear collection marshalling with non-blittable elements
+ throw new MarshallingNotSupportedException(info, context);
+ }
+
+ if (marshalInfo.UseDefaultMarshalling && info.ManagedType is SzArrayType)
+ {
+ return new ArrayMarshaller(
+ new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: true),
+ elementInfo,
+ elementIsBlittable);
+ }
+
+ IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false);
+
+ // Elements in the collection must be blittable to use the pinnable marshaller.
+ return marshalInfo.IsPinnableManagedType && elementIsBlittable
+ ? new PinnableManagedValueMarshaller(marshallingGenerator)
+ : marshallingGenerator;
+ }
+
private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo)
{
// Marshalling out or return parameter, but no out marshaller is specified
@@ -323,7 +381,7 @@ namespace Microsoft.Interop
// Collections have extra configuration, so handle them here.
if (marshalInfo is NativeLinearCollectionMarshallingInfo_V1 collectionMarshallingInfo)
{
- return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, marshallingStrategy);
+ return CreateNativeCollectionMarshaller_V1(info, context, collectionMarshallingInfo, marshallingStrategy);
}
else if (marshalInfo.NativeValueType is not null)
{
@@ -399,7 +457,7 @@ namespace Microsoft.Interop
}
}
- private IMarshallingGenerator CreateNativeCollectionMarshaller(
+ private IMarshallingGenerator CreateNativeCollectionMarshaller_V1(
TypePositionInfo info,
StubCodeContext context,
NativeLinearCollectionMarshallingInfo_V1 collectionInfo,
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs
index 1b52e8712cc..6fbc03c93d0 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs
@@ -545,4 +545,284 @@ namespace Microsoft.Interop
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);
public IEnumerable<StatementSyntax> GenerateNotifyForSuccessfulInvokeStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateNotifyForSuccessfulInvokeStatements(info, context);
}
+
+ /// <summary>
+ /// 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 StatelessLinearCollectionMarshalling : ICustomTypeMarshallingStrategy
+ {
+ private readonly TypeSyntax _marshallerTypeSyntax;
+ private readonly TypeSyntax _nativeTypeSyntax;
+ private readonly MarshallerShape _shape;
+ private readonly TypeSyntax _managedElementType;
+ private readonly TypeSyntax _unmanagedElementType;
+ private readonly ExpressionSyntax _numElementsExpression;
+
+ public StatelessLinearCollectionMarshalling(TypeSyntax marshallerTypeSyntax, TypeSyntax nativeTypeSyntax, MarshallerShape shape, TypeSyntax managedElementType, TypeSyntax unmanagedElementType, ExpressionSyntax numElementsExpression)
+ {
+ _marshallerTypeSyntax = marshallerTypeSyntax;
+ _nativeTypeSyntax = nativeTypeSyntax;
+ _shape = shape;
+ _managedElementType = managedElementType;
+ _unmanagedElementType = unmanagedElementType;
+ _numElementsExpression = numElementsExpression;
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return _nativeTypeSyntax;
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
+ public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ if (!_shape.HasFlag(MarshallerShape.ToUnmanaged))
+ yield break;
+
+ (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info);
+ string numElementsIdentifier = GetNumElementsIdentifier(info, context);
+
+ // <nativeIdentifier> = <marshallerType>.AllocateContainerForUnmanagedElements(<managedIdentifier>, out <numElements>);
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(nativeIdentifier),
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForUnmanagedElements)),
+ ArgumentList(SeparatedList(new ArgumentSyntax[]
+ {
+ Argument(IdentifierName(managedIdentifier)),
+ Argument(IdentifierName(numElementsIdentifier))
+ .WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))
+ })))));
+
+ // <marshallerType>.GetUnmanagedValuesDestination(<nativeIdentifier>, <numElements>)
+ ExpressionSyntax destination =
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesDestination)),
+ ArgumentList(SeparatedList(new ArgumentSyntax[]
+ {
+ Argument(IdentifierName(nativeIdentifier)),
+ Argument(IdentifierName(numElementsIdentifier)),
+ })));
+
+ if (!info.IsByRef && info.ByValueContentsMarshalKind == ByValueContentsMarshalKind.Out)
+ {
+ // 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.
+ // <marshallerType>.GetUnmanagedValuesDestination(<nativeIdentifier>, <numElements>).Clear();
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ destination,
+ IdentifierName("Clear"))));
+ yield break;
+ }
+
+ // Skip the cast if the managed and unmanaged element types are the same
+ if (!_unmanagedElementType.IsEquivalentTo(_managedElementType))
+ {
+ // MemoryMarshal.Cast<<unmanagedElementType>, <managedElementType>>(<destination>)
+ destination = InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ GenericName(
+ Identifier("Cast"))
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SeparatedList(
+ new[]
+ {
+ _unmanagedElementType,
+ _managedElementType
+ })))),
+ ArgumentList(SingletonSeparatedList(
+ Argument(destination))));
+ }
+
+ // <marshallerType>.GetManagedValuesSource(<managedIdentifer>).CopyTo(<destination>);
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesSource)),
+ ArgumentList(SingletonSeparatedList(
+ Argument(IdentifierName(managedIdentifier))))),
+ IdentifierName("CopyTo")))
+ .AddArgumentListArguments(
+ Argument(destination)));
+ }
+
+ public IEnumerable<StatementSyntax> GenerateNotifyForSuccessfulInvokeStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
+ public IEnumerable<StatementSyntax> GeneratePinnedMarshalStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ yield return LocalDeclarationStatement(
+ VariableDeclaration(
+ PredefinedType(Token(SyntaxKind.IntKeyword)),
+ SingletonSeparatedList(
+ VariableDeclarator(GetNumElementsIdentifier(info, context)))));
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ if (!_shape.HasFlag(MarshallerShape.ToManaged))
+ yield break;
+
+ (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info);
+ string numElementsIdentifier = GetNumElementsIdentifier(info, context);
+
+ ExpressionSyntax copySource;
+ ExpressionSyntax copyDestination;
+ if (!info.IsByRef && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out))
+ {
+ // <marshallerType>.GetUnmanagedValuesDestination(<nativeIdentifier>, <numElements>)
+ copySource =
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesDestination)),
+ ArgumentList(SeparatedList(new ArgumentSyntax[]
+ {
+ Argument(IdentifierName(nativeIdentifier)),
+ Argument(IdentifierName(numElementsIdentifier)),
+ })));
+
+ // MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(<marshallerType>.GetManagedValuesSource(<managedIdentifer>)), <marshallerType>.GetManagedValuesSource(<managedIdentifer>).Length)
+ copyDestination = InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ IdentifierName("CreateSpan")),
+ ArgumentList(
+ SeparatedList(new[]
+ {
+ Argument(
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ IdentifierName("GetReference")),
+ ArgumentList(SingletonSeparatedList(
+ Argument(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesSource)),
+ ArgumentList(SingletonSeparatedList(
+ Argument(IdentifierName(managedIdentifier))))))))))
+ .WithRefKindKeyword(
+ Token(SyntaxKind.RefKeyword)),
+ Argument(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesSource)),
+ ArgumentList(SingletonSeparatedList(
+ Argument(IdentifierName(managedIdentifier))))),
+ IdentifierName("Length")))
+ })));
+
+ }
+ else
+ {
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(numElementsIdentifier),
+ _numElementsExpression));
+
+ // <managedIdentifier> = <marshallerType>.AllocateContainerForManagedElements(<nativeIdentifier>, <numElements>);
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(managedIdentifier),
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForManagedElements)),
+ ArgumentList(SeparatedList(new ArgumentSyntax[]
+ {
+ Argument(IdentifierName(nativeIdentifier)),
+ Argument(IdentifierName(numElementsIdentifier))
+ })))));
+
+ // <marshallerType>.GetUnmanagedValuesSource(<nativeIdentifier>, <numElements>)
+ copySource = InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesSource)),
+ ArgumentList(SeparatedList(new ArgumentSyntax[]
+ {
+ Argument(IdentifierName(nativeIdentifier)),
+ Argument(IdentifierName(numElementsIdentifier))
+ })));
+
+ // <marshellerType>.GetManagedValuesDestination(<managedIdentifier>)
+ copyDestination = InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ _marshallerTypeSyntax,
+ IdentifierName(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesDestination)),
+ ArgumentList(SingletonSeparatedList(Argument(IdentifierName(managedIdentifier)))));
+ }
+
+ // Skip the cast if the managed and unmanaged element types are the same
+ if (!_unmanagedElementType.IsEquivalentTo(_managedElementType))
+ {
+ // MemoryMarshal.Cast<<unmanagedElementType>, <elementType>>(<copySource>)
+ copySource = InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ GenericName(
+ Identifier("Cast"),
+ TypeArgumentList(SeparatedList(
+ new[]
+ {
+ _unmanagedElementType,
+ _managedElementType
+ })))),
+ ArgumentList(SingletonSeparatedList(
+ Argument(copySource))));
+ }
+
+ // <copySource>.CopyTo(<copyDestination>);
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ copySource,
+ IdentifierName("CopyTo")))
+ .AddArgumentListArguments(
+ Argument(copyDestination)));
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context) => Array.Empty<ArgumentSyntax>();
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true;
+
+ private static string GetNumElementsIdentifier(TypePositionInfo info, StubCodeContext context)
+ => context.GetAdditionalIdentifier(info, "numElements");
+ }
}
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 e4e72e0535d..7269e4f444c 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
@@ -146,6 +146,20 @@ namespace Microsoft.Interop
bool IsPinnableManagedType) : MarshallingInfo;
/// <summary>
+ /// Custom type marshalling via MarshalUsingAttribute or NativeMarshallingAttribute for a linear collection
+ /// </summary>
+ public sealed record NativeLinearCollectionMarshallingInfo(
+ ManagedTypeInfo EntryPointType,
+ CustomTypeMarshallers Marshallers,
+ bool IsPinnableManagedType,
+ CountInfo ElementCountInfo,
+ ManagedTypeInfo PlaceholderTypeParameter,
+ bool UseDefaultMarshalling) : NativeMarshallingAttributeInfo(
+ EntryPointType,
+ Marshallers,
+ IsPinnableManagedType);
+
+ /// <summary>
/// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute
/// </summary>
public record NativeMarshallingAttributeInfo_V1(
@@ -583,7 +597,6 @@ namespace Microsoft.Interop
ImmutableHashSet<string> inspectedElements,
ref int maxIndirectionDepthUsed)
{
- bool isLinearCollectionMarshalling = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointType);
if (ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(entryPointType))
{
if (!entryPointType.IsStatic)
@@ -592,15 +605,65 @@ namespace Microsoft.Interop
return NoMarshallingInfo.Instance;
}
- if (ManualTypeMarshallingHelper.TryGetMarshallersFromEntryType(entryPointType, type, isLinearCollectionMarshalling, _compilation, out CustomTypeMarshallers? marshallers))
+ ManagedTypeInfo entryPointTypeInfo = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(entryPointType);
+ bool isPinnableManagedType = !isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null;
+
+ bool isLinearCollectionMarshalling = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointType);
+ if (isLinearCollectionMarshalling)
{
- bool isPinnableManagedType = !isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null;
- return isLinearCollectionMarshalling
- ? NoMarshallingInfo.Instance // TODO: handle linear collection marshallers
- : new NativeMarshallingAttributeInfo(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(entryPointType), marshallers.Value, isPinnableManagedType);
- }
+ // Update the entry point type with the type arguments based on the managed type
+ if (type is IArrayTypeSymbol arrayManagedType)
+ {
+ if (entryPointType.Arity != 2)
+ {
+ _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerEntryPointTypeMustMatchArity), entryPointType.ToDisplayString(), type.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
- return NoMarshallingInfo.Instance;
+ entryPointType = entryPointType.ConstructedFrom.Construct(
+ arrayManagedType.ElementType,
+ entryPointType.TypeArguments.Last());
+ }
+ else if (type is INamedTypeSymbol namedManagedType)
+ {
+ // Entry point type for linear collection marshalling must have the arity of the managed type + 1
+ // for the [ElementUnmanagedType] placeholder
+ if (entryPointType.Arity != namedManagedType.Arity + 1)
+ {
+ _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerEntryPointTypeMustMatchArity), entryPointType.ToDisplayString(), type.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+
+ entryPointType = entryPointType.ConstructedFrom.Construct(
+ namedManagedType.TypeArguments.Add(entryPointType.TypeArguments.Last()).ToArray());
+ }
+ else
+ {
+ _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerEntryPointTypeMustMatchArity), entryPointType.ToDisplayString(), type.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+
+ int maxIndirectionDepthUsedLocal = maxIndirectionDepthUsed;
+ Func<ITypeSymbol, MarshallingInfo> getMarshallingInfoForElement = (ITypeSymbol elementType) => GetMarshallingInfo(elementType, new Dictionary<int, AttributeData>(), 1, ImmutableHashSet<string>.Empty, ref maxIndirectionDepthUsedLocal);
+ if (ManualTypeMarshallingHelper.TryGetLinearCollectionMarshallersFromEntryType(entryPointType, type, _compilation, getMarshallingInfoForElement, out CustomTypeMarshallers? marshallers))
+ {
+ maxIndirectionDepthUsed = maxIndirectionDepthUsedLocal;
+ return new NativeLinearCollectionMarshallingInfo(
+ entryPointTypeInfo,
+ marshallers.Value,
+ isPinnableManagedType,
+ parsedCountInfo,
+ ManagedTypeInfo.CreateTypeInfoForTypeSymbol(entryPointType.TypeParameters.Last()),
+ UseDefaultMarshalling: !isMarshalUsingAttribute);
+ }
+ }
+ else
+ {
+ if (ManualTypeMarshallingHelper.TryGetValueMarshallersFromEntryType(entryPointType, type, _compilation, out CustomTypeMarshallers? marshallers))
+ {
+ return new NativeMarshallingAttributeInfo(entryPointTypeInfo, marshallers.Value, isPinnableManagedType);
+ }
+ }
}
return CreateNativeMarshallingInfo_V1(type, entryPointType, attrData, isMarshalUsingAttribute, indirectionLevel, parsedCountInfo, useSiteAttributes, inspectedElements, ref maxIndirectionDepthUsed);
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/Strings.resx
index 54047927230..ab2d865565c 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/Strings.resx
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/Strings.resx
@@ -216,4 +216,7 @@
<data name="MarshallerTypeMustBeStatic" xml:space="preserve">
<value>The marshaller type '{0}' for managed type '{1}' must be static.</value>
</data>
+ <data name="MarshallerEntryPointTypeMustMatchArity" xml:space="preserve">
+ <value>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.cs.xlf
index 25c6a406662..29fe10cb569 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.cs.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.cs.xlf
@@ -102,6 +102,11 @@
<target state="translated">Určený parametr musí být zařazený ze spravovaného do nespravovaného, ale zařazovací typ {0} to nepodporuje.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">Typ zařazovače {0} pro spravovaný typ {1} musí být statický.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.de.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.de.xlf
index 45a1edaf2fb..1504b218a5c 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.de.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.de.xlf
@@ -102,6 +102,11 @@
<target state="translated">Der angegebene Parameter muss von verwaltet zu nicht verwaltet gemarshallt werden, aber der Marshaller-Typ ‚{0}‘ unterstützt dies nicht.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">Der Marshaller-Typ ‚{0}‘ für den verwalteten Typ ‚{1}‘ muss statisch sein.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.es.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.es.xlf
index 858d693b5f0..bad966c9b76 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.es.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.es.xlf
@@ -102,6 +102,11 @@
<target state="translated">El parámetro especificado debe serializarse de administrado a no administrado, pero el tipo no administrado “{0}” no lo admite.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">El tipo de serializador "{0}" para el tipo administrado "{1}" debe ser estático.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.fr.xlf
index b0120073f66..09c9eedd0c1 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.fr.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.fr.xlf
@@ -102,6 +102,11 @@
<target state="translated">Le paramètre spécifié doit être marshalé de managé à non managé, mais le type marshaleur « {0} » ne le prend pas en charge.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">Le type marshaleur « {0} » pour le type managé « {1} » doit être statique.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.it.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.it.xlf
index af89ec7231a..ffb03f2cd11 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.it.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.it.xlf
@@ -102,6 +102,11 @@
<target state="translated">È necessario effettuare il marshalling del parametro specificato da gestito a non gestito, ma il tipo di gestore del marshalling '{0}' non lo supporta.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">Il tipo di gestore del marshalling '{0}' per il tipo gestito '{1}' deve essere statico.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ja.xlf
index f869a30bc7c..9f5e5a7fee5 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ja.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ja.xlf
@@ -102,6 +102,11 @@
<target state="translated">指定されたパラメーターはマネージドからアンマネージドにマーシャリングする必要がありますが、マーシャラー型 '{0}' ではそれはサポートされていません。</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">マネージド型 '{1}' のマーシャラー型 '{0}' は静的である必要があります。</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ko.xlf
index 3898fe3a6ff..d3eb44d2b4b 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ko.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ko.xlf
@@ -102,6 +102,11 @@
<target state="translated">지정된 매개 변수를 관리형에서 비관리형으로 마샬링해야 하지만 마샬러 유형 '{0}'은(는) 지원하지 않습니다.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">관리 유형 '{1}'에 대한 마샬러 유형 '{0}'은(는) 정적이어야 합니다.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pl.xlf
index 86f2f1c1ed2..4b1649bde70 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pl.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pl.xlf
@@ -102,6 +102,11 @@
<target state="translated">Określony parametr musi być kierowany z zarządzanego do niezarządzanego, ale typ marszałka „{0}” go nie obsługuje.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">Typ marszałka „{0}” dla typu zarządzanego „{1}” musi być statyczny.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pt-BR.xlf
index 6b3833c214a..1560c58c143 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.pt-BR.xlf
@@ -102,6 +102,11 @@
<target state="translated">O parâmetro especificado precisa ser marshalled de gerenciado para não gerenciado, mas o tipo de marshaller '{0}' não dá suporte a ele.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">O tipo de marshaller '{0}' do tipo gerenciado '{1}' deve ser estático.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ru.xlf
index 6271065d4f4..d586077f4d7 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ru.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.ru.xlf
@@ -102,6 +102,11 @@
<target state="translated">Указанный параметр необходимо маршалировать из управляемого в неуправляемый, но тип маршаллера "{0}" не поддерживает это.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">Тип маршаллера "{0}" для управляемого типа "{1}" должен быть статическим.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.tr.xlf
index e647fe89cde..3d0d853df03 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.tr.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.tr.xlf
@@ -102,6 +102,11 @@
<target state="translated">Belirtilen parametrenin yönetilenden yönetilmeyene doğru hazırlanması gerekiyor, ancak '{0}' hazırlayıcı türü bunu desteklemiyor.</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">'{1}' yönetilen türü için '{0}' hazırlayıcı türünün statik olması gerekir.</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hans.xlf
index 12d07bfb18e..06ee106c044 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hans.xlf
@@ -102,6 +102,11 @@
<target state="translated">需要将指定的参数从托管封送到非托管,但封送程序类型“{0}”不支持它。</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">托管类型“{1}”的封送程序类型“{0}”必须是静态的。</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hant.xlf
index 7b21148b921..38167063527 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources/xlf/Strings.zh-Hant.xlf
@@ -102,6 +102,11 @@
<target state="translated">指定的參數必須從受控封送處理到非受控,但封送處理程式類型 '{0}' 不支援。</target>
<note />
</trans-unit>
+ <trans-unit id="MarshallerEntryPointTypeMustMatchArity">
+ <source>The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</source>
+ <target state="new">The marshaller entry point type '{0}' for managed type '{1}' must have an arity of one greater than the managed type.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MarshallerTypeMustBeStatic">
<source>The marshaller type '{0}' for managed type '{1}' must be static.</source>
<target state="translated">受控類型 '{1}' 的封送處理程式類型 '{0}' 必須是靜態。</target>
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 f9c3128f772..5428b971559 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
@@ -17,6 +17,8 @@ namespace Microsoft.Interop
public const string CustomTypeMarshallerAttributeGenericPlaceholder = "System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute.GenericPlaceholder";
public const string CustomMarshallerAttribute = "System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute";
+ public const string CustomMarshallerAttributeGenericPlaceholder = CustomMarshallerAttribute + ".GenericPlaceholder";
+ public const string ElementUnmanagedTypeAttribute = "System.Runtime.InteropServices.Marshalling.ElementUnmanagedTypeAttribute";
public const string AnsiStringMarshaller = "System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller";
public const string BStrStringMarshaller = "System.Runtime.InteropServices.Marshalling.BStrStringMarshaller";
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 513b961c590..2ff90bf93b9 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
@@ -176,5 +176,14 @@ namespace Microsoft.Interop
or SpecialType.System_Single
or SpecialType.System_Double;
}
+
+ public static bool IsConstructedFromEqualTypes(this ITypeSymbol type, ITypeSymbol other)
+ {
+ return (type, other) switch
+ {
+ (INamedTypeSymbol namedType, INamedTypeSymbol namedOther) => SymbolEqualityComparer.Default.Equals(namedType.ConstructedFrom, namedOther.ConstructedFrom),
+ _ => SymbolEqualityComparer.Default.Equals(type, other)
+ };
+ }
}
}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ElementUnmanagedTypeAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ElementUnmanagedTypeAttribute.cs
new file mode 100644
index 00000000000..d0f508904a1
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ElementUnmanagedTypeAttribute.cs
@@ -0,0 +1,17 @@
+// 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.Marshalling
+{
+ /// <summary>
+ /// Specifies that a particular generic parameter is the collection element's unmanaged type.
+ /// </summary>
+ /// <remarks>
+ /// If this attribute is provided on a generic parameter of a marshaller, then the generator will assume
+ /// that it is a linear collection marshaller.
+ /// </remarks>
+ [AttributeUsage(AttributeTargets.GenericParameter)]
+ public sealed class ElementUnmanagedTypeAttribute : Attribute
+ {
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.Custom.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.Custom.cs
new file mode 100644
index 00000000000..a1b8789fa16
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/ArrayTests.Custom.cs
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using SharedTypes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+using System.Text;
+
+using Xunit;
+
+namespace LibraryImportGenerator.IntegrationTests
+{
+ partial class NativeExportsNE
+ {
+ public partial class Arrays
+ {
+ // TODO: All these tests can be removed once we switch the array marshaller in runtime libraries
+ // to V2 of custom type marshalling shapes
+ public partial class Custom
+ {
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
+ public static partial int Sum([MarshalUsing(typeof(CustomArrayMarshaller<,>))] int[] values, int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
+ public static partial int SumInArray([MarshalUsing(typeof(CustomArrayMarshaller<,>))] in int[] values, int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "duplicate_int_array")]
+ public static partial void Duplicate([MarshalUsing(typeof(CustomArrayMarshaller<,>), CountElementName = "numValues")] ref int[] values, int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array")]
+ [return: MarshalUsing(typeof(CustomArrayMarshaller<,>), CountElementName = "numValues")]
+ public static partial int[] CreateRange(int start, int end, out int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array_out")]
+ public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(CustomArrayMarshaller<,>), CountElementName = "numValues")] out int[] res);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_char_array", StringMarshalling = StringMarshalling.Utf16)]
+ public static partial int SumChars([MarshalUsing(typeof(CustomArrayMarshaller<,>))] char[] chars, int numElements);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_char_array", StringMarshalling = StringMarshalling.Utf16)]
+ public static partial void ReverseChars([MarshalUsing(typeof(CustomArrayMarshaller<,>), CountElementName = "numElements")] ref char[] chars, int numElements);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
+ [return: MarshalUsing(typeof(CustomArrayMarshaller<,>), ConstantElementCount = sizeof(long))]
+ public static partial byte[] GetLongBytes(long l);
+ }
+ }
+ }
+
+ public class ArrayTests_Custom
+ {
+ private int[] GetIntArray() => new[] { 1, 5, 79, 165, 32, 3 };
+
+ [Fact]
+ public void IntArray_ByValue()
+ {
+ int[] array = GetIntArray();
+ Assert.Equal(array.Sum(), NativeExportsNE.Arrays.Custom.Sum(array, array.Length));
+ }
+
+ [Fact]
+ public void NullIntArray_ByValue()
+ {
+ int[] array = null;
+ Assert.Equal(-1, NativeExportsNE.Arrays.Custom.Sum(array, 0));
+ }
+
+ [Fact]
+ public void ZeroLengthArray_MarshalledAsNonNull()
+ {
+ var array = new int[0];
+ Assert.Equal(0, NativeExportsNE.Arrays.Custom.Sum(array, array.Length));
+ }
+
+ [Fact]
+ public void IntArray_In()
+ {
+ int[] array = GetIntArray();
+ Assert.Equal(array.Sum(), NativeExportsNE.Arrays.Custom.SumInArray(array, array.Length));
+ }
+
+ [Fact]
+ public void IntArray_Ref()
+ {
+ int[] array = GetIntArray();
+ var newArray = array;
+ NativeExportsNE.Arrays.Custom.Duplicate(ref newArray, array.Length);
+ Assert.Equal((IEnumerable<int>)array, newArray);
+ }
+
+ [Fact]
+ public void CharArray_ByValue()
+ {
+ char[] array = CharacterTests.CharacterMappings().Select(o => (char)o[0]).ToArray();
+ Assert.Equal(array.Sum(c => c), NativeExportsNE.Arrays.Custom.SumChars(array, array.Length));
+ }
+
+ [Fact]
+ public void CharArray_Ref()
+ {
+ char[] array = CharacterTests.CharacterMappings().Select(o => (char)o[0]).ToArray();
+ var newArray = array;
+ NativeExportsNE.Arrays.Custom.ReverseChars(ref newArray, array.Length);
+ Assert.Equal(array.Reverse(), newArray);
+ }
+
+ [Fact]
+ public void IntArray_Return()
+ {
+ int start = 5;
+ int end = 20;
+
+ IEnumerable<int> expected = Enumerable.Range(start, end - start);
+ Assert.Equal(expected, NativeExportsNE.Arrays.Custom.CreateRange(start, end, out _));
+
+ int[] res;
+ NativeExportsNE.Arrays.Custom.CreateRange_Out(start, end, out _, out res);
+ Assert.Equal(expected, res);
+ }
+
+ [Fact]
+ public void NullArray_Return()
+ {
+ Assert.Null(NativeExportsNE.Arrays.Custom.CreateRange(1, 0, out _));
+
+ int[] res;
+ NativeExportsNE.Arrays.Custom.CreateRange_Out(1, 0, out _, out res);
+ Assert.Null(res);
+ }
+
+ [Fact]
+ public void ConstantSizeArray()
+ {
+ var longVal = 0x12345678ABCDEF10L;
+
+ Assert.Equal(longVal, MemoryMarshal.Read<long>(NativeExportsNE.Arrays.Custom.GetLongBytes(longVal)));
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.V1.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.V1.cs
new file mode 100644
index 00000000000..ea039de946e
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.V1.cs
@@ -0,0 +1,246 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using SharedTypes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+using System.Text;
+
+using Xunit;
+
+namespace LibraryImportGenerator.IntegrationTests
+{
+ partial class NativeExportsNE
+ {
+ public partial class Collections
+ {
+ public partial class V1
+ {
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
+ public static partial int Sum([MarshalUsing(typeof(ListMarshaller_V1<int>))] List<int> values, int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
+ public static partial int SumInArray([MarshalUsing(typeof(ListMarshaller_V1<int>))] in List<int> values, int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "duplicate_int_array")]
+ public static partial void Duplicate([MarshalUsing(typeof(ListMarshaller_V1<int>), CountElementName = "numValues")] ref List<int> values, int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array")]
+ [return: MarshalUsing(typeof(ListMarshaller_V1<int>), CountElementName = "numValues")]
+ public static partial List<int> CreateRange(int start, int end, out int numValues);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array_out")]
+ public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(ListMarshaller_V1<int>), CountElementName = "numValues")] out List<int> res);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")]
+ public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller_V1<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")]
+ public static partial int SumStringLengths([MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] WrappedList_V1<string> strArray);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_replace")]
+ public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller_V1<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_V1<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)]
+ public static partial List<string> ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller_V1<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_V1<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray,
+ out int numElements,
+ [MarshalUsing(typeof(ListMarshaller_V1<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] out List<string> res);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
+ [return: MarshalUsing(typeof(ListMarshaller_V1<byte>), ConstantElementCount = sizeof(long))]
+ public static partial List<byte> GetLongBytes(long l);
+
+ [LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool AndAllMembers([MarshalUsing(typeof(ListMarshaller_V1<BoolStruct_V1>))] List<BoolStruct_V1> pArray, int length);
+ }
+ }
+ }
+
+ public class CollectionTests_V1
+ {
+ [Fact]
+ public void BlittableElementColllectionMarshalledToNativeAsExpected()
+ {
+ var list = new List<int> { 1, 5, 79, 165, 32, 3 };
+ Assert.Equal(list.Sum(), NativeExportsNE.Collections.V1.Sum(list, list.Count));
+ }
+
+ [Fact]
+ public void NullBlittableElementColllectionMarshalledToNativeAsExpected()
+ {
+ Assert.Equal(-1, NativeExportsNE.Collections.V1.Sum(null, 0));
+ }
+
+ [Fact]
+ public void BlittableElementColllectionInParameter()
+ {
+ var list = new List<int> { 1, 5, 79, 165, 32, 3 };
+ Assert.Equal(list.Sum(), NativeExportsNE.Collections.V1.SumInArray(list, list.Count));
+ }
+
+ [Fact]
+ public void BlittableElementCollectionRefParameter()
+ {
+ var list = new List<int> { 1, 5, 79, 165, 32, 3 };
+ var newList = list;
+ NativeExportsNE.Collections.V1.Duplicate(ref newList, list.Count);
+ Assert.Equal((IEnumerable<int>)list, newList);
+ }
+
+ [Fact]
+ public void BlittableElementCollectionReturnedFromNative()
+ {
+ int start = 5;
+ int end = 20;
+
+ IEnumerable<int> expected = Enumerable.Range(start, end - start);
+ Assert.Equal(expected, NativeExportsNE.Collections.V1.CreateRange(start, end, out _));
+
+ List<int> res;
+ NativeExportsNE.Collections.V1.CreateRange_Out(start, end, out _, out res);
+ Assert.Equal(expected, res);
+ }
+
+ [Fact]
+ public void NullBlittableElementCollectionReturnedFromNative()
+ {
+ Assert.Null(NativeExportsNE.Collections.V1.CreateRange(1, 0, out _));
+
+ List<int> res;
+ NativeExportsNE.Collections.V1.CreateRange_Out(1, 0, out _, out res);
+ Assert.Null(res);
+ }
+
+ private static List<string> GetStringList()
+ {
+ return new()
+ {
+ "ABCdef 123$%^",
+ "🍜 !! 🍜 !!",
+ "🌲 木 🔥 火 🌾 土 🛡 金 🌊 水" ,
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae posuere mauris, sed ultrices leo. Suspendisse potenti. Mauris enim enim, blandit tincidunt consequat in, varius sit amet neque. Morbi eget porttitor ex. Duis mattis aliquet ante quis imperdiet. Duis sit.",
+ string.Empty,
+ null
+ };
+ }
+
+ [Fact]
+ public void ByValueCollectionWithNonBlittableElements()
+ {
+ var strings = GetStringList();
+ Assert.Equal(strings.Sum(str => str?.Length ?? 0), NativeExportsNE.Collections.V1.SumStringLengths(strings));
+ }
+
+ [Fact]
+ public void ByValueNullCollectionWithNonBlittableElements()
+ {
+ Assert.Equal(0, NativeExportsNE.Collections.V1.SumStringLengths(null));
+ }
+
+ [Fact]
+ public void ByValueCollectionWithNonBlittableElements_WithDefaultMarshalling()
+ {
+ var strings = new WrappedList_V1<string>(GetStringList());
+ Assert.Equal(strings.Wrapped.Sum(str => str?.Length ?? 0), NativeExportsNE.Collections.V1.SumStringLengths(strings));
+ }
+
+ [Fact]
+ public void ByRefCollectionWithNonBlittableElements()
+ {
+ var strings = GetStringList();
+ var expectedStrings = strings.Select(s => ReverseChars(s)).ToList();
+ NativeExportsNE.Collections.V1.ReverseStrings_Ref(ref strings, out _);
+
+ Assert.Equal((IEnumerable<string>)expectedStrings, strings);
+ }
+
+ [Fact]
+ public void ReturnCollectionWithNonBlittableElements()
+ {
+ var strings = GetStringList();
+ var expectedStrings = strings.Select(s => ReverseChars(s)).ToList();
+ Assert.Equal(expectedStrings, NativeExportsNE.Collections.V1.ReverseStrings_Return(strings, out _));
+
+ List<string> res;
+ NativeExportsNE.Collections.V1.ReverseStrings_Out(strings, out _, out res);
+ Assert.Equal(expectedStrings, res);
+ }
+
+ [Fact]
+ public void ByRefNullCollectionWithNonBlittableElements()
+ {
+ List<string> strings = null;
+ NativeExportsNE.Collections.V1.ReverseStrings_Ref(ref strings, out _);
+
+ Assert.Null(strings);
+ }
+
+ [Fact]
+ public void ReturnNullCollectionWithNonBlittableElements()
+ {
+ List<string> strings = null;
+ Assert.Null(NativeExportsNE.Collections.V1.ReverseStrings_Return(strings, out _));
+
+ List<string> res;
+ NativeExportsNE.Collections.V1.ReverseStrings_Out(strings, out _, out res);
+ Assert.Null(res);
+ }
+
+ [Fact]
+ public void ConstantSizeCollection()
+ {
+ var longVal = 0x12345678ABCDEF10L;
+
+ Assert.Equal(longVal, MemoryMarshal.Read<long>(CollectionsMarshal.AsSpan(NativeExportsNE.Collections.V1.GetLongBytes(longVal))));
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void CollectionWithSimpleNonBlittableTypeMarshalling(bool result)
+ {
+ var boolValues = new List<BoolStruct_V1>
+ {
+ new BoolStruct_V1
+ {
+ b1 = true,
+ b2 = true,
+ b3 = true,
+ },
+ new BoolStruct_V1
+ {
+ b1 = true,
+ b2 = true,
+ b3 = true,
+ },
+ new BoolStruct_V1
+ {
+ b1 = true,
+ b2 = true,
+ b3 = result,
+ },
+ };
+
+ Assert.Equal(result, NativeExportsNE.Collections.V1.AndAllMembers(boolValues, boolValues.Count));
+ }
+
+ private static string ReverseChars(string value)
+ {
+ if (value == null)
+ return null;
+
+ var chars = value.ToCharArray();
+ Array.Reverse(chars);
+ return new string(chars);
+ }
+ }
+}
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 cf058810d43..b4851346917 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionTests.cs
@@ -18,50 +18,24 @@ namespace LibraryImportGenerator.IntegrationTests
public partial class Collections
{
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
- public static partial int Sum([MarshalUsing(typeof(ListMarshaller<int>))] List<int> values, int numValues);
-
- [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
- public static partial int Sum(ref int values, int numValues);
+ public static partial int Sum([MarshalUsing(typeof(ListMarshaller<,>))] List<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
- public static partial int SumInArray([MarshalUsing(typeof(ListMarshaller<int>))] in List<int> values, int numValues);
+ public static partial int SumInArray([MarshalUsing(typeof(ListMarshaller<,>))] in List<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "duplicate_int_array")]
- public static partial void Duplicate([MarshalUsing(typeof(ListMarshaller<int>), CountElementName = "numValues")] ref List<int> values, int numValues);
+ public static partial void Duplicate([MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")] ref List<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array")]
- [return:MarshalUsing(typeof(ListMarshaller<int>), CountElementName = "numValues")]
+ [return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
public static partial List<int> CreateRange(int start, int end, out int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array_out")]
- 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), ElementIndirectionDepth = 1)] List<string> strArray);
-
- [LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")]
- public static partial int SumStringLengths([MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] WrappedList<string> strArray);
-
- [LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_replace")]
- 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), 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), ElementIndirectionDepth = 1)] List<string> strArray,
- out int numElements,
- [MarshalUsing(typeof(ListMarshaller<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] out List<string> res);
+ public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")] out List<int> res);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
- [return:MarshalUsing(typeof(ListMarshaller<byte>), ConstantElementCount = sizeof(long))]
+ [return: MarshalUsing(typeof(ListMarshaller<,>), ConstantElementCount = sizeof(long))]
public static partial List<byte> GetLongBytes(long l);
-
- [LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")]
- [return: MarshalAs(UnmanagedType.U1)]
- public static partial bool AndAllMembers([MarshalUsing(typeof(ListMarshaller<BoolStruct_V1>))] List<BoolStruct_V1> pArray, int length);
}
}
@@ -134,113 +108,11 @@ namespace LibraryImportGenerator.IntegrationTests
}
[Fact]
- public void ByValueCollectionWithNonBlittableElements()
- {
- var strings = GetStringList();
- Assert.Equal(strings.Sum(str => str?.Length ?? 0), NativeExportsNE.Collections.SumStringLengths(strings));
- }
-
- [Fact]
- public void ByValueNullCollectionWithNonBlittableElements()
- {
- Assert.Equal(0, NativeExportsNE.Collections.SumStringLengths(null));
- }
-
- [Fact]
- public void ByValueCollectionWithNonBlittableElements_WithDefaultMarshalling()
- {
- var strings = new WrappedList<string>(GetStringList());
- Assert.Equal(strings.Wrapped.Sum(str => str?.Length ?? 0), NativeExportsNE.Collections.SumStringLengths(strings));
- }
-
- [Fact]
- public void ByRefCollectionWithNonBlittableElements()
- {
- var strings = GetStringList();
- var expectedStrings = strings.Select(s => ReverseChars(s)).ToList();
- NativeExportsNE.Collections.ReverseStrings_Ref(ref strings, out _);
-
- Assert.Equal((IEnumerable<string>)expectedStrings, strings);
- }
-
- [Fact]
- public void ReturnCollectionWithNonBlittableElements()
- {
- var strings = GetStringList();
- var expectedStrings = strings.Select(s => ReverseChars(s)).ToList();
- Assert.Equal(expectedStrings, NativeExportsNE.Collections.ReverseStrings_Return(strings, out _));
-
- List<string> res;
- NativeExportsNE.Collections.ReverseStrings_Out(strings, out _, out res);
- Assert.Equal(expectedStrings, res);
- }
-
- [Fact]
- public void ByRefNullCollectionWithNonBlittableElements()
- {
- List<string> strings = null;
- NativeExportsNE.Collections.ReverseStrings_Ref(ref strings, out _);
-
- Assert.Null(strings);
- }
-
- [Fact]
- public void ReturnNullCollectionWithNonBlittableElements()
- {
- List<string> strings = null;
- Assert.Null(NativeExportsNE.Collections.ReverseStrings_Return(strings, out _));
-
- List<string> res;
- NativeExportsNE.Collections.ReverseStrings_Out(strings, out _, out res);
- Assert.Null(res);
- }
-
- [Fact]
public void ConstantSizeCollection()
{
var longVal = 0x12345678ABCDEF10L;
Assert.Equal(longVal, MemoryMarshal.Read<long>(CollectionsMarshal.AsSpan(NativeExportsNE.Collections.GetLongBytes(longVal))));
}
-
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void CollectionWithSimpleNonBlittableTypeMarshalling(bool result)
- {
- var boolValues = new List<BoolStruct_V1>
- {
- new BoolStruct_V1
- {
- b1 = true,
- b2 = true,
- b3 = true,
- },
- new BoolStruct_V1
- {
- b1 = true,
- b2 = true,
- b3 = true,
- },
- new BoolStruct_V1
- {
- b1 = true,
- b2 = true,
- b3 = result,
- },
- };
-
- Assert.Equal(result, NativeExportsNE.Collections.AndAllMembers(boolValues, boolValues.Count));
- }
-
- private static string ReverseChars(string value)
- {
- if (value == null)
- return null;
-
- var chars = value.ToCharArray();
- Array.Reverse(chars);
- return new string(chars);
- }
}
}
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 d3e5b0c2e1f..eb3c4a66c00 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs
@@ -1379,49 +1379,162 @@ struct RecursiveStruct2
int i;
}";
- public static string CollectionByValue(string elementType) => BasicParameterByValue($"TestCollection<{elementType}>", DisableRuntimeMarshalling) + @"
-[NativeMarshalling(typeof(Marshaller<>))]
-class TestCollection<T> {}
-
-[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.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;
-}
+ public static class CustomCollectionMarshalling
+ {
+ public static string TestCollection(bool defineNativeMarshalling = true) => $@"
+{(defineNativeMarshalling ? "[NativeMarshalling(typeof(Marshaller<,>))]" : string.Empty)}
+class TestCollection<T> {{}}
";
- public static string CollectionByValue<T>() => CollectionByValue(typeof(T).ToString());
-
- public static string MarshalUsingCollectionCountInfoParametersAndModifiers(string collectionType) => $@"
+ public static string CollectionOutParameter(string collectionType, string predeclaration = "") => $@"
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
-{DisableRuntimeMarshalling}
+{predeclaration}
partial class Test
{{
[LibraryImport(""DoesNotExist"")]
- [return:MarshalUsing(ConstantElementCount=10)]
- public static partial {collectionType} Method(
- {collectionType} p,
- in {collectionType} pIn,
- int pRefSize,
- [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
- [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
- out int pOutSize
- );
-}}";
+ public static partial int Method(
+ [MarshalUsing(ConstantElementCount = 10)] out {collectionType} pOut);
+}}
+";
+ public static string CollectionReturnType(string collectionType, string predeclaration = "") => $@"
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+{predeclaration}
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ [return: MarshalUsing(ConstantElementCount = 10)]
+ public static partial {collectionType} Method();
+}}
+";
+ public static class Stateless
+ {
+ public const string In = @"
+[CustomMarshaller(typeof(TestCollection<>), Scenario.ManagedToUnmanagedIn, typeof(Marshaller<,>))]
+static unsafe class Marshaller<T, [ElementUnmanagedType] TUnmanagedElement>
+{
+ public static byte* AllocateContainerForUnmanagedElements(TestCollection<T> managed, out int numElements) => throw null;
+ public static System.ReadOnlySpan<T> GetManagedValuesSource(TestCollection<T> managed) => throw null;
+ public static System.Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements) => throw null;
+}
+";
+ public const string InBuffer = @"
+[CustomMarshaller(typeof(TestCollection<>), Scenario.ManagedToUnmanagedIn, typeof(Marshaller<,>))]
+static unsafe class Marshaller<T, [ElementUnmanagedType] TUnmanagedElement>
+{
+ public const int BufferSize = 0x100;
+ public static byte* AllocateContainerForUnmanagedElements(TestCollection<T> managed, System.Span<byte> buffer, out int numElements) => throw null;
+ public static System.ReadOnlySpan<T> GetManagedValuesSource(TestCollection<T> managed) => throw null;
+ public static System.Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements) => throw null;
+}
+";
+ public const string Ref = @"
+[CustomMarshaller(typeof(TestCollection<>), Scenario.Default, typeof(Marshaller<,>))]
+static unsafe class Marshaller<T, [ElementUnmanagedType] TUnmanagedElement>
+{
+ public static byte* AllocateContainerForUnmanagedElements(TestCollection<T> managed, out int numElements) => throw null;
+ public static System.ReadOnlySpan<T> GetManagedValuesSource(TestCollection<T> managed) => throw null;
+ public static System.Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements) => throw null;
- public static string CustomCollectionWithMarshaller(bool enableDefaultMarshalling)
+ public static TestCollection<T> AllocateContainerForManagedElements(byte* unmanaged, int length) => throw null;
+ public static System.Span<T> GetManagedValuesDestination(TestCollection<T> managed) => throw null;
+ public static System.ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* unmanaged, int numElements) => throw null;
+}
+";
+ public const string RefNested = @"
+[CustomMarshaller(typeof(TestCollection<>), Scenario.Default, typeof(Marshaller<,>.Ref.Nested))]
+static unsafe class Marshaller<T, [ElementUnmanagedType] TUnmanagedElement>
+{
+ static class Nested
+ {
+ static class Ref
{
- string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty;
- return nativeMarshallingAttribute + @"class TestCollection<T> {}
+ public static byte* AllocateContainerForUnmanagedElements(TestCollection<T> managed, out int numElements) => throw null;
+ public static System.ReadOnlySpan<T> GetManagedValuesSource(TestCollection<T> managed) => throw null;
+ public static System.Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements) => throw null;
+
+ public static TestCollection<T> AllocateContainerForManagedElements(byte* unmanaged, int length) => throw null;
+ public static System.Span<T> GetManagedValuesDestination(TestCollection<T> managed) => throw null;
+ public static System.ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* unmanaged, int numElements) => throw null;
+ }
+ }
+}
+";
+ public const string Out = @"
+[CustomMarshaller(typeof(TestCollection<>), Scenario.ManagedToUnmanagedOut, typeof(Marshaller<,>))]
+static unsafe class Marshaller<T, [ElementUnmanagedType] TUnmanagedElement>
+{
+ public static TestCollection<T> AllocateContainerForManagedElements(byte* unmanaged, int length) => throw null;
+ public static System.Span<T> GetManagedValuesDestination(TestCollection<T> managed) => throw null;
+ public static System.ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* unmanaged, int numElements) => throw null;
+}
+";
+ public static string ByValue<T>() => ByValue(typeof(T).ToString());
+ public static string ByValue(string elementType) => BasicParameterByValue($"TestCollection<{elementType}>", DisableRuntimeMarshalling)
+ + TestCollection()
+ + In;
+
+ public static string ByValueCallerAllocatedBuffer<T>() => ByValueCallerAllocatedBuffer(typeof(T).ToString());
+ public static string ByValueCallerAllocatedBuffer(string elementType) => BasicParameterByValue($"TestCollection<{elementType}>", DisableRuntimeMarshalling)
+ + TestCollection()
+ + In;
+
+ public static string DefaultMarshallerParametersAndModifiers<T>() => DefaultMarshallerParametersAndModifiers(typeof(T).ToString());
+ public static string DefaultMarshallerParametersAndModifiers(string elementType) => MarshalUsingCollectionCountInfoParametersAndModifiers($"TestCollection<{elementType}>")
+ + TestCollection()
+ + Ref;
+
+ public static string CustomMarshallerParametersAndModifiers<T>() => CustomMarshallerParametersAndModifiers(typeof(T).ToString());
+ public static string CustomMarshallerParametersAndModifiers(string elementType) => MarshalUsingCollectionParametersAndModifiers($"TestCollection<{elementType}>", $"Marshaller<,>")
+ + TestCollection(defineNativeMarshalling: false)
+ + Ref;
+
+ public static string CustomMarshallerReturnValueLength<T>() => CustomMarshallerReturnValueLength(typeof(T).ToString());
+ public static string CustomMarshallerReturnValueLength(string elementType) => MarshalUsingCollectionReturnValueLength($"TestCollection<{elementType}>", $"Marshaller<,>")
+ + TestCollection(defineNativeMarshalling: false)
+ + Ref;
+
+ public static string NativeToManagedOnlyOutParameter<T>() => NativeToManagedOnlyOutParameter(typeof(T).ToString());
+ public static string NativeToManagedOnlyOutParameter(string elementType) => CollectionOutParameter($"TestCollection<{elementType}>")
+ + TestCollection()
+ + Out;
+
+ public static string NativeToManagedOnlyReturnValue<T>() => NativeToManagedOnlyReturnValue(typeof(T).ToString());
+ public static string NativeToManagedOnlyReturnValue(string elementType) => CollectionReturnType($"TestCollection<{elementType}>")
+ + TestCollection()
+ + Out;
+
+ public static string NestedMarshallerParametersAndModifiers<T>() => DefaultMarshallerParametersAndModifiers(typeof(T).ToString());
+ public static string NestedMarshallerParametersAndModifiers(string elementType) => MarshalUsingCollectionCountInfoParametersAndModifiers($"TestCollection<{elementType}>")
+ + TestCollection()
+ + RefNested;
+
+ public static string GenericCollectionMarshallingArityMismatch => BasicParameterByValue("TestCollection<int>", DisableRuntimeMarshalling)
+ + @"
+[NativeMarshalling(typeof(Marshaller<,,>))]
+class TestCollection<T> {}
+
+[CustomMarshaller(typeof(TestCollection<>), Scenario.Default, typeof(Marshaller<,,>))]
+static unsafe class Marshaller<T, U, [ElementUnmanagedType] TUnmanagedElement>
+{
+ public static byte* AllocateContainerForUnmanagedElements(TestCollection<T> managed, out int numElements) => throw null;
+ public static System.ReadOnlySpan<T> GetManagedValuesSource(TestCollection<T> managed) => throw null;
+ public static System.Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements) => throw null;
+
+ public static TestCollection<T> AllocateContainerForManagedElements(byte* unmanaged, int length) => throw null;
+ public static System.Span<T> GetManagedValuesDestination(TestCollection<T> managed) => throw null;
+ public static System.ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* unmanaged, int numElements) => throw null;
+}
+";
+ }
+ }
+
+ public static class CustomCollectionMarshalling_V1
+ {
+ public static string ByValue(string elementType) => BasicParameterByValue($"TestCollection<{elementType}>", DisableRuntimeMarshalling) + @"
+[NativeMarshalling(typeof(Marshaller<>))]
+class TestCollection<T> {}
[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
ref struct Marshaller<T>
@@ -1435,95 +1548,44 @@ ref struct Marshaller<T>
public System.IntPtr ToNativeValue() => throw null;
public void FromNativeValue(System.IntPtr value) => throw null;
public TestCollection<T> ToManaged() => throw null;
-}";
- }
-
- public static string MarshalUsingCollectionCountInfoParametersAndModifiers<T>() => MarshalUsingCollectionCountInfoParametersAndModifiers(typeof(T).ToString());
-
- public static string CustomCollectionDefaultMarshallerParametersAndModifiers(string elementType) => MarshalUsingCollectionCountInfoParametersAndModifiers($"TestCollection<{elementType}>") + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
-
- public static string CustomCollectionDefaultMarshallerParametersAndModifiers<T>() => CustomCollectionDefaultMarshallerParametersAndModifiers(typeof(T).ToString());
-
- public static string MarshalUsingCollectionParametersAndModifiers(string collectionType, string marshallerType) => $@"
-using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.Marshalling;
-{DisableRuntimeMarshalling}
-partial class Test
-{{
- [LibraryImport(""DoesNotExist"")]
- [return:MarshalUsing(typeof({marshallerType}), ConstantElementCount=10)]
- public static partial {collectionType} Method(
- [MarshalUsing(typeof({marshallerType}))] {collectionType} p,
- [MarshalUsing(typeof({marshallerType}))] in {collectionType} pIn,
- int pRefSize,
- [MarshalUsing(typeof({marshallerType}), CountElementName = ""pRefSize"")] ref {collectionType} pRef,
- [MarshalUsing(typeof({marshallerType}), CountElementName = ""pOutSize"")] out {collectionType} pOut,
- out int pOutSize
- );
-}}";
+}
+";
- public static string CustomCollectionCustomMarshallerParametersAndModifiers(string elementType) => MarshalUsingCollectionParametersAndModifiers($"TestCollection<{elementType}>", $"Marshaller<{elementType}>") + CustomCollectionWithMarshaller(enableDefaultMarshalling: false);
+ public static string ByValue<T>() => ByValue(typeof(T).ToString());
- public static string CustomCollectionCustomMarshallerParametersAndModifiers<T>() => CustomCollectionCustomMarshallerParametersAndModifiers(typeof(T).ToString());
+ public static string CustomCollectionWithMarshaller(bool enableDefaultMarshalling)
+ {
+ string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty;
+ return nativeMarshallingAttribute + @"class TestCollection<T> {}
- public static string MarshalUsingCollectionReturnValueLength(string collectionType, string marshallerType) => $@"
-using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.Marshalling;
-{DisableRuntimeMarshalling}
-partial class Test
-{{
- [LibraryImport(""DoesNotExist"")]
- public static partial int Method(
- [MarshalUsing(typeof({marshallerType}), CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out {collectionType} pOut
- );
-}}";
+ [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.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;
+ }";
+ }
- public static string CustomCollectionCustomMarshallerReturnValueLength(string elementType) => MarshalUsingCollectionReturnValueLength($"TestCollection<{elementType}>", $"Marshaller<{elementType}>") + CustomCollectionWithMarshaller(enableDefaultMarshalling: false);
+ public static string DefaultMarshallerParametersAndModifiers(string elementType) => MarshalUsingCollectionCountInfoParametersAndModifiers($"TestCollection<{elementType}>") + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
- public static string CustomCollectionCustomMarshallerReturnValueLength<T>() => CustomCollectionCustomMarshallerReturnValueLength(typeof(T).ToString());
+ public static string DefaultMarshallerParametersAndModifiers<T>() => DefaultMarshallerParametersAndModifiers(typeof(T).ToString());
- public static string MarshalUsingArrayParameterWithSizeParam(string sizeParamType, bool isByRef) => $@"
-using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.Marshalling;
-{DisableRuntimeMarshalling}
-partial class Test
-{{
- [LibraryImport(""DoesNotExist"")]
- public static partial void Method(
- {(isByRef ? "ref" : "")} {sizeParamType} pRefSize,
- [MarshalUsing(CountElementName = ""pRefSize"")] ref int[] pRef
- );
-}}";
+ public static string CustomMarshallerParametersAndModifiers(string elementType) => MarshalUsingCollectionParametersAndModifiers($"TestCollection<{elementType}>", $"Marshaller<{elementType}>") + CustomCollectionWithMarshaller(enableDefaultMarshalling: false);
- public static string MarshalUsingArrayParameterWithSizeParam<T>(bool isByRef) => MarshalUsingArrayParameterWithSizeParam(typeof(T).ToString(), isByRef);
+ public static string CustomMarshallerParametersAndModifiers<T>() => CustomMarshallerParametersAndModifiers(typeof(T).ToString());
- public static string MarshalUsingCollectionWithConstantAndElementCount => $@"
-using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.Marshalling;
-{DisableRuntimeMarshalling}
-partial class Test
-{{
- [LibraryImport(""DoesNotExist"")]
- public static partial void Method(
- int pRefSize,
- [MarshalUsing(ConstantElementCount = 10, CountElementName = ""pRefSize"")] ref int[] pRef
- );
-}}";
+ public static string CustomMarshallerReturnValueLength(string elementType) => MarshalUsingCollectionReturnValueLength($"TestCollection<{elementType}>", $"Marshaller<{elementType}>") + CustomCollectionWithMarshaller(enableDefaultMarshalling: false);
- public static string MarshalUsingCollectionWithNullElementName => $@"
-using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.Marshalling;
-{DisableRuntimeMarshalling}
-partial class Test
-{{
- [LibraryImport(""DoesNotExist"")]
- public static partial void Method(
- int pRefSize,
- [MarshalUsing(CountElementName = null)] ref int[] pRef
- );
-}}";
+ public static string CustomMarshallerReturnValueLength<T>() => CustomMarshallerReturnValueLength(typeof(T).ToString());
- public static string GenericCollectionMarshallingArityMismatch => BasicParameterByValue("TestCollection<int>", DisableRuntimeMarshalling) + @"
+ public static string GenericCollectionMarshallingArityMismatch => BasicParameterByValue("TestCollection<int>", DisableRuntimeMarshalling) + @"
[NativeMarshalling(typeof(Marshaller<,>))]
class TestCollection<T> {}
@@ -1542,7 +1604,7 @@ ref struct Marshaller<T, U>
public TestCollection<T> ToManaged() => throw null;
}";
- public static string GenericCollectionWithCustomElementMarshalling => $@"
+ public static string GenericCollectionWithCustomElementMarshalling => $@"
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
{DisableRuntimeMarshalling}
@@ -1569,7 +1631,7 @@ struct IntWrapper
" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
- public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth => $@"
+ public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth => $@"
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
{DisableRuntimeMarshalling}
@@ -1588,7 +1650,7 @@ struct IntWrapper
" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
- public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth => $@"
+ public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth => $@"
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
{DisableRuntimeMarshalling}
@@ -1606,6 +1668,98 @@ struct IntWrapper
}}
" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
+ }
+
+ public static string MarshalUsingCollectionCountInfoParametersAndModifiers(string collectionType) => $@"
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+{DisableRuntimeMarshalling}
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ [return:MarshalUsing(ConstantElementCount=10)]
+ public static partial {collectionType} Method(
+ {collectionType} p,
+ in {collectionType} pIn,
+ int pRefSize,
+ [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
+ [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
+ out int pOutSize
+ );
+}}";
+
+ public static string MarshalUsingCollectionCountInfoParametersAndModifiers<T>() => MarshalUsingCollectionCountInfoParametersAndModifiers(typeof(T).ToString());
+
+ public static string MarshalUsingCollectionParametersAndModifiers(string collectionType, string marshallerType) => $@"
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+{DisableRuntimeMarshalling}
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ [return:MarshalUsing(typeof({marshallerType}), ConstantElementCount=10)]
+ public static partial {collectionType} Method(
+ [MarshalUsing(typeof({marshallerType}))] {collectionType} p,
+ [MarshalUsing(typeof({marshallerType}))] in {collectionType} pIn,
+ int pRefSize,
+ [MarshalUsing(typeof({marshallerType}), CountElementName = ""pRefSize"")] ref {collectionType} pRef,
+ [MarshalUsing(typeof({marshallerType}), CountElementName = ""pOutSize"")] out {collectionType} pOut,
+ out int pOutSize
+ );
+}}";
+
+ public static string MarshalUsingCollectionReturnValueLength(string collectionType, string marshallerType) => $@"
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+{DisableRuntimeMarshalling}
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial int Method(
+ [MarshalUsing(typeof({marshallerType}), CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out {collectionType} pOut
+ );
+}}";
+
+ public static string MarshalUsingArrayParameterWithSizeParam(string sizeParamType, bool isByRef) => $@"
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+{DisableRuntimeMarshalling}
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void Method(
+ {(isByRef ? "ref" : "")} {sizeParamType} pRefSize,
+ [MarshalUsing(CountElementName = ""pRefSize"")] ref int[] pRef
+ );
+}}";
+
+ public static string MarshalUsingArrayParameterWithSizeParam<T>(bool isByRef) => MarshalUsingArrayParameterWithSizeParam(typeof(T).ToString(), isByRef);
+
+ public static string MarshalUsingCollectionWithConstantAndElementCount => $@"
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+{DisableRuntimeMarshalling}
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void Method(
+ int pRefSize,
+ [MarshalUsing(ConstantElementCount = 10, CountElementName = ""pRefSize"")] ref int[] pRef
+ );
+}}";
+
+ public static string MarshalUsingCollectionWithNullElementName => $@"
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+{DisableRuntimeMarshalling}
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void Method(
+ int pRefSize,
+ [MarshalUsing(CountElementName = null)] ref int[] pRef
+ );
+}}";
public static string MarshalAsAndMarshalUsingOnReturnValue => $@"
using System.Runtime.InteropServices;
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 935980dbc73..539a5116bcf 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs
@@ -117,11 +117,12 @@ namespace LibraryImportGenerator.UnitTests
yield return new object[] { CodeSnippets.MarshalUsingCollectionWithNullElementName, 2, 0 };
// Generic collection marshaller has different arity than collection.
- yield return new object[] { CodeSnippets.GenericCollectionMarshallingArityMismatch, 2, 0 };
+ yield return new object[] { CodeSnippets.CustomCollectionMarshalling.Stateless.GenericCollectionMarshallingArityMismatch, 2, 0 };
+ yield return new object[] { CodeSnippets.CustomCollectionMarshalling_V1.GenericCollectionMarshallingArityMismatch, 2, 0 };
yield return new object[] { CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 2, 0 };
- yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 };
- yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 };
+ yield return new object[] { CodeSnippets.CustomCollectionMarshalling_V1.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 };
+ yield return new object[] { CodeSnippets.CustomCollectionMarshalling_V1.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/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs
index 5536cfd4a24..4a971cd7f1d 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs
@@ -222,20 +222,47 @@ namespace LibraryImportGenerator.UnitTests
yield return new[] { CodeSnippets.MaybeBlittableGenericTypeParametersAndModifiers<double>() };
yield return new[] { CodeSnippets.MaybeBlittableGenericTypeParametersAndModifiers<IntPtr>() };
yield return new[] { CodeSnippets.MaybeBlittableGenericTypeParametersAndModifiers<UIntPtr>() };
+ }
+ public static IEnumerable<object[]> CustomCollections()
+ {
// Custom collection marshalling
- yield return new[] { CodeSnippets.CollectionByValue<byte>() };
- yield return new[] { CodeSnippets.CollectionByValue<sbyte>() };
- yield return new[] { CodeSnippets.CollectionByValue<short>() };
- yield return new[] { CodeSnippets.CollectionByValue<ushort>() };
- yield return new[] { CodeSnippets.CollectionByValue<int>() };
- yield return new[] { CodeSnippets.CollectionByValue<uint>() };
- yield return new[] { CodeSnippets.CollectionByValue<long>() };
- yield return new[] { CodeSnippets.CollectionByValue<ulong>() };
- yield return new[] { CodeSnippets.CollectionByValue<float>() };
- yield return new[] { CodeSnippets.CollectionByValue<double>() };
- yield return new[] { CodeSnippets.CollectionByValue<IntPtr>() };
- yield return new[] { CodeSnippets.CollectionByValue<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<byte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<sbyte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<short>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<ushort>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<uint>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<long>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<ulong>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<float>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<double>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValue<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<byte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<sbyte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<short>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<ushort>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<uint>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<long>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<ulong>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<float>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<double>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.ByValueCallerAllocatedBuffer<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<byte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<sbyte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<short>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<ushort>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<uint>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<long>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<ulong>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<float>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<double>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.ByValue<UIntPtr>() };
yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<byte[]>() };
yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<sbyte[]>() };
yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<short[]>() };
@@ -248,37 +275,66 @@ namespace LibraryImportGenerator.UnitTests
yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<double[]>() };
yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<IntPtr[]>() };
yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<UIntPtr[]>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<byte>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<sbyte>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<short>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<ushort>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<int>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<uint>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<long>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<ulong>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<float>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<double>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<IntPtr>() };
- yield return new[] { CodeSnippets.CustomCollectionDefaultMarshallerParametersAndModifiers<UIntPtr>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<byte>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<sbyte>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<short>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<ushort>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<int>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<uint>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<long>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<ulong>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<float>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<double>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<IntPtr>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<UIntPtr>() };
- yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerReturnValueLength<int>() };
- yield return new[] { CodeSnippets.GenericCollectionWithCustomElementMarshalling };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<byte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<sbyte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<short>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<ushort>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<uint>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<long>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<ulong>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<float>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<double>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.DefaultMarshallerParametersAndModifiers<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<byte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<sbyte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<short>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<ushort>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<uint>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<long>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<ulong>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<float>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<double>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerParametersAndModifiers<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.CustomMarshallerReturnValueLength<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.NativeToManagedOnlyOutParameter<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.NativeToManagedOnlyReturnValue<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling.Stateless.NestedMarshallerParametersAndModifiers<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<byte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<sbyte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<short>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<ushort>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<uint>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<long>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<ulong>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<float>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<double>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.DefaultMarshallerParametersAndModifiers<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<byte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<sbyte>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<short>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<ushort>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<uint>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<long>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<ulong>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<float>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<double>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerParametersAndModifiers<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.CustomMarshallerReturnValueLength<int>() };
+ yield return new[] { CodeSnippets.CustomCollectionMarshalling_V1.GenericCollectionWithCustomElementMarshalling };
yield return new[] { CodeSnippets.CollectionsOfCollectionsStress };
}
[Theory]
[MemberData(nameof(CodeSnippetsToCompile))]
+ [MemberData(nameof(CustomCollections))]
public async Task ValidateSnippets(string source)
{
Compilation comp = await TestUtils.CreateCompilation(source);
@@ -301,7 +357,6 @@ namespace LibraryImportGenerator.UnitTests
yield return new object[] { CodeSnippets.PreprocessorIfAfterAttributeAroundFunctionAdditionalFunctionAfter("Foo"), new string[] { "Foo" } };
yield return new object[] { CodeSnippets.PreprocessorIfAfterAttributeAroundFunctionAdditionalFunctionAfter("Foo"), Array.Empty<string>() };
}
-
[Theory]
[MemberData(nameof(CodeSnippetsToCompileWithPreprocessorSymbols))]
public async Task ValidateSnippetsWithPreprocessorDefintions(string source, IEnumerable<string> preprocessorSymbols)
diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.V1.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.V1.cs
index 7f8d5cc0f72..78b8d1bda02 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.V1.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.V1.cs
@@ -144,24 +144,24 @@ namespace SharedTypes
}
[CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x200)]
- public unsafe ref struct ListMarshaller<T>
+ public unsafe ref struct ListMarshaller_V1<T>
{
private List<T> managedList;
private readonly int sizeOfNativeElement;
private IntPtr allocatedMemory;
- public ListMarshaller(int sizeOfNativeElement)
+ public ListMarshaller_V1(int sizeOfNativeElement)
: this()
{
this.sizeOfNativeElement = sizeOfNativeElement;
}
- public ListMarshaller(List<T> managed, int sizeOfNativeElement)
+ public ListMarshaller_V1(List<T> managed, int sizeOfNativeElement)
:this(managed, Span<byte>.Empty, sizeOfNativeElement)
{
}
- public ListMarshaller(List<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
+ public ListMarshaller_V1(List<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
{
allocatedMemory = default;
this.sizeOfNativeElement = sizeOfNativeElement;
@@ -228,10 +228,10 @@ namespace SharedTypes
}
}
- [NativeMarshalling(typeof(WrappedListMarshaller<>))]
- public struct WrappedList<T>
+ [NativeMarshalling(typeof(WrappedListMarshaller_V1<>))]
+ public struct WrappedList_V1<T>
{
- public WrappedList(List<T> list)
+ public WrappedList_V1(List<T> list)
{
Wrapped = list;
}
@@ -241,25 +241,25 @@ namespace SharedTypes
public ref T GetPinnableReference() => ref CollectionsMarshal.AsSpan(Wrapped).GetPinnableReference();
}
- [CustomTypeMarshaller(typeof(WrappedList<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x200)]
- public unsafe ref struct WrappedListMarshaller<T>
+ [CustomTypeMarshaller(typeof(WrappedList_V1<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x200)]
+ public unsafe ref struct WrappedListMarshaller_V1<T>
{
- private ListMarshaller<T> _marshaller;
+ private ListMarshaller_V1<T> _marshaller;
- public WrappedListMarshaller(int sizeOfNativeElement)
+ public WrappedListMarshaller_V1(int sizeOfNativeElement)
: this()
{
- this._marshaller = new ListMarshaller<T>(sizeOfNativeElement);
+ this._marshaller = new ListMarshaller_V1<T>(sizeOfNativeElement);
}
- public WrappedListMarshaller(WrappedList<T> managed, int sizeOfNativeElement)
+ public WrappedListMarshaller_V1(WrappedList_V1<T> managed, int sizeOfNativeElement)
: this(managed, Span<byte>.Empty, sizeOfNativeElement)
{
}
- public WrappedListMarshaller(WrappedList<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
+ public WrappedListMarshaller_V1(WrappedList_V1<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
{
- this._marshaller = new ListMarshaller<T>(managed.Wrapped, stackSpace, sizeOfNativeElement);
+ this._marshaller = new ListMarshaller_V1<T>(managed.Wrapped, stackSpace, sizeOfNativeElement);
}
public ReadOnlySpan<T> GetManagedValuesSource() => _marshaller.GetManagedValuesSource();
@@ -276,7 +276,7 @@ namespace SharedTypes
public void FromNativeValue(byte* value) => _marshaller.FromNativeValue(value);
- public WrappedList<T> ToManaged() => new(_marshaller.ToManaged());
+ public WrappedList_V1<T> ToManaged() => new(_marshaller.ToManaged());
public void FreeNative() => _marshaller.FreeNative();
}
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 f9f34a6e4de..b01337b9973 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs
@@ -166,7 +166,6 @@ namespace SharedTypes
}
}
-
[CustomMarshaller(typeof(IntWrapper), Scenario.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
@@ -282,4 +281,92 @@ namespace SharedTypes
}
}
}
+
+ [CustomMarshaller(typeof(List<>), Scenario.Default, typeof(ListMarshaller<,>))]
+ public unsafe static class ListMarshaller<T, [ElementUnmanagedType] TUnmanagedElement> where TUnmanagedElement : unmanaged
+ {
+ public static byte* AllocateContainerForUnmanagedElements(List<T> managed, out int numElements)
+ => AllocateContainerForUnmanagedElements(managed, Span<byte>.Empty, out numElements);
+
+ public static byte* AllocateContainerForUnmanagedElements(List<T> managed, Span<byte> buffer, out int numElements)
+ {
+ if (managed is null)
+ {
+ numElements = 0;
+ return null;
+ }
+
+ numElements = managed.Count;
+
+ // Always allocate at least one byte when the list is zero-length.
+ int spaceToAllocate = Math.Max(checked(sizeof(TUnmanagedElement) * numElements), 1);
+ if (spaceToAllocate <= buffer.Length)
+ {
+ return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer));
+ }
+ else
+ {
+ return (byte*)Marshal.AllocCoTaskMem(spaceToAllocate);
+ }
+ }
+
+ public static ReadOnlySpan<T> GetManagedValuesSource(List<T> managed)
+ => CollectionsMarshal.AsSpan(managed);
+
+ public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
+ => new Span<TUnmanagedElement>(unmanaged, numElements);
+
+ public static List<T> AllocateContainerForManagedElements(byte* unmanaged, int length)
+ {
+ if (unmanaged is null)
+ return null;
+
+ var list = new List<T>(length);
+ for (int i = 0; i < length; i++)
+ {
+ list.Add(default);
+ }
+
+ return list;
+ }
+
+ public static Span<T> GetManagedValuesDestination(List<T> managed)
+ => CollectionsMarshal.AsSpan(managed);
+
+ public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* nativeValue, int numElements)
+ => new ReadOnlySpan<TUnmanagedElement>(nativeValue, numElements);
+
+ public static void Free(byte* unmanaged)
+ => Marshal.FreeCoTaskMem((IntPtr)unmanaged);
+ }
+
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), Scenario.Default, typeof(CustomArrayMarshaller<,>))]
+ public unsafe static class CustomArrayMarshaller<T, [ElementUnmanagedType] TUnmanagedElement> where TUnmanagedElement : unmanaged
+ {
+ public static byte* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
+ {
+ if (managed is null)
+ {
+ numElements = 0;
+ return null;
+ }
+
+ numElements = managed.Length;
+ return (byte*)Marshal.AllocCoTaskMem(checked(sizeof(TUnmanagedElement) * numElements));
+ }
+
+ public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
+
+ public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
+ => new Span<TUnmanagedElement>(unmanaged, numElements);
+
+ public static T[] AllocateContainerForManagedElements(byte* unmanaged, int length) => unmanaged is null ? null : new T[length];
+
+ public static Span<T> GetManagedValuesDestination(T[] managed) => managed;
+
+ public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* unmanaged, int numElements)
+ => new Span<TUnmanagedElement>(unmanaged, numElements);
+
+ public static void Free(byte* unmanaged) => Marshal.FreeCoTaskMem((IntPtr)unmanaged);
+ }
}