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

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/design/libraries/DllImportGenerator/SpanMarshallers.md2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs4
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ContiguousCollectionElementMarshallingCodeContext.cs (renamed from src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ArrayMarshallingCodeContext.cs)40
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportStub.cs7
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingHelper.cs83
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ArrayMarshaller.cs141
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs313
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshaller.cs272
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshallingGenerator.cs88
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ICustomNativeTypeMarshallingStrategy.cs985
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallerHelpers.cs22
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallingGenerator.cs191
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs317
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/PinnableManagedValueMarshaller.cs84
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs3
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/MarshallingAttributeInfo.cs667
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx2
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/StubCodeGenerator.cs21
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs12
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypePositionInfo.cs259
-rw-r--r--src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs14
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ArrayMarshaller.cs217
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs34
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs161
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs257
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs28
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/Compiles.cs128
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs112
31 files changed, 3116 insertions, 1354 deletions
diff --git a/docs/design/libraries/DllImportGenerator/SpanMarshallers.md b/docs/design/libraries/DllImportGenerator/SpanMarshallers.md
index 4fa80be494f..a1b3e74f672 100644
--- a/docs/design/libraries/DllImportGenerator/SpanMarshallers.md
+++ b/docs/design/libraries/DllImportGenerator/SpanMarshallers.md
@@ -94,6 +94,8 @@ A generic collection marshaller would be required to have the following shape, i
[GenericContiguousCollectionMarshaller]
public struct GenericContiguousCollectionMarshallerImpl<T, U, V,...>
{
+ // this constructor is required if marshalling from native to managed is supported.
+ public GenericContiguousCollectionMarshallerImpl(int nativeSizeOfElement);
// these constructors are required if marshalling from managed to native is supported.
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, int nativeSizeOfElement);
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, Span<byte> stackSpace, int nativeSizeOfElement); // optional
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs
index 97bc5938771..965592e6522 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs
@@ -364,9 +364,9 @@ namespace Microsoft.Interop.Analyzers
continue;
}
- hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type);
+ hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard);
- if (!hasStackallocConstructor && ManualTypeMarshallingHelper.IsStackallocConstructor(ctor, type, SpanOfByte))
+ if (!hasStackallocConstructor && ManualTypeMarshallingHelper.IsStackallocConstructor(ctor, type, SpanOfByte, ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard))
{
hasStackallocConstructor = true;
IFieldSymbol stackAllocSizeField = nativeType.GetMembers("StackBufferSize").OfType<IFieldSymbol>().FirstOrDefault();
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ArrayMarshallingCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ContiguousCollectionElementMarshallingCodeContext.cs
index 9e8d1ab5a20..89c3b03590d 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ArrayMarshallingCodeContext.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ContiguousCollectionElementMarshallingCodeContext.cs
@@ -10,13 +10,11 @@ using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Microsoft.Interop
{
- internal sealed class ArrayMarshallingCodeContext : StubCodeContext
+ internal sealed class ContiguousCollectionElementMarshallingCodeContext : StubCodeContext
{
- public const string LocalManagedIdentifierSuffix = "_local";
-
private readonly string indexerIdentifier;
+ private readonly string nativeSpanIdentifier;
private readonly StubCodeContext parentContext;
- private readonly bool appendLocalManagedIdentifierSuffix;
public override bool PinningSupported => false;
@@ -27,34 +25,29 @@ namespace Microsoft.Interop
/// can be added to the stub to track additional state for the marshaller in the stub.
/// </summary>
/// <remarks>
- /// Currently, array scenarios do not support declaring additional temporary variables to support
+ /// Currently, collection scenarios do not support declaring additional temporary variables to support
/// marshalling. This can be accomplished in the future with some additional infrastructure to support
- /// declaring arrays additional arrays in the stub to support the temporary state.
+ /// declaring additional arrays in the stub to support the temporary state.
/// </remarks>
public override bool CanUseAdditionalTemporaryState => false;
/// <summary>
- /// Create a <see cref="StubCodeContext"/> for marshalling elements of an array.
+ /// Create a <see cref="StubCodeContext"/> for marshalling elements of an collection.
/// </summary>
/// <param name="currentStage">The current marshalling stage.</param>
- /// <param name="indexerIdentifier">The indexer in the loop to get the element to marshal from the array.</param>
+ /// <param name="indexerIdentifier">The indexer in the loop to get the element to marshal from the collection.</param>
+ /// <param name="nativeSpanIdentifier">The identifier of the native value storage cast to the target element type.</param>
/// <param name="parentContext">The parent context.</param>
- /// <param name="appendLocalManagedIdentifierSuffix">
- /// For array marshalling, we sometimes cache the array in a local to avoid multithreading issues.
- /// Set this to <c>true</c> to add the <see cref="LocalManagedIdentifierSuffix"/> to the managed identifier when
- /// marshalling the array elements to ensure that we use the local copy instead of the managed identifier
- /// when marshalling elements.
- /// </param>
- public ArrayMarshallingCodeContext(
+ public ContiguousCollectionElementMarshallingCodeContext(
Stage currentStage,
string indexerIdentifier,
- StubCodeContext parentContext,
- bool appendLocalManagedIdentifierSuffix)
+ string nativeSpanIdentifier,
+ StubCodeContext parentContext)
{
CurrentStage = currentStage;
this.indexerIdentifier = indexerIdentifier;
+ this.nativeSpanIdentifier = nativeSpanIdentifier;
this.parentContext = parentContext;
- this.appendLocalManagedIdentifierSuffix = appendLocalManagedIdentifierSuffix;
}
/// <summary>
@@ -64,12 +57,11 @@ namespace Microsoft.Interop
/// <returns>Managed and native identifiers</returns>
public override (string managed, string native) GetIdentifiers(TypePositionInfo info)
{
- var (managed, native) = parentContext.GetIdentifiers(info);
- if (appendLocalManagedIdentifierSuffix)
- {
- managed += LocalManagedIdentifierSuffix;
- }
- return ($"{managed}[{indexerIdentifier}]", $"{native}[{indexerIdentifier}]");
+ var (_, native) = parentContext.GetIdentifiers(info);
+ return (
+ $"{native}.ManagedValues[{indexerIdentifier}]",
+ $"{nativeSpanIdentifier}[{indexerIdentifier}]"
+ );
}
public override TypePositionInfo? GetTypePositionInfoForManagedIndex(int index)
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportStub.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportStub.cs
index cff9d7f41d8..f74f9b2a3cf 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportStub.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/DllImportStub.cs
@@ -157,12 +157,15 @@ namespace Microsoft.Interop
var defaultInfo = new DefaultMarshallingInfo(defaultEncoding);
+ var marshallingAttributeParser = new MarshallingAttributeInfoParser(env.Compilation, diagnostics, defaultInfo, method);
+
// Determine parameter and return types
var paramsTypeInfo = new List<TypePositionInfo>();
for (int i = 0; i < method.Parameters.Length; i++)
{
var param = method.Parameters[i];
- var typeInfo = TypePositionInfo.CreateForParameter(param, defaultInfo, env.Compilation, diagnostics, method.ContainingType);
+ MarshallingInfo marshallingInfo = marshallingAttributeParser.ParseMarshallingInfo(param.Type, param.GetAttributes());
+ var typeInfo = TypePositionInfo.CreateForParameter(param, marshallingInfo, env.Compilation);
typeInfo = typeInfo with
{
ManagedIndex = i,
@@ -171,7 +174,7 @@ namespace Microsoft.Interop
paramsTypeInfo.Add(typeInfo);
}
- TypePositionInfo retTypeInfo = TypePositionInfo.CreateForType(method.ReturnType, method.GetReturnTypeAttributes(), defaultInfo, env.Compilation, diagnostics, method.ContainingType);
+ TypePositionInfo retTypeInfo = TypePositionInfo.CreateForType(method.ReturnType, marshallingAttributeParser.ParseMarshallingInfo(method.ReturnType, method.GetReturnTypeAttributes()));
retTypeInfo = retTypeInfo with
{
ManagedIndex = TypePositionInfo.ReturnIndex,
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingHelper.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingHelper.cs
index eb825f1c7a6..1d073064a39 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingHelper.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/ManualTypeMarshallingHelper.cs
@@ -1,4 +1,5 @@
+using System;
using System.Linq;
using Microsoft.CodeAnalysis;
@@ -11,6 +12,22 @@ namespace Microsoft.Interop
public const string StackBufferSizeFieldName = "StackBufferSize";
public const string ToManagedMethodName = "ToManaged";
public const string FreeNativeMethodName = "FreeNative";
+ public const string ManagedValuesPropertyName = "ManagedValues";
+ public const string NativeValueStoragePropertyName = "NativeValueStorage";
+ public const string SetUnmarshalledCollectionLengthMethodName = "SetUnmarshalledCollectionLength";
+
+ public static class MarshalUsingProperties
+ {
+ public const string ElementIndirectionLevel = nameof(ElementIndirectionLevel);
+ public const string CountElementName = nameof(CountElementName);
+ public const string ConstantElementCount = nameof(ConstantElementCount);
+ }
+
+ public enum NativeTypeMarshallingVariant
+ {
+ Standard,
+ ContiguousCollection
+ }
public static bool HasToManagedMethod(ITypeSymbol nativeType, ITypeSymbol managedType)
{
@@ -23,8 +40,17 @@ namespace Microsoft.Interop
&& !m.IsStatic);
}
- public static bool IsManagedToNativeConstructor(IMethodSymbol ctor, ITypeSymbol managedType)
+ public static bool IsManagedToNativeConstructor(
+ IMethodSymbol ctor,
+ ITypeSymbol managedType,
+ NativeTypeMarshallingVariant variant)
{
+ if (variant == NativeTypeMarshallingVariant.ContiguousCollection)
+ {
+ return ctor.Parameters.Length == 2
+ && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type)
+ && ctor.Parameters[1].Type.SpecialType == SpecialType.System_Int32;
+ }
return ctor.Parameters.Length == 1
&& SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type);
}
@@ -32,8 +58,16 @@ namespace Microsoft.Interop
public static bool IsStackallocConstructor(
IMethodSymbol ctor,
ITypeSymbol managedType,
- ITypeSymbol spanOfByte)
+ ITypeSymbol spanOfByte,
+ NativeTypeMarshallingVariant variant)
{
+ if (variant == NativeTypeMarshallingVariant.ContiguousCollection)
+ {
+ return ctor.Parameters.Length == 3
+ && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type)
+ && SymbolEqualityComparer.Default.Equals(spanOfByte, ctor.Parameters[1].Type)
+ && ctor.Parameters[2].Type.SpecialType == SpecialType.System_Int32;
+ }
return ctor.Parameters.Length == 2
&& SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type)
&& SymbolEqualityComparer.Default.Equals(spanOfByte, ctor.Parameters[1].Type);
@@ -62,8 +96,49 @@ namespace Microsoft.Interop
{
return type.GetMembers(FreeNativeMethodName)
.OfType<IMethodSymbol>()
- .Any(m => m is { Parameters: { Length: 0 } } and
- ({ ReturnType: { SpecialType: SpecialType.System_Void } }));
+ .Any(m => m is { IsStatic: false, Parameters: { Length: 0 }, ReturnType: { SpecialType: SpecialType.System_Void } });
+ }
+
+ public static bool TryGetManagedValuesProperty(ITypeSymbol type, out IPropertySymbol managedValuesProperty)
+ {
+ managedValuesProperty = type
+ .GetMembers(ManagedValuesPropertyName)
+ .OfType<IPropertySymbol>()
+ .FirstOrDefault(p => p is { IsStatic: false, GetMethod: not null, ReturnsByRef: false, ReturnsByRefReadonly: false });
+ return managedValuesProperty is not null;
+ }
+
+ public static bool TryGetElementTypeFromContiguousCollectionMarshaller(ITypeSymbol type, out ITypeSymbol elementType)
+ {
+ if (!TryGetManagedValuesProperty(type, out IPropertySymbol managedValuesProperty))
+ {
+ elementType = null!;
+ return false;
+ }
+
+ elementType = ((INamedTypeSymbol)managedValuesProperty.Type).TypeArguments[0];
+ return true;
+ }
+
+ public static bool HasSetUnmarshalledCollectionLengthMethod(ITypeSymbol type)
+ {
+ return type.GetMembers(SetUnmarshalledCollectionLengthMethodName)
+ .OfType<IMethodSymbol>()
+ .Any(m => m is
+ {
+ IsStatic: false,
+ Parameters: { Length: 1 },
+ ReturnType: { SpecialType: SpecialType.System_Void }
+ } && m.Parameters[0].Type.SpecialType == SpecialType.System_Int32);
+ }
+
+ public static bool HasNativeValueStorageProperty(ITypeSymbol type, ITypeSymbol spanOfByte)
+ {
+ return type
+ .GetMembers(NativeValueStoragePropertyName)
+ .OfType<IPropertySymbol>()
+ .Any(p => p is {IsStatic: false, GetMethod: not null, ReturnsByRef: false, ReturnsByRefReadonly: false }
+ && SymbolEqualityComparer.Default.Equals(p.Type, spanOfByte));
}
}
} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ArrayMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ArrayMarshaller.cs
new file mode 100644
index 00000000000..6d0f98c533c
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ArrayMarshaller.cs
@@ -0,0 +1,141 @@
+ο»Ώusing System.Collections.Generic;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+ internal sealed class ArrayMarshaller : IMarshallingGenerator
+ {
+ private readonly IMarshallingGenerator manualMarshallingGenerator;
+ private readonly TypeSyntax elementType;
+ private readonly bool enablePinning;
+
+ public ArrayMarshaller(IMarshallingGenerator manualMarshallingGenerator, TypeSyntax elementType, bool enablePinning)
+ {
+ this.manualMarshallingGenerator = manualMarshallingGenerator;
+ this.elementType = elementType;
+ this.enablePinning = enablePinning;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ if (IsPinningPathSupported(info, context))
+ {
+ string identifier = context.GetIdentifiers(info).native;
+ return Argument(CastExpression(AsNativeType(info), IdentifierName(identifier)));
+ }
+ return manualMarshallingGenerator.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return manualMarshallingGenerator.AsNativeType(info);
+ }
+
+ public ParameterSyntax AsParameter(TypePositionInfo info)
+ {
+ return manualMarshallingGenerator.AsParameter(info);
+ }
+
+ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
+ {
+ if (IsPinningPathSupported(info, context))
+ {
+ return GeneratePinningPath(info, context);
+ }
+ return manualMarshallingGenerator.Generate(info, context);
+ }
+
+ public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context)
+ {
+ if (context.PinningSupported && enablePinning)
+ {
+ return false;
+ }
+ return marshalKind.HasFlag(ByValueContentsMarshalKind.Out);
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ if (IsPinningPathSupported(info, context))
+ {
+ return false;
+ }
+ return manualMarshallingGenerator.UsesNativeIdentifier(info, context);
+ }
+
+ private bool IsPinningPathSupported(TypePositionInfo info, StubCodeContext context)
+ {
+ return context.PinningSupported && enablePinning && !info.IsByRef && !info.IsManagedReturnPosition;
+ }
+
+ private IEnumerable<StatementSyntax> GeneratePinningPath(TypePositionInfo info, StubCodeContext context)
+ {
+ var (managedIdentifer, nativeIdentifier) = context.GetIdentifiers(info);
+ string byRefIdentifier = $"__byref_{managedIdentifer}";
+ TypeSyntax arrayElementType = elementType;
+ if (context.CurrentStage == StubCodeContext.Stage.Marshal)
+ {
+ // [COMPAT] We use explicit byref calculations here instead of just using a fixed statement
+ // since a fixed statement converts a zero-length array to a null pointer.
+ // Many native APIs, such as GDI+, ICU, etc. validate that an array parameter is non-null
+ // even when the passed in array length is zero. To avoid breaking customers that want to move
+ // to source-generated interop in subtle ways, we explicitly pass a reference to the 0-th element
+ // of an array as long as it is non-null, matching the behavior of the built-in interop system
+ // for single-dimensional zero-based arrays.
+
+ // ref <elementType> <byRefIdentifier> = <managedIdentifer> == null ? ref *(<elementType*)0 : ref MemoryMarshal.GetArrayDataReference(<managedIdentifer>);
+ var nullRef =
+ PrefixUnaryExpression(SyntaxKind.PointerIndirectionExpression,
+ CastExpression(
+ PointerType(arrayElementType),
+ LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))));
+
+ var getArrayDataReference =
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ IdentifierName("GetArrayDataReference")),
+ ArgumentList(SingletonSeparatedList(
+ Argument(IdentifierName(managedIdentifer)))));
+
+ yield return LocalDeclarationStatement(
+ VariableDeclaration(
+ RefType(arrayElementType))
+ .WithVariables(SingletonSeparatedList(
+ VariableDeclarator(Identifier(byRefIdentifier))
+ .WithInitializer(EqualsValueClause(
+ RefExpression(ParenthesizedExpression(
+ ConditionalExpression(
+ BinaryExpression(
+ SyntaxKind.EqualsExpression,
+ IdentifierName(managedIdentifer),
+ LiteralExpression(
+ SyntaxKind.NullLiteralExpression)),
+ RefExpression(nullRef),
+ RefExpression(getArrayDataReference)))))))));
+ }
+ if (context.CurrentStage == StubCodeContext.Stage.Pin)
+ {
+ // fixed (<nativeType> <nativeIdentifier> = &Unsafe.As<elementType, byte>(ref <byrefIdentifier>))
+ yield return FixedStatement(
+ VariableDeclaration(AsNativeType(info), SingletonSeparatedList(
+ VariableDeclarator(nativeIdentifier)
+ .WithInitializer(EqualsValueClause(
+ PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ParseTypeName(TypeNames.System_Runtime_CompilerServices_Unsafe),
+ GenericName("As").AddTypeArgumentListArguments(
+ arrayElementType,
+ PredefinedType(Token(SyntaxKind.ByteKeyword)))))
+ .AddArgumentListArguments(
+ Argument(IdentifierName(byRefIdentifier))
+ .WithRefKindKeyword(Token(SyntaxKind.RefKeyword)))))))),
+ EmptyStatement());
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs
deleted file mode 100644
index cd42a96d832..00000000000
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs
+++ /dev/null
@@ -1,313 +0,0 @@
-using System.Collections.Generic;
-
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-
-namespace Microsoft.Interop
-{
- internal class BlittableArrayMarshaller : ConditionalStackallocMarshallingGenerator
- {
- /// <summary>
- /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
- /// Number kept small to ensure that P/Invokes with a lot of small array parameters doesn't
- /// blow the stack since this is a new optimization in the code-generated interop.
- /// </summary>
- private const int StackAllocBytesThreshold = 0x200;
- private readonly ExpressionSyntax _numElementsExpr;
-
- public BlittableArrayMarshaller(ExpressionSyntax numElementsExpr)
- {
- _numElementsExpr = numElementsExpr;
- }
-
- private TypeSyntax GetElementTypeSyntax(TypePositionInfo info)
- {
- return ((IArrayTypeSymbol)info.ManagedType).ElementType.AsTypeSyntax();
- }
-
- public override TypeSyntax AsNativeType(TypePositionInfo info)
- {
- return PointerType(GetElementTypeSyntax(info));
- }
-
- public override ParameterSyntax AsParameter(TypePositionInfo info)
- {
- var type = info.IsByRef
- ? PointerType(AsNativeType(info))
- : AsNativeType(info);
- return Parameter(Identifier(info.InstanceIdentifier))
- .WithType(type);
- }
-
- public override ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
- {
- return info.IsByRef
- ? Argument(
- PrefixUnaryExpression(
- SyntaxKind.AddressOfExpression,
- IdentifierName(context.GetIdentifiers(info).native)))
- : Argument(IdentifierName(context.GetIdentifiers(info).native));
- }
-
- public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
- {
- var (managedIdentifer, nativeIdentifier) = context.GetIdentifiers(info);
- if (!info.IsByRef && !info.IsManagedReturnPosition && context.PinningSupported)
- {
- string byRefIdentifier = $"__byref_{managedIdentifer}";
- if (context.CurrentStage == StubCodeContext.Stage.Marshal)
- {
- // [COMPAT] We use explicit byref calculations here instead of just using a fixed statement
- // since a fixed statement converts a zero-length array to a null pointer.
- // Many native APIs, such as GDI+, ICU, etc. validate that an array parameter is non-null
- // even when the passed in array length is zero. To avoid breaking customers that want to move
- // to source-generated interop in subtle ways, we explicitly pass a reference to the 0-th element
- // of an array as long as it is non-null, matching the behavior of the built-in interop system
- // for single-dimensional zero-based arrays.
-
- // ref <elementType> <byRefIdentifier> = <managedIdentifer> == null ? ref *(<elementType*)0 : ref MemoryMarshal.GetArrayDataReference(<managedIdentifer>);
- var nullRef =
- PrefixUnaryExpression(SyntaxKind.PointerIndirectionExpression,
- CastExpression(
- PointerType(GetElementTypeSyntax(info)),
- LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))));
-
- var getArrayDataReference =
- InvocationExpression(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
- IdentifierName("GetArrayDataReference")),
- ArgumentList(SingletonSeparatedList(
- Argument(IdentifierName(managedIdentifer)))));
-
- yield return LocalDeclarationStatement(
- VariableDeclaration(
- RefType(GetElementTypeSyntax(info)))
- .WithVariables(SingletonSeparatedList(
- VariableDeclarator(Identifier(byRefIdentifier))
- .WithInitializer(EqualsValueClause(
- RefExpression(ParenthesizedExpression(
- ConditionalExpression(
- BinaryExpression(
- SyntaxKind.EqualsExpression,
- IdentifierName(managedIdentifer),
- LiteralExpression(
- SyntaxKind.NullLiteralExpression)),
- RefExpression(nullRef),
- RefExpression(getArrayDataReference)))))))));
- }
- if (context.CurrentStage == StubCodeContext.Stage.Pin)
- {
- // fixed (<nativeType> <nativeIdentifier> = &<byrefIdentifier>)
- yield return FixedStatement(
- VariableDeclaration(AsNativeType(info), SingletonSeparatedList(
- VariableDeclarator(nativeIdentifier)
- .WithInitializer(EqualsValueClause(
- PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
- IdentifierName(byRefIdentifier)))))),
- EmptyStatement());
- }
- yield break;
- }
-
- TypeSyntax spanElementTypeSyntax = GetElementTypeSyntax(info);
- if (spanElementTypeSyntax is PointerTypeSyntax)
- {
- // Pointers cannot be passed to generics, so use IntPtr for this case.
- spanElementTypeSyntax = ParseTypeName("System.IntPtr");
- }
-
- switch (context.CurrentStage)
- {
- case StubCodeContext.Stage.Setup:
- if (TryGenerateSetupSyntax(info, context, out StatementSyntax conditionalAllocSetup))
- yield return conditionalAllocSetup;
-
- break;
- case StubCodeContext.Stage.Marshal:
- if (info.RefKind != RefKind.Out)
- {
- foreach (var statement in GenerateConditionalAllocationSyntax(
- info,
- context,
- StackAllocBytesThreshold))
- {
- yield return statement;
- }
-
- // new Span<T>(nativeIdentifier, managedIdentifier.Length)
- var nativeSpan = ObjectCreationExpression(
- GenericName(TypeNames.System_Span)
- .WithTypeArgumentList(
- TypeArgumentList(
- SingletonSeparatedList(spanElementTypeSyntax))))
- .WithArgumentList(
- ArgumentList(
- SeparatedList(
- new []{
- Argument(
- CastExpression(
- PointerType(spanElementTypeSyntax),
- IdentifierName(nativeIdentifier))),
- Argument(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(managedIdentifer),
- IdentifierName("Length")))
- })));
-
- // new Span<T>(managedIdentifier).CopyTo(<nativeSpan>);
- yield return IfStatement(
- BinaryExpression(SyntaxKind.NotEqualsExpression,
- IdentifierName(managedIdentifer),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
- ExpressionStatement(
- InvocationExpression(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- ObjectCreationExpression(
- GenericName(Identifier(TypeNames.System_Span),
- TypeArgumentList(
- SingletonSeparatedList(
- spanElementTypeSyntax))))
- .WithArgumentList(
- ArgumentList(SingletonSeparatedList(
- Argument(IdentifierName(managedIdentifer))))),
- IdentifierName("CopyTo")))
- .WithArgumentList(
- ArgumentList(
- SingletonSeparatedList(
- Argument(nativeSpan))))));
- }
- break;
- case StubCodeContext.Stage.Unmarshal:
- if (info.IsManagedReturnPosition
- || (info.IsByRef && info.RefKind != RefKind.In)
- || info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out))
- {
- // new Span<T>(nativeIdentifier, managedIdentifier.Length).CopyTo(managedIdentifier);
- var unmarshalContentsStatement =
- ExpressionStatement(
- InvocationExpression(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- ObjectCreationExpression(
- GenericName(Identifier(TypeNames.System_Span),
- TypeArgumentList(
- SingletonSeparatedList(
- spanElementTypeSyntax))))
- .WithArgumentList(
- ArgumentList(
- SeparatedList(
- new[]{
- Argument(CastExpression(
- PointerType(spanElementTypeSyntax),
- IdentifierName(nativeIdentifier))),
- Argument(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(managedIdentifer),
- IdentifierName("Length")))
- }))),
- IdentifierName("CopyTo")))
- .WithArgumentList(
- ArgumentList(
- SingletonSeparatedList(
- Argument(IdentifierName(managedIdentifer))))));
-
- if (info.IsManagedReturnPosition || info.IsByRef)
- {
- yield return IfStatement(
- BinaryExpression(SyntaxKind.NotEqualsExpression,
- IdentifierName(nativeIdentifier),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
- Block(
- // <managedIdentifier> = new <managedElementType>[<numElementsExpression>];
- ExpressionStatement(
- AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(managedIdentifer),
- ArrayCreationExpression(
- ArrayType(GetElementTypeSyntax(info),
- SingletonList(ArrayRankSpecifier(
- SingletonSeparatedList(_numElementsExpr))))))),
- unmarshalContentsStatement),
- ElseClause(
- ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(managedIdentifer),
- LiteralExpression(SyntaxKind.NullLiteralExpression)))));
- }
- else
- {
- yield return IfStatement(
- BinaryExpression(SyntaxKind.NotEqualsExpression,
- IdentifierName(managedIdentifer),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
- unmarshalContentsStatement);
- }
-
- }
- break;
- case StubCodeContext.Stage.Cleanup:
- yield return GenerateConditionalAllocationFreeSyntax(info, context);
- break;
- }
- }
-
- public override bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
- {
- return (info.IsByRef || info.IsManagedReturnPosition) || !context.PinningSupported;
- }
-
- protected override ExpressionSyntax GenerateAllocationExpression(TypePositionInfo info, StubCodeContext context, SyntaxToken byteLengthIdentifier, out bool allocationRequiresByteLength)
- {
- allocationRequiresByteLength = true;
- // (<nativeType>)Marshal.AllocCoTaskMem(<byteLengthIdentifier>)
- return CastExpression(AsNativeType(info),
- InvocationExpression(
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- ParseTypeName(TypeNames.System_Runtime_InteropServices_Marshal),
- IdentifierName("AllocCoTaskMem")),
- ArgumentList(SingletonSeparatedList(Argument(IdentifierName(byteLengthIdentifier))))));
- }
-
- protected override ExpressionSyntax GenerateByteLengthCalculationExpression(TypePositionInfo info, StubCodeContext context)
- {
- // checked(sizeof(<nativeElementType>) * <managedIdentifier>.Length)
- return CheckedExpression(SyntaxKind.CheckedExpression,
- BinaryExpression(SyntaxKind.MultiplyExpression,
- SizeOfExpression(GetElementTypeSyntax(info)),
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(context.GetIdentifiers(info).managed),
- IdentifierName("Length"))));
- }
-
- protected override StatementSyntax GenerateStackallocOnlyValueMarshalling(TypePositionInfo info, StubCodeContext context, SyntaxToken byteLengthIdentifier, SyntaxToken stackAllocPtrIdentifier)
- {
- return EmptyStatement();
- }
-
- protected override ExpressionSyntax GenerateFreeExpression(TypePositionInfo info, StubCodeContext context)
- {
- // Marshal.FreeCoTaskMem((IntPtr)<nativeIdentifier>)
- return InvocationExpression(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- ParseTypeName(TypeNames.System_Runtime_InteropServices_Marshal),
- IdentifierName("FreeCoTaskMem")),
- ArgumentList(SingletonSeparatedList(
- Argument(
- CastExpression(
- ParseTypeName("System.IntPtr"),
- IdentifierName(context.GetIdentifiers(info).native))))));
- }
-
- public override bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context)
- {
- return !context.PinningSupported && marshalKind.HasFlag(ByValueContentsMarshalKind.Out);
- }
- }
-
-}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshaller.cs
deleted file mode 100644
index f1bda542f03..00000000000
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshaller.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-ο»Ώusing System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-
-namespace Microsoft.Interop
-{
- class CustomNativeTypeMarshaller : IMarshallingGenerator
- {
- private const string MarshalerLocalSuffix = "__marshaler";
- private readonly TypeSyntax _nativeTypeSyntax;
- private readonly TypeSyntax _nativeLocalTypeSyntax;
- private readonly SupportedMarshallingMethods _marshallingMethods;
- private readonly bool _hasFreeNative;
- private readonly bool _useValueProperty;
- private readonly bool _marshalerTypePinnable;
-
- public CustomNativeTypeMarshaller(NativeMarshallingAttributeInfo marshallingInfo)
- {
- ITypeSymbol nativeType = marshallingInfo.ValuePropertyType ?? marshallingInfo.NativeMarshallingType;
- _nativeTypeSyntax = ParseTypeName(nativeType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- _nativeLocalTypeSyntax = ParseTypeName(marshallingInfo.NativeMarshallingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- _marshallingMethods = marshallingInfo.MarshallingMethods;
- _hasFreeNative = ManualTypeMarshallingHelper.HasFreeNativeMethod(marshallingInfo.NativeMarshallingType);
- _useValueProperty = marshallingInfo.ValuePropertyType != null;
- _marshalerTypePinnable = marshallingInfo.NativeTypePinnable;
- }
-
- public CustomNativeTypeMarshaller(GeneratedNativeMarshallingAttributeInfo marshallingInfo)
- {
- _nativeTypeSyntax = _nativeLocalTypeSyntax = ParseTypeName(marshallingInfo.NativeMarshallingFullyQualifiedTypeName);
- _marshallingMethods = SupportedMarshallingMethods.ManagedToNative | SupportedMarshallingMethods.NativeToManaged;
- _hasFreeNative = true;
- _useValueProperty = false;
- _marshalerTypePinnable = false;
- }
-
- public TypeSyntax AsNativeType(TypePositionInfo info)
- {
- return _nativeTypeSyntax;
- }
-
- public ParameterSyntax AsParameter(TypePositionInfo info)
- {
- var type = info.IsByRef
- ? PointerType(AsNativeType(info))
- : AsNativeType(info);
- return Parameter(Identifier(info.InstanceIdentifier))
- .WithType(type);
- }
-
- public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
- {
- string identifier = context.GetIdentifiers(info).native;
- if (info.IsByRef)
- {
- return Argument(
- PrefixUnaryExpression(
- SyntaxKind.AddressOfExpression,
- IdentifierName(identifier)));
- }
-
- if (context.PinningSupported && (_marshallingMethods & SupportedMarshallingMethods.Pinning) != 0)
- {
- return Argument(CastExpression(AsNativeType(info), IdentifierName(identifier)));
- }
-
- return Argument(IdentifierName(identifier));
- }
-
- public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
- {
- (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info);
- string marshalerIdentifier = _useValueProperty ? nativeIdentifier + MarshalerLocalSuffix : nativeIdentifier;
- if (!info.IsManagedReturnPosition
- && !info.IsByRef
- && context.PinningSupported
- && (_marshallingMethods & SupportedMarshallingMethods.Pinning) != 0)
- {
- if (context.CurrentStage == StubCodeContext.Stage.Pin)
- {
- yield return FixedStatement(
- VariableDeclaration(
- PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))),
- SingletonSeparatedList(
- VariableDeclarator(Identifier(nativeIdentifier))
- .WithInitializer(EqualsValueClause(
- IdentifierName(managedIdentifier)
- ))
- )
- ),
- EmptyStatement()
- );
- }
- yield break;
- }
-
- switch (context.CurrentStage)
- {
- case StubCodeContext.Stage.Setup:
- if (_useValueProperty)
- {
- yield return LocalDeclarationStatement(
- VariableDeclaration(
- _nativeLocalTypeSyntax,
- SingletonSeparatedList(
- VariableDeclarator(marshalerIdentifier)
- .WithInitializer(EqualsValueClause(LiteralExpression(SyntaxKind.DefaultLiteralExpression))))));
- }
- break;
- case StubCodeContext.Stage.Marshal:
- if (!info.IsManagedReturnPosition && info.RefKind != RefKind.Out)
- {
- // Stack space must be usable and the marshaler must support stackalloc to use stackalloc.
- // We also require pinning to be supported to enable users to pass the stackalloc'd Span
- // to native code by having the marshaler type return a byref to the Span's elements
- // in its GetPinnableReference method.
- bool scenarioSupportsStackalloc = context.StackSpaceUsable
- && (_marshallingMethods & SupportedMarshallingMethods.ManagedToNativeStackalloc) != 0
- && context.PinningSupported;
-
- List<ArgumentSyntax> arguments = new List<ArgumentSyntax>
- {
- Argument(IdentifierName(managedIdentifier))
- };
-
- if (scenarioSupportsStackalloc && (!info.IsByRef || info.RefKind == RefKind.In))
- {
- string stackallocIdentifier = $"{managedIdentifier}__stackptr";
- // byte* <managedIdentifier>__stackptr = stackalloc byte[<_nativeLocalType>.StackBufferSize];
- yield return LocalDeclarationStatement(
- VariableDeclaration(
- PointerType(PredefinedType(Token(SyntaxKind.ByteKeyword))),
- SingletonSeparatedList(
- VariableDeclarator(stackallocIdentifier)
- .WithInitializer(EqualsValueClause(
- StackAllocArrayCreationExpression(
- ArrayType(
- PredefinedType(Token(SyntaxKind.ByteKeyword)),
- SingletonList(ArrayRankSpecifier(SingletonSeparatedList<ExpressionSyntax>(
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- _nativeLocalTypeSyntax,
- IdentifierName(ManualTypeMarshallingHelper.StackBufferSizeFieldName))
- ))))))))));
-
- // new Span<byte>(<managedIdentifier>__stackptr, <_nativeLocalType>.StackBufferSize)
- arguments.Add(Argument(
- ObjectCreationExpression(
- GenericName(Identifier(TypeNames.System_Span),
- TypeArgumentList(SingletonSeparatedList<TypeSyntax>(
- PredefinedType(Token(SyntaxKind.ByteKeyword))))))
- .WithArgumentList(
- ArgumentList(SeparatedList(new ArgumentSyntax[]
- {
- Argument(IdentifierName(stackallocIdentifier)),
- Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- _nativeLocalTypeSyntax,
- IdentifierName(ManualTypeMarshallingHelper.StackBufferSizeFieldName)))
- })))));
- }
-
- // <marshalerIdentifier> = new <_nativeLocalType>(<arguments>);
- yield return ExpressionStatement(
- AssignmentExpression(
- SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(marshalerIdentifier),
- ObjectCreationExpression(_nativeLocalTypeSyntax)
- .WithArgumentList(ArgumentList(SeparatedList(arguments)))));
-
- bool skipValueProperty = _marshalerTypePinnable && (!info.IsByRef || info.RefKind == RefKind.In);
-
- if (_useValueProperty && !skipValueProperty)
- {
- // <nativeIdentifier> = <marshalerIdentifier>.Value;
- yield return ExpressionStatement(
- AssignmentExpression(
- SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(nativeIdentifier),
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(marshalerIdentifier),
- IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName))));
- }
- }
- break;
- case StubCodeContext.Stage.Pin:
- if (_marshalerTypePinnable && (!info.IsByRef || info.RefKind == RefKind.In))
- {
- // fixed (<_nativeTypeSyntax> <nativeIdentifier> = &<marshalerIdentifier>)
- yield return FixedStatement(
- VariableDeclaration(
- _nativeTypeSyntax,
- SingletonSeparatedList(
- VariableDeclarator(nativeIdentifier)
- .WithInitializer(EqualsValueClause(
- PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
- InvocationExpression(
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(marshalerIdentifier),
- IdentifierName(ManualTypeMarshallingHelper.GetPinnableReferenceName)),
- ArgumentList())))))),
- EmptyStatement());
- }
- break;
- case StubCodeContext.Stage.Unmarshal:
- if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
- {
- if (_useValueProperty)
- {
- // <marshalerIdentifier>.Value = <nativeIdentifier>;
- yield return ExpressionStatement(
- AssignmentExpression(
- SyntaxKind.SimpleAssignmentExpression,
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(marshalerIdentifier),
- IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)),
- IdentifierName(nativeIdentifier)));
- }
-
- // <managedIdentifier> = <marshalerIdentifier>.ToManaged();
- yield return ExpressionStatement(
- AssignmentExpression(
- SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(managedIdentifier),
- InvocationExpression(
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(marshalerIdentifier),
- IdentifierName(ManualTypeMarshallingHelper.ToManagedMethodName)))));
- }
- break;
- case StubCodeContext.Stage.Cleanup:
- if (_hasFreeNative)
- {
- // <marshalerIdentifier>.FreeNative();
- yield return ExpressionStatement(
- InvocationExpression(
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(marshalerIdentifier),
- IdentifierName(ManualTypeMarshallingHelper.FreeNativeMethodName))));
- }
- break;
- // TODO: Determine how to keep alive delegates that are in struct fields.
- default:
- break;
- }
- }
-
- public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
- {
- if (info.IsManagedReturnPosition || info.IsByRef && info.RefKind != RefKind.In)
- {
- return true;
- }
- if (context.PinningSupported)
- {
- if (!info.IsByRef && (_marshallingMethods & SupportedMarshallingMethods.Pinning) != 0)
- {
- return false;
- }
- else if (_marshalerTypePinnable)
- {
- return false;
- }
- }
- return true;
- }
-
- public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
- }
-}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshallingGenerator.cs
new file mode 100644
index 00000000000..1f81e5040cf
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/CustomNativeTypeMarshallingGenerator.cs
@@ -0,0 +1,88 @@
+ο»Ώusing System;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+ /// <summary>
+ /// Implements generating code for an <see cref="ICustomNativeTypeMarshallingStrategy"/> instance.
+ /// </summary>
+ internal sealed class CustomNativeTypeMarshallingGenerator : IMarshallingGenerator
+ {
+ private readonly ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller;
+ private readonly bool enableByValueContentsMarshalling;
+
+ public CustomNativeTypeMarshallingGenerator(ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller, bool enableByValueContentsMarshalling)
+ {
+ this.nativeTypeMarshaller = nativeTypeMarshaller;
+ this.enableByValueContentsMarshalling = enableByValueContentsMarshalling;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ return nativeTypeMarshaller.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return nativeTypeMarshaller.AsNativeType(info);
+ }
+
+ public ParameterSyntax AsParameter(TypePositionInfo info)
+ {
+ var type = info.IsByRef
+ ? PointerType(AsNativeType(info))
+ : AsNativeType(info);
+ return Parameter(Identifier(info.InstanceIdentifier))
+ .WithType(type);
+ }
+
+ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
+ {
+ // Although custom native type marshalling doesn't support [In] or [Out] by value marshalling,
+ // other marshallers that wrap this one might, so we handle the correct cases here.
+ switch (context.CurrentStage)
+ {
+ case StubCodeContext.Stage.Setup:
+ return nativeTypeMarshaller.GenerateSetupStatements(info, context);
+ case StubCodeContext.Stage.Marshal:
+ if (!info.IsManagedReturnPosition && info.RefKind != RefKind.Out)
+ {
+ return nativeTypeMarshaller.GenerateMarshalStatements(info, context, nativeTypeMarshaller.GetNativeTypeConstructorArguments(info, context));
+ }
+ break;
+ case StubCodeContext.Stage.Pin:
+ if (!info.IsByRef || info.RefKind == RefKind.In)
+ {
+ return nativeTypeMarshaller.GeneratePinStatements(info, context);
+ }
+ break;
+ case StubCodeContext.Stage.Unmarshal:
+ if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)
+ || (enableByValueContentsMarshalling && !info.IsByRef && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)))
+ {
+ return nativeTypeMarshaller.GenerateUnmarshalStatements(info, context);
+ }
+ break;
+ case StubCodeContext.Stage.Cleanup:
+ return nativeTypeMarshaller.GenerateCleanupStatements(info, context);
+ default:
+ break;
+ }
+
+ return Array.Empty<StatementSyntax>();
+ }
+
+ public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context)
+ {
+ return enableByValueContentsMarshalling;
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return nativeTypeMarshaller.UsesNativeIdentifier(info, context);
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ICustomNativeTypeMarshallingStrategy.cs
new file mode 100644
index 00000000000..283afaf6066
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/ICustomNativeTypeMarshallingStrategy.cs
@@ -0,0 +1,985 @@
+ο»Ώusing System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+ /// <summary>
+ /// The base interface for implementing various different aspects of the custom native type and collection marshalling specs.
+ /// </summary>
+ interface ICustomNativeTypeMarshallingStrategy
+ {
+ TypeSyntax AsNativeType(TypePositionInfo info);
+
+ ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context);
+
+ IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context);
+
+ IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments);
+
+ IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context);
+
+ IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context);
+
+ IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context);
+
+ IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context);
+
+ bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context);
+ }
+
+ /// <summary>
+ /// Marshalling support for a type that has a custom native type.
+ /// </summary>
+ internal sealed class SimpleCustomNativeTypeMarshalling : ICustomNativeTypeMarshallingStrategy
+ {
+ private readonly TypeSyntax nativeTypeSyntax;
+
+ public SimpleCustomNativeTypeMarshalling(TypeSyntax nativeTypeSyntax)
+ {
+ this.nativeTypeSyntax = nativeTypeSyntax;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ string identifier = context.GetIdentifiers(info).native;
+ if (info.IsByRef)
+ {
+ return Argument(
+ PrefixUnaryExpression(
+ SyntaxKind.AddressOfExpression,
+ IdentifierName(identifier)));
+ }
+
+ return Argument(IdentifierName(identifier));
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return nativeTypeSyntax;
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return true;
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return Array.Empty<StatementSyntax>();
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ // <nativeIdentifier> = new(<arguments>);
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(context.GetIdentifiers(info).native),
+ ImplicitObjectCreationExpression()
+ .WithArgumentList(ArgumentList(SeparatedList(nativeTypeConstructorArguments)))));
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ // If the current element is being marshalled by-value [Out], then don't call the ToManaged method and do the assignment.
+ // The assignment will end up being a no-op and will not be observed.
+ if (!info.IsByRef && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out))
+ {
+ yield break;
+ }
+
+ var (managedIdentifier, nativeIdentifier) = context.GetIdentifiers(info);
+ // <managedIdentifier> = <marshalerIdentifier>.ToManaged();
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(managedIdentifier),
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(nativeIdentifier),
+ IdentifierName(ManualTypeMarshallingHelper.ToManagedMethodName)))));
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ yield return Argument(IdentifierName(context.GetIdentifiers(info).managed));
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return Array.Empty<StatementSyntax>();
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return Array.Empty<StatementSyntax>();
+ }
+ }
+
+ /// <summary>
+ /// A context that redefines the 'native' identifier for a TypePositionInfo to be the marshaller identifier.
+ /// </summary>
+ internal class CustomNativeTypeWithValuePropertyStubContext : StubCodeContext
+ {
+ private readonly StubCodeContext parentContext;
+
+ public CustomNativeTypeWithValuePropertyStubContext(StubCodeContext parentContext)
+ {
+ this.parentContext = parentContext;
+ CurrentStage = parentContext.CurrentStage;
+ }
+
+ public override bool PinningSupported => parentContext.PinningSupported;
+
+ public override bool StackSpaceUsable => parentContext.StackSpaceUsable;
+
+ public override bool CanUseAdditionalTemporaryState => parentContext.CanUseAdditionalTemporaryState;
+
+ public override TypePositionInfo? GetTypePositionInfoForManagedIndex(int index)
+ {
+ return parentContext.GetTypePositionInfoForManagedIndex(index);
+ }
+
+ public override (string managed, string native) GetIdentifiers(TypePositionInfo info)
+ {
+ return (parentContext.GetIdentifiers(info).managed, MarshallerHelpers.GetMarshallerIdentifier(info, parentContext));
+ }
+ }
+
+ /// <summary>
+ /// Marshaller that enables support of a Value property on a native type.
+ /// </summary>
+ internal sealed class CustomNativeTypeWithValuePropertyMarshalling : ICustomNativeTypeMarshallingStrategy
+ {
+ private readonly ICustomNativeTypeMarshallingStrategy innerMarshaller;
+ private readonly TypeSyntax valuePropertyType;
+
+ public CustomNativeTypeWithValuePropertyMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax valuePropertyType)
+ {
+ this.innerMarshaller = innerMarshaller;
+ this.valuePropertyType = valuePropertyType;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ string identifier = context.GetIdentifiers(info).native;
+ if (info.IsByRef)
+ {
+ return Argument(
+ PrefixUnaryExpression(
+ SyntaxKind.AddressOfExpression,
+ IdentifierName(identifier)));
+ }
+
+ return Argument(IdentifierName(identifier));
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return valuePropertyType;
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return true;
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ return innerMarshaller.GenerateCleanupStatements(info, subContext);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ foreach (var statement in innerMarshaller.GenerateMarshalStatements(info, subContext, nativeTypeConstructorArguments))
+ {
+ yield return statement;
+ }
+
+ // <nativeIdentifier> = <marshalerIdentifier>.Value;
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(context.GetIdentifiers(info).native),
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(subContext.GetIdentifiers(info).native),
+ IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName))));
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+
+ // <marshalerIdentifier>.Value = <nativeIdentifier>;
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(subContext.GetIdentifiers(info).native),
+ IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)),
+ IdentifierName(context.GetIdentifiers(info).native)));
+
+ foreach (var statement in innerMarshaller.GenerateUnmarshalStatements(info, subContext))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ return innerMarshaller.GetNativeTypeConstructorArguments(info, subContext);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ yield return LocalDeclarationStatement(
+ VariableDeclaration(
+ innerMarshaller.AsNativeType(info),
+ SingletonSeparatedList(
+ VariableDeclarator(subContext.GetIdentifiers(info).native)
+ .WithInitializer(EqualsValueClause(LiteralExpression(SyntaxKind.DefaultLiteralExpression))))));
+
+ foreach (var statement in innerMarshaller.GenerateSetupStatements(info, subContext))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ return innerMarshaller.GeneratePinStatements(info, subContext);
+ }
+ }
+
+ /// <summary>
+ /// Marshaller that enables support for a stackalloc constructor variant on a native type.
+ /// </summary>
+ internal sealed class StackallocOptimizationMarshalling : ICustomNativeTypeMarshallingStrategy
+ {
+ private readonly ICustomNativeTypeMarshallingStrategy innerMarshaller;
+
+ public StackallocOptimizationMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller)
+ {
+ this.innerMarshaller = innerMarshaller;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return innerMarshaller.AsNativeType(info);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateCleanupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ if (StackAllocOptimizationValid(info, context))
+ {
+ // byte* <managedIdentifier>__stackptr = stackalloc byte[<_nativeLocalType>.StackBufferSize];
+ yield return LocalDeclarationStatement(
+ VariableDeclaration(
+ PointerType(PredefinedType(Token(SyntaxKind.ByteKeyword))),
+ SingletonSeparatedList(
+ VariableDeclarator(GetStackAllocPointerIdentifier(info, context))
+ .WithInitializer(EqualsValueClause(
+ StackAllocArrayCreationExpression(
+ ArrayType(
+ PredefinedType(Token(SyntaxKind.ByteKeyword)),
+ SingletonList(ArrayRankSpecifier(SingletonSeparatedList<ExpressionSyntax>(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ AsNativeType(info),
+ IdentifierName(ManualTypeMarshallingHelper.StackBufferSizeFieldName))
+ ))))))))));
+ }
+
+ foreach (var statement in innerMarshaller.GenerateMarshalStatements(info, context, nativeTypeConstructorArguments))
+ {
+ yield return statement;
+ }
+ }
+
+ private static bool StackAllocOptimizationValid(TypePositionInfo info, StubCodeContext context)
+ {
+ return context.StackSpaceUsable && context.PinningSupported && (!info.IsByRef || info.RefKind == RefKind.In);
+ }
+
+ private static string GetStackAllocPointerIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return $"{context.GetIdentifiers(info).managed}__stackptr";
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GeneratePinStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateSetupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateUnmarshalStatements(info, context);
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ foreach (var arg in innerMarshaller.GetNativeTypeConstructorArguments(info, context))
+ {
+ yield return arg;
+ }
+ if (StackAllocOptimizationValid(info, context))
+ {
+ yield return Argument(
+ ObjectCreationExpression(
+ GenericName(Identifier(TypeNames.System_Span),
+ TypeArgumentList(SingletonSeparatedList<TypeSyntax>(
+ PredefinedType(Token(SyntaxKind.ByteKeyword))))))
+ .WithArgumentList(
+ ArgumentList(SeparatedList(new ArgumentSyntax[]
+ {
+ Argument(IdentifierName(GetStackAllocPointerIdentifier(info, context))),
+ Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ AsNativeType(info),
+ IdentifierName(ManualTypeMarshallingHelper.StackBufferSizeFieldName)))
+ }))));
+ }
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.UsesNativeIdentifier(info, context);
+ }
+ }
+
+ /// <summary>
+ /// Marshaller that enables support for a FreeNative method on a native type.
+ /// </summary>
+ internal sealed class FreeNativeCleanupStrategy : ICustomNativeTypeMarshallingStrategy
+ {
+ private readonly ICustomNativeTypeMarshallingStrategy innerMarshaller;
+
+ public FreeNativeCleanupStrategy(ICustomNativeTypeMarshallingStrategy innerMarshaller)
+ {
+ this.innerMarshaller = innerMarshaller;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return innerMarshaller.AsNativeType(info);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ foreach (var statement in innerMarshaller.GenerateCleanupStatements(info, context))
+ {
+ yield return statement;
+ }
+
+ // <nativeIdentifier>.FreeNative();
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(context.GetIdentifiers(info).native),
+ IdentifierName(ManualTypeMarshallingHelper.FreeNativeMethodName))));
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ return innerMarshaller.GenerateMarshalStatements(info, context, nativeTypeConstructorArguments);
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GeneratePinStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateSetupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateUnmarshalStatements(info, context);
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GetNativeTypeConstructorArguments(info, context);
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.UsesNativeIdentifier(info, context);
+ }
+ }
+
+ /// <summary>
+ /// Marshaller that enables support for a GetPinnableReference method on a native type, with a Value property fallback.
+ /// </summary>
+ internal sealed class PinnableMarshallerTypeMarshalling : ICustomNativeTypeMarshallingStrategy
+ {
+ private readonly ICustomNativeTypeMarshallingStrategy innerMarshaller;
+ private readonly TypeSyntax valuePropertyType;
+
+ public PinnableMarshallerTypeMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax valuePropertyType)
+ {
+ this.innerMarshaller = innerMarshaller;
+ this.valuePropertyType = valuePropertyType;
+ }
+
+ private bool CanPinMarshaller(TypePositionInfo info, StubCodeContext context)
+ {
+ return context.PinningSupported && !info.IsManagedReturnPosition && !info.IsByRef || info.RefKind == RefKind.In;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return valuePropertyType;
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ return innerMarshaller.GenerateCleanupStatements(info, subContext);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ foreach (var statement in innerMarshaller.GenerateMarshalStatements(info, subContext, nativeTypeConstructorArguments))
+ {
+ yield return statement;
+ }
+
+ if (!CanPinMarshaller(info, context))
+ {
+ // <nativeIdentifier> = <marshalerIdentifier>.Value;
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(context.GetIdentifiers(info).native),
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(subContext.GetIdentifiers(info).native),
+ IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName))));
+ }
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ // fixed (<_nativeTypeSyntax> <nativeIdentifier> = &<marshalerIdentifier>)
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ yield return FixedStatement(
+ VariableDeclaration(
+ valuePropertyType,
+ SingletonSeparatedList(
+ VariableDeclarator(context.GetIdentifiers(info).native)
+ .WithInitializer(EqualsValueClause(
+ PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(subContext.GetIdentifiers(info).native),
+ IdentifierName(ManualTypeMarshallingHelper.GetPinnableReferenceName)),
+ ArgumentList())))))),
+ EmptyStatement());
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+ yield return LocalDeclarationStatement(
+ VariableDeclaration(
+ innerMarshaller.AsNativeType(info),
+ SingletonSeparatedList(
+ VariableDeclarator(subContext.GetIdentifiers(info).native)
+ .WithInitializer(EqualsValueClause(LiteralExpression(SyntaxKind.DefaultLiteralExpression))))));
+
+ foreach (var statement in innerMarshaller.GenerateSetupStatements(info, subContext))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ var subContext = new CustomNativeTypeWithValuePropertyStubContext(context);
+
+ if (!CanPinMarshaller(info, context))
+ {
+ // <marshalerIdentifier>.Value = <nativeIdentifier>;
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(subContext.GetIdentifiers(info).native),
+ IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)),
+ IdentifierName(context.GetIdentifiers(info).native)));
+ }
+
+ foreach (var statement in innerMarshaller.GenerateUnmarshalStatements(info, subContext))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GetNativeTypeConstructorArguments(info, context);
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ if (CanPinMarshaller(info, context))
+ {
+ return false;
+ }
+ return innerMarshaller.UsesNativeIdentifier(info, context);
+ }
+ }
+
+ /// <summary>
+ /// Marshaller that enables support for native types with the constructor variants that take a sizeOfElement int parameter and that have a SetUnmarshalledCollectionLength method.
+ /// </summary>
+ internal sealed class NumElementsExpressionMarshalling : ICustomNativeTypeMarshallingStrategy
+ {
+ private readonly ICustomNativeTypeMarshallingStrategy innerMarshaller;
+ private readonly ExpressionSyntax numElementsExpression;
+ private readonly ExpressionSyntax sizeOfElementExpression;
+
+ public NumElementsExpressionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, ExpressionSyntax numElementsExpression, ExpressionSyntax sizeOfElementExpression)
+ {
+ this.innerMarshaller = innerMarshaller;
+ this.numElementsExpression = numElementsExpression;
+ this.sizeOfElementExpression = sizeOfElementExpression;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return innerMarshaller.AsNativeType(info);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateCleanupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ return innerMarshaller.GenerateMarshalStatements(info, context, nativeTypeConstructorArguments);
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GeneratePinStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateSetupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ string marshalerIdentifier = MarshallerHelpers.GetMarshallerIdentifier(info, context);
+ if (info.RefKind == RefKind.Out || info.IsManagedReturnPosition)
+ {
+ yield return ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(marshalerIdentifier),
+ ImplicitObjectCreationExpression().AddArgumentListArguments(Argument(sizeOfElementExpression))));
+ }
+
+ if (info.IsByRef || !info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out))
+ {
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(marshalerIdentifier),
+ IdentifierName(ManualTypeMarshallingHelper.SetUnmarshalledCollectionLengthMethodName)))
+ .AddArgumentListArguments(Argument(numElementsExpression)));
+ }
+
+ foreach (var statement in innerMarshaller.GenerateUnmarshalStatements(info, context))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ foreach (var arg in innerMarshaller.GetNativeTypeConstructorArguments(info, context))
+ {
+ yield return arg;
+ }
+ yield return Argument(sizeOfElementExpression);
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.UsesNativeIdentifier(info, context);
+ }
+ }
+
+ /// <summary>
+ /// Marshaller that enables support for marshalling blittable elements of a contiguous collection via a native type that implements the contiguous collection marshalling spec.
+ /// </summary>
+ internal sealed class ContiguousBlittableElementCollectionMarshalling : ICustomNativeTypeMarshallingStrategy
+ {
+ private readonly ICustomNativeTypeMarshallingStrategy innerMarshaller;
+ private readonly TypeSyntax elementType;
+
+ public ContiguousBlittableElementCollectionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType)
+ {
+ this.innerMarshaller = innerMarshaller;
+ this.elementType = elementType;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return innerMarshaller.AsNativeType(info);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateCleanupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ string nativeIdentifier = context.GetIdentifiers(info).native;
+ foreach (var statement in innerMarshaller.GenerateMarshalStatements(info, context, nativeTypeConstructorArguments))
+ {
+ yield return statement;
+ }
+
+ 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.
+ yield break;
+ }
+
+ // <nativeIdentifier>.ManagedValues.CopyTo(MemoryMarshal.Cast<byte, <elementType>>(<nativeIdentifier>.NativeValueStorage));
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(nativeIdentifier),
+ IdentifierName(ManualTypeMarshallingHelper.ManagedValuesPropertyName)),
+ IdentifierName("CopyTo")))
+ .AddArgumentListArguments(
+ Argument(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ GenericName(
+ Identifier("Cast"))
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SeparatedList(
+ new[]
+ {
+ PredefinedType(Token(SyntaxKind.ByteKeyword)),
+ elementType
+ })))))
+ .AddArgumentListArguments(
+ Argument(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(nativeIdentifier),
+ IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName)))))));
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GeneratePinStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateSetupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ string nativeIdentifier = context.GetIdentifiers(info).native;
+ // MemoryMarshal.Cast<byte, <elementType>>(<nativeIdentifier>.NativeValueStorage).CopyTo(<nativeIdentifier>.ManagedValues);
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ GenericName(
+ Identifier("Cast"))
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SeparatedList(
+ new[]
+ {
+ PredefinedType(Token(SyntaxKind.ByteKeyword)),
+ elementType
+ })))))
+ .AddArgumentListArguments(
+ Argument(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(nativeIdentifier),
+ IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName)))),
+ IdentifierName("CopyTo")))
+ .AddArgumentListArguments(
+ Argument(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(nativeIdentifier),
+ IdentifierName(ManualTypeMarshallingHelper.ManagedValuesPropertyName)))));
+
+ foreach (var statement in innerMarshaller.GenerateUnmarshalStatements(info, context))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GetNativeTypeConstructorArguments(info, context);
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.UsesNativeIdentifier(info, context);
+ }
+ }
+
+ /// <summary>
+ /// Marshaller that enables support for marshalling non-blittable elements of a contiguous collection via a native type that implements the contiguous collection marshalling spec.
+ /// </summary>
+ internal sealed class ContiguousNonBlittableElementCollectionMarshalling : ICustomNativeTypeMarshallingStrategy
+ {
+ private const string IndexerIdentifier = "__i";
+
+ private readonly ICustomNativeTypeMarshallingStrategy innerMarshaller;
+ private readonly IMarshallingGenerator elementMarshaller;
+ private readonly TypePositionInfo elementInfo;
+
+ public ContiguousNonBlittableElementCollectionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller,
+ IMarshallingGenerator elementMarshaller,
+ TypePositionInfo elementInfo)
+ {
+ this.innerMarshaller = innerMarshaller;
+ this.elementMarshaller = elementMarshaller;
+ this.elementInfo = elementInfo;
+ }
+
+ private string GetNativeSpanIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return context.GetIdentifiers(info).managed + "__nativeSpan";
+ }
+
+ private LocalDeclarationStatementSyntax GenerateNativeSpanDeclaration(TypePositionInfo info, StubCodeContext context)
+ {
+ string nativeIdentifier = context.GetIdentifiers(info).native;
+ string nativeSpanIdentifier = GetNativeSpanIdentifier(info, context);
+ return LocalDeclarationStatement(VariableDeclaration(
+ GenericName(
+ Identifier(TypeNames.System_Span),
+ TypeArgumentList(
+ SingletonSeparatedList(elementMarshaller.AsNativeType(elementInfo).GetCompatibleGenericTypeParameterSyntax()))
+ ),
+ SingletonSeparatedList(
+ VariableDeclarator(Identifier(nativeSpanIdentifier))
+ .WithInitializer(EqualsValueClause(
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
+ GenericName(
+ Identifier("Cast"))
+ .WithTypeArgumentList(
+ TypeArgumentList(
+ SeparatedList(
+ new[]
+ {
+ PredefinedType(Token(SyntaxKind.ByteKeyword)),
+ elementMarshaller.AsNativeType(elementInfo).GetCompatibleGenericTypeParameterSyntax()
+ })))))
+ .AddArgumentListArguments(
+ Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(nativeIdentifier),
+ IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName)))))))));
+ }
+
+ private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo info, StubCodeContext context, bool useManagedSpanForLength)
+ {
+ string nativeIdentifier = context.GetIdentifiers(info).native;
+ string nativeSpanIdentifier = GetNativeSpanIdentifier(info, context);
+ var elementSubContext = new ContiguousCollectionElementMarshallingCodeContext(
+ context.CurrentStage,
+ IndexerIdentifier,
+ nativeSpanIdentifier,
+ context);
+
+ string collectionIdentifierForLength = useManagedSpanForLength
+ ? $"{nativeIdentifier}.{ManualTypeMarshallingHelper.ManagedValuesPropertyName}"
+ : nativeSpanIdentifier;
+
+ TypePositionInfo localElementInfo = elementInfo with
+ {
+ InstanceIdentifier = info.InstanceIdentifier,
+ RefKind = info.IsByRef ? info.RefKind : info.ByValueContentsMarshalKind.GetRefKindForByValueContentsKind(),
+ ManagedIndex = info.ManagedIndex,
+ NativeIndex = info.NativeIndex
+ };
+
+ StatementSyntax marshallingStatement = Block(
+ List(elementMarshaller.Generate(
+ localElementInfo,
+ elementSubContext)));
+
+ if (elementMarshaller.AsNativeType(elementInfo) is PointerTypeSyntax)
+ {
+ PointerNativeTypeAssignmentRewriter rewriter = new(elementSubContext.GetIdentifiers(localElementInfo).native);
+ marshallingStatement = (StatementSyntax)rewriter.Visit(marshallingStatement);
+ }
+
+ // Iterate through the elements of the native collection to unmarshal them
+ return Block(
+ GenerateNativeSpanDeclaration(info, context),
+ MarshallerHelpers.GetForLoop(collectionIdentifierForLength, IndexerIdentifier)
+ .WithStatement(marshallingStatement));
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return innerMarshaller.AsNativeType(info);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: false);
+ foreach (var statement in innerMarshaller.GenerateCleanupStatements(info, context))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable<ArgumentSyntax> nativeTypeConstructorArguments)
+ {
+ foreach (var statement in innerMarshaller.GenerateMarshalStatements(info, context, nativeTypeConstructorArguments))
+ {
+ yield return statement;
+ }
+
+ 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.
+ yield break;
+ }
+
+ yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: true);
+ }
+
+ public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GeneratePinStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GenerateSetupStatements(info, context);
+ }
+
+ public IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
+ {
+ yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: false);
+ foreach (var statement in innerMarshaller.GenerateUnmarshalStatements(info, context))
+ {
+ yield return statement;
+ }
+ }
+
+ public IEnumerable<ArgumentSyntax> GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.GetNativeTypeConstructorArguments(info, context);
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ return innerMarshaller.UsesNativeIdentifier(info, context);
+ }
+
+ /// <summary>
+ /// Rewrite assignment expressions to the native identifier to cast to IntPtr.
+ /// This handles the case where the native type of a non-blittable managed type is a pointer,
+ /// which are unsupported in generic type parameters.
+ /// </summary>
+ private class PointerNativeTypeAssignmentRewriter : CSharpSyntaxRewriter
+ {
+ private readonly string nativeIdentifier;
+
+ public PointerNativeTypeAssignmentRewriter(string nativeIdentifier)
+ {
+ this.nativeIdentifier = nativeIdentifier;
+ }
+
+ public override SyntaxNode VisitAssignmentExpression(AssignmentExpressionSyntax node)
+ {
+ if (node.Left.ToString() == nativeIdentifier)
+ {
+ return node.WithRight(
+ CastExpression(MarshallerHelpers.SystemIntPtrType, node.Right));
+ }
+
+ return node;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallerHelpers.cs
index fd2b8c6200b..e0529fe55b0 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallerHelpers.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallerHelpers.cs
@@ -15,6 +15,8 @@ namespace Microsoft.Interop
public static readonly TypeSyntax InteropServicesMarshalType = ParseTypeName(TypeNames.System_Runtime_InteropServices_Marshal);
+ public static readonly TypeSyntax SystemIntPtrType = ParseTypeName("System.IntPtr");
+
public static ForStatementSyntax GetForLoop(string collectionIdentifier, string indexerIdentifier)
{
// for(int <indexerIdentifier> = 0; <indexerIdentifier> < <collectionIdentifier>.Length; ++<indexerIdentifier>)
@@ -73,6 +75,24 @@ namespace Microsoft.Interop
};
}
+ public static TypeSyntax GetCompatibleGenericTypeParameterSyntax(this TypeSyntax type)
+ {
+ TypeSyntax spanElementTypeSyntax = type;
+ if (spanElementTypeSyntax is PointerTypeSyntax)
+ {
+ // Pointers cannot be passed to generics, so use IntPtr for this case.
+ spanElementTypeSyntax = SystemIntPtrType;
+ }
+ return spanElementTypeSyntax;
+ }
+
+ private const string MarshalerLocalSuffix = "__marshaler";
+ public static string GetMarshallerIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ var (_, nativeIdentifier) = context.GetIdentifiers(info);
+ return nativeIdentifier + MarshalerLocalSuffix;
+ }
+
public static class StringMarshaller
{
public static ExpressionSyntax AllocationExpression(CharEncoding encoding, string managedIdentifier)
@@ -111,7 +131,7 @@ namespace Microsoft.Interop
ArgumentList(SingletonSeparatedList(
Argument(
CastExpression(
- ParseTypeName("System.IntPtr"),
+ SystemIntPtrType,
IdentifierName(nativeIdentifier))))));
}
}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallingGenerator.cs
index c8f13da707c..f406e2c3e6d 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallingGenerator.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/MarshallingGenerator.cs
@@ -250,7 +250,7 @@ namespace Microsoft.Interop
// Must go before the cases that do not explicitly check for marshalling info to support
// the user overridding the default marshalling rules with a MarshalUsing attribute.
case { MarshallingAttributeInfo: NativeMarshallingAttributeInfo marshalInfo }:
- return CreateCustomNativeTypeMarshaller(info, context, marshalInfo);
+ return CreateCustomNativeTypeMarshaller(info, context, marshalInfo, options);
case { MarshallingAttributeInfo: BlittableTypeAttributeInfo }:
return Blittable;
@@ -266,9 +266,6 @@ namespace Microsoft.Interop
case { ManagedType: { SpecialType: SpecialType.System_String } }:
return CreateStringMarshaller(info, context);
-
- case { ManagedType: IArrayTypeSymbol { IsSZArray: true, ElementType: ITypeSymbol elementType } }:
- return CreateArrayMarshaller(info, context, options, elementType);
case { ManagedType: { SpecialType: SpecialType.System_Void } }:
return Forwarder;
@@ -366,32 +363,36 @@ namespace Microsoft.Interop
throw new MarshallingNotSupportedException(info, context);
}
- private static ExpressionSyntax GetNumElementsExpressionFromMarshallingInfo(TypePositionInfo info, StubCodeContext context, AnalyzerConfigOptions options)
+ private static ExpressionSyntax GetNumElementsExpressionFromMarshallingInfo(TypePositionInfo info, CountInfo count, StubCodeContext context, AnalyzerConfigOptions options)
{
- ExpressionSyntax numElementsExpression;
- if (info.MarshallingAttributeInfo is not ArrayMarshalAsInfo marshalAsInfo)
+ return count switch
{
- throw new MarshallingNotSupportedException(info, context)
+ SizeAndParamIndexInfo(int size, SizeAndParamIndexInfo.UnspecifiedData) => GetConstSizeExpression(size),
+ ConstSizeCountInfo(int size) => GetConstSizeExpression(size),
+ SizeAndParamIndexInfo(SizeAndParamIndexInfo.UnspecifiedData, int paramIndex) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(context.GetTypePositionInfoForManagedIndex(paramIndex))),
+ SizeAndParamIndexInfo(int size, int paramIndex) => CheckedExpression(SyntaxKind.CheckedExpression, BinaryExpression(SyntaxKind.AddExpression, GetConstSizeExpression(size), GetExpressionForParam(context.GetTypePositionInfoForManagedIndex(paramIndex)))),
+ CountElementCountInfo(TypePositionInfo elementInfo) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(elementInfo)),
+ _ => throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = Resources.ArraySizeMustBeSpecified
- };
+ },
+ };
+
+ static LiteralExpressionSyntax GetConstSizeExpression(int size)
+ {
+ return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(size));
}
- LiteralExpressionSyntax? constSizeExpression = marshalAsInfo.ArraySizeConst != ArrayMarshalAsInfo.UnspecifiedData
- ? LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(marshalAsInfo.ArraySizeConst))
- : null;
- ExpressionSyntax? sizeParamIndexExpression = null;
- if (marshalAsInfo.ArraySizeParamIndex != ArrayMarshalAsInfo.UnspecifiedData)
+ ExpressionSyntax GetExpressionForParam(TypePositionInfo? paramInfo)
{
- TypePositionInfo? paramIndexInfo = context.GetTypePositionInfoForManagedIndex(marshalAsInfo.ArraySizeParamIndex);
- if (paramIndexInfo is null)
+ if (paramInfo is null)
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = Resources.ArraySizeParamIndexOutOfRange
};
}
- else if (!paramIndexInfo.ManagedType.IsIntegralType())
+ else if (!paramInfo.ManagedType.IsIntegralType())
{
throw new MarshallingNotSupportedException(info, context)
{
@@ -400,53 +401,53 @@ namespace Microsoft.Interop
}
else
{
- var (managed, native) = context.GetIdentifiers(paramIndexInfo);
- string identifier = Create(paramIndexInfo, context, options).UsesNativeIdentifier(paramIndexInfo, context) ? native : managed;
- sizeParamIndexExpression = CastExpression(
+ var (managed, native) = context.GetIdentifiers(paramInfo);
+ string identifier = Create(paramInfo, context, options).UsesNativeIdentifier(paramInfo, context) ? native : managed;
+ return CastExpression(
PredefinedType(Token(SyntaxKind.IntKeyword)),
IdentifierName(identifier));
}
}
- numElementsExpression = (constSizeExpression, sizeParamIndexExpression) switch
- {
- (null, null) => throw new MarshallingNotSupportedException(info, context)
- {
- NotSupportedDetails = Resources.ArraySizeMustBeSpecified
- },
- (not null, null) => constSizeExpression!,
- (null, not null) => CheckedExpression(SyntaxKind.CheckedExpression, sizeParamIndexExpression!),
- (not null, not null) => CheckedExpression(SyntaxKind.CheckedExpression, BinaryExpression(SyntaxKind.AddExpression, constSizeExpression!, sizeParamIndexExpression!))
- };
- return numElementsExpression;
}
- private static IMarshallingGenerator CreateArrayMarshaller(TypePositionInfo info, StubCodeContext context, AnalyzerConfigOptions options, ITypeSymbol elementType)
+ private static IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo, AnalyzerConfigOptions options)
{
- var elementMarshallingInfo = info.MarshallingAttributeInfo switch
+ ValidateCustomNativeTypeMarshallingSupported(info, context, marshalInfo);
+
+ ICustomNativeTypeMarshallingStrategy marshallingStrategy = new SimpleCustomNativeTypeMarshalling(marshalInfo.NativeMarshallingType.AsTypeSyntax());
+
+ if ((marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNativeStackalloc) != 0)
{
- ArrayMarshalAsInfo(UnmanagedType.LPArray, _) marshalAs => marshalAs.ElementMarshallingInfo,
- ArrayMarshallingInfo marshalInfo => marshalInfo.ElementMarshallingInfo,
- NoMarshallingInfo _ => NoMarshallingInfo.Instance,
- _ => throw new MarshallingNotSupportedException(info, context)
- };
+ marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy);
+ }
- var elementMarshaller = Create(
- TypePositionInfo.CreateForType(elementType, elementMarshallingInfo),
- new ArrayMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context, false),
- options);
- ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0));
- if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
+ if (ManualTypeMarshallingHelper.HasFreeNativeMethod(marshalInfo.NativeMarshallingType))
{
- // In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here.
- numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, context, options);
+ marshallingStrategy = new FreeNativeCleanupStrategy(marshallingStrategy);
}
-
- return elementMarshaller == Blittable
- ? new BlittableArrayMarshaller(numElementsExpression)
- : new NonBlittableArrayMarshaller(elementMarshaller, numElementsExpression);
+
+ // Collections have extra configuration, so handle them here.
+ if (marshalInfo is NativeContiguousCollectionMarshallingInfo collectionMarshallingInfo)
+ {
+ return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, options, marshallingStrategy);
+ }
+
+ if (marshalInfo.ValuePropertyType is not null)
+ {
+ marshallingStrategy = DecorateWithValuePropertyStrategy(marshalInfo, marshallingStrategy);
+ }
+
+ IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false);
+
+ if ((marshalInfo.MarshallingMethods & SupportedMarshallingMethods.Pinning) != 0)
+ {
+ return new PinnableManagedValueMarshaller(marshallingGenerator);
+ }
+
+ return marshallingGenerator;
}
- private static IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo)
+ private static void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo)
{
if (marshalInfo.ValuePropertyType is not null && !context.CanUseAdditionalTemporaryState)
{
@@ -458,7 +459,7 @@ namespace Microsoft.Interop
// The marshalling method for this type doesn't support marshalling from native to managed,
// but our scenario requires marshalling from native to managed.
- if ((info.RefKind == RefKind.Ref || info.RefKind == RefKind.Out || info.IsManagedReturnPosition)
+ if ((info.RefKind == RefKind.Ref || info.RefKind == RefKind.Out || info.IsManagedReturnPosition)
&& (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.NativeToManaged) == 0)
{
throw new MarshallingNotSupportedException(info, context)
@@ -471,9 +472,9 @@ namespace Microsoft.Interop
// Pinning is required for the stackalloc marshalling to enable users to safely pass the stackalloc Span's byref
// to native if we ever start using a conditional stackalloc method and cannot guarantee that the Span we provide
// the user with is backed by stack allocated memory.
- else if (!info.IsByRef
- && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNative) == 0
- && !(context.PinningSupported && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.Pinning) == 0)
+ else if (!info.IsByRef
+ && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNative) == 0
+ && !(context.PinningSupported && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.Pinning) == 0)
&& !(context.StackSpaceUsable && context.PinningSupported && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNativeStackalloc) == 0))
{
throw new MarshallingNotSupportedException(info, context)
@@ -484,8 +485,8 @@ namespace Microsoft.Interop
// The marshalling method for this type doesn't support marshalling from managed to native by reference,
// but our scenario requires marshalling from managed to native by reference.
// "in" byref supports stack marshalling.
- else if (info.RefKind == RefKind.In
- && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNative) == 0
+ else if (info.RefKind == RefKind.In
+ && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNative) == 0
&& !(context.StackSpaceUsable && context.PinningSupported && (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNativeStackalloc) != 0))
{
throw new MarshallingNotSupportedException(info, context)
@@ -496,7 +497,7 @@ namespace Microsoft.Interop
// The marshalling method for this type doesn't support marshalling from managed to native by reference,
// but our scenario requires marshalling from managed to native by reference.
// "ref" byref marshalling doesn't support stack marshalling
- else if (info.RefKind == RefKind.Ref
+ else if (info.RefKind == RefKind.Ref
&& (marshalInfo.MarshallingMethods & SupportedMarshallingMethods.ManagedToNative) == 0)
{
throw new MarshallingNotSupportedException(info, context)
@@ -504,8 +505,78 @@ namespace Microsoft.Interop
NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.ToDisplayString())
};
}
-
- return new CustomNativeTypeMarshaller(marshalInfo);
+ }
+
+ private static ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller)
+ {
+ TypeSyntax valuePropertyTypeSyntax = marshalInfo.ValuePropertyType!.AsTypeSyntax();
+ if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshalInfo.NativeMarshallingType) is not null)
+ {
+ return new PinnableMarshallerTypeMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax);
+ }
+
+ return new CustomNativeTypeWithValuePropertyMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax);
+ }
+
+ private static IMarshallingGenerator CreateNativeCollectionMarshaller(
+ TypePositionInfo info,
+ StubCodeContext context,
+ NativeContiguousCollectionMarshallingInfo collectionInfo,
+ AnalyzerConfigOptions options,
+ ICustomNativeTypeMarshallingStrategy marshallingStrategy)
+ {
+ var elementInfo = TypePositionInfo.CreateForType(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo);
+ var elementMarshaller = Create(
+ elementInfo,
+ new ContiguousCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, string.Empty, context),
+ options);
+ var elementType = elementMarshaller.AsNativeType(elementInfo);
+
+ bool isBlittable = elementMarshaller == Blittable;
+
+ if (isBlittable)
+ {
+ marshallingStrategy = new ContiguousBlittableElementCollectionMarshalling(marshallingStrategy, collectionInfo.ElementType.AsTypeSyntax());
+ }
+ else
+ {
+ marshallingStrategy = new ContiguousNonBlittableElementCollectionMarshalling(marshallingStrategy, elementMarshaller, elementInfo);
+ }
+
+ // Explicitly insert the Value property handling here (before numElements handling) so that the numElements handling will be emitted before the Value property handling in unmarshalling.
+ if (collectionInfo.ValuePropertyType is not null)
+ {
+ marshallingStrategy = DecorateWithValuePropertyStrategy(collectionInfo, marshallingStrategy);
+ }
+
+ ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0));
+ if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
+ {
+ // In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here.
+ numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, collectionInfo.ElementCountInfo, context, options);
+ }
+
+ marshallingStrategy = new NumElementsExpressionMarshalling(
+ marshallingStrategy,
+ numElementsExpression,
+ SizeOfExpression(elementType));
+
+ if (collectionInfo.UseDefaultMarshalling && info.ManagedType is IArrayTypeSymbol { IsSZArray: true })
+ {
+ return new ArrayMarshaller(
+ new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: true),
+ elementType,
+ isBlittable);
+ }
+
+ IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false);
+
+ if ((collectionInfo.MarshallingMethods & SupportedMarshallingMethods.Pinning) != 0)
+ {
+ return new PinnableManagedValueMarshaller(marshallingGenerator);
+ }
+
+ return marshallingGenerator;
}
}
}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs
deleted file mode 100644
index 40d793c7310..00000000000
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs
+++ /dev/null
@@ -1,317 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-
-namespace Microsoft.Interop
-{
- internal class NonBlittableArrayMarshaller : ConditionalStackallocMarshallingGenerator
- {
- /// <summary>
- /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
- /// Number kept small to ensure that P/Invokes with a lot of small array parameters doesn't
- /// blow the stack since this is a new optimization in the code-generated interop.
- /// </summary>
- private const int StackAllocBytesThreshold = 0x200;
-
- private const string IndexerIdentifier = "__i";
-
- private readonly IMarshallingGenerator _elementMarshaller;
- private readonly ExpressionSyntax _numElementsExpr;
-
- public NonBlittableArrayMarshaller(IMarshallingGenerator elementMarshaller, ExpressionSyntax numElementsExpr)
- {
- _elementMarshaller = elementMarshaller;
- _numElementsExpr = numElementsExpr;
- }
-
- private ITypeSymbol GetElementTypeSymbol(TypePositionInfo info)
- {
- return ((IArrayTypeSymbol)info.ManagedType).ElementType;
- }
-
- private TypeSyntax GetNativeElementTypeSyntax(TypePositionInfo info)
- {
- return _elementMarshaller.AsNativeType(TypePositionInfo.CreateForType(GetElementTypeSymbol(info), NoMarshallingInfo.Instance));
- }
-
- public override TypeSyntax AsNativeType(TypePositionInfo info)
- {
- return PointerType(GetNativeElementTypeSyntax(info));
- }
-
- public override ParameterSyntax AsParameter(TypePositionInfo info)
- {
- var type = info.IsByRef
- ? PointerType(AsNativeType(info))
- : AsNativeType(info);
- return Parameter(Identifier(info.InstanceIdentifier))
- .WithType(type);
- }
-
- public override ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
- {
- return info.IsByRef
- ? Argument(
- PrefixUnaryExpression(
- SyntaxKind.AddressOfExpression,
- IdentifierName(context.GetIdentifiers(info).native)))
- : Argument(IdentifierName(context.GetIdentifiers(info).native));
- }
-
- public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
- {
- var (managedIdentifer, nativeIdentifier) = context.GetIdentifiers(info);
- RefKind elementRefKind = info.IsByRef ? info.RefKind : info.ByValueContentsMarshalKind.GetRefKindForByValueContentsKind();
- bool cacheManagedValue = ShouldCacheManagedValue(info, context);
- string managedLocal = !cacheManagedValue ? managedIdentifer : managedIdentifer + ArrayMarshallingCodeContext.LocalManagedIdentifierSuffix;
-
- switch (context.CurrentStage)
- {
- case StubCodeContext.Stage.Setup:
- if (TryGenerateSetupSyntax(info, context, out StatementSyntax conditionalAllocSetup))
- yield return conditionalAllocSetup;
-
- if (cacheManagedValue)
- {
- yield return LocalDeclarationStatement(
- VariableDeclaration(
- info.ManagedType.AsTypeSyntax(),
- SingletonSeparatedList(
- VariableDeclarator(managedLocal)
- .WithInitializer(EqualsValueClause(
- IdentifierName(managedIdentifer))))));
- }
- break;
- case StubCodeContext.Stage.Marshal:
- if (info.RefKind != RefKind.Out)
- {
- foreach (var statement in GenerateConditionalAllocationSyntax(
- info,
- context,
- StackAllocBytesThreshold))
- {
- yield return statement;
- }
-
- var arraySubContext = new ArrayMarshallingCodeContext(context.CurrentStage, IndexerIdentifier, context, appendLocalManagedIdentifierSuffix: cacheManagedValue);
-
- TypeSyntax spanElementTypeSyntax = GetNativeElementTypeSyntax(info);
- if (spanElementTypeSyntax is PointerTypeSyntax)
- {
- // Pointers cannot be passed to generics, so use IntPtr for this case.
- spanElementTypeSyntax = ParseTypeName("System.IntPtr");
- }
-
- if (info is { IsByRef: false, ByValueContentsMarshalKind: ByValueContentsMarshalKind.Out })
- {
- // We don't marshal values from managed to native for [Out] by value arrays,
- // we only allocate the buffer.
- yield break;
- }
-
- // Iterate through the elements of the array to marshal them
- yield return IfStatement(BinaryExpression(SyntaxKind.NotEqualsExpression,
- IdentifierName(managedLocal),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
- Block(
- // new Span<T>(<nativeIdentifier>, <managedIdentifier>.Length).Clear();
- ExpressionStatement(
- InvocationExpression(
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- ObjectCreationExpression(
- GenericName(TypeNames.System_Span)
- .WithTypeArgumentList(
- TypeArgumentList(
- SingletonSeparatedList(spanElementTypeSyntax))))
- .WithArgumentList(
- ArgumentList(
- SeparatedList(
- new []{
- Argument(
- CastExpression(
- PointerType(spanElementTypeSyntax),
- IdentifierName(nativeIdentifier))),
- Argument(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(managedIdentifer),
- IdentifierName("Length")))
- }))),
- IdentifierName("Clear")),
- ArgumentList())),
- MarshallerHelpers.GetForLoop(managedLocal, IndexerIdentifier)
- .WithStatement(Block(
- List(_elementMarshaller.Generate(
- info with { ManagedType = GetElementTypeSymbol(info), RefKind = elementRefKind },
- arraySubContext))))));
- }
- break;
- case StubCodeContext.Stage.Unmarshal:
- if (info.IsManagedReturnPosition
- || (info.IsByRef && info.RefKind != RefKind.In)
- || info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out))
- {
- var arraySubContext = new ArrayMarshallingCodeContext(context.CurrentStage, IndexerIdentifier, context, appendLocalManagedIdentifierSuffix: cacheManagedValue);
- // Iterate through the elements of the native array to unmarshal them
- StatementSyntax unmarshalContentsStatement =
- MarshallerHelpers.GetForLoop(managedLocal, IndexerIdentifier)
- .WithStatement(Block(
- List(_elementMarshaller.Generate(
- info with { ManagedType = GetElementTypeSymbol(info), RefKind = elementRefKind },
- arraySubContext))));
-
- if (!info.IsByRef)
- {
- if (info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out))
- {
- yield return IfStatement(
- BinaryExpression(SyntaxKind.NotEqualsExpression,
- IdentifierName(managedLocal),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
- unmarshalContentsStatement);
-
- if (cacheManagedValue)
- {
- yield return ExpressionStatement(
- AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(managedIdentifer),
- IdentifierName(managedLocal))
- );
- }
- yield break;
- }
- }
-
- yield return IfStatement(
- BinaryExpression(SyntaxKind.NotEqualsExpression,
- IdentifierName(nativeIdentifier),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
- Block(
- // <managedLocal> = new <managedElementType>[<numElementsExpression>];
- ExpressionStatement(
- AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(managedLocal),
- ArrayCreationExpression(
- ArrayType(GetElementTypeSymbol(info).AsTypeSyntax(),
- SingletonList(ArrayRankSpecifier(
- SingletonSeparatedList(_numElementsExpr))))))),
- unmarshalContentsStatement
- ),
- ElseClause(
- ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(managedLocal),
- LiteralExpression(SyntaxKind.NullLiteralExpression)))));
-
- if (cacheManagedValue)
- {
- yield return ExpressionStatement(
- AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(managedIdentifer),
- IdentifierName(managedLocal))
- );
- }
- }
- break;
- case StubCodeContext.Stage.Cleanup:
- {
- var arraySubContext = new ArrayMarshallingCodeContext(context.CurrentStage, IndexerIdentifier, context, appendLocalManagedIdentifierSuffix: cacheManagedValue);
- var elementCleanup = List(_elementMarshaller.Generate(info with { ManagedType = GetElementTypeSymbol(info), RefKind = elementRefKind }, arraySubContext));
- if (elementCleanup.Count != 0)
- {
- // Iterate through the elements of the native array to clean up any unmanaged resources.
- yield return IfStatement(
- BinaryExpression(SyntaxKind.NotEqualsExpression,
- IdentifierName(managedLocal),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
- MarshallerHelpers.GetForLoop(managedLocal, IndexerIdentifier)
- .WithStatement(Block(elementCleanup)));
- }
- yield return GenerateConditionalAllocationFreeSyntax(info, context);
- }
- break;
- }
- }
-
- private static bool ShouldCacheManagedValue(TypePositionInfo info, StubCodeContext context)
- {
- return info.IsByRef && context.CanUseAdditionalTemporaryState;
- }
-
- public override bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
- {
- return true;
- }
-
-
- protected override ExpressionSyntax GenerateAllocationExpression(TypePositionInfo info, StubCodeContext context, SyntaxToken byteLengthIdentifier, out bool allocationRequiresByteLength)
- {
- allocationRequiresByteLength = true;
- // (<nativeType>*)Marshal.AllocCoTaskMem(<byteLengthIdentifier>)
- return CastExpression(AsNativeType(info),
- InvocationExpression(
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- ParseTypeName(TypeNames.System_Runtime_InteropServices_Marshal),
- IdentifierName("AllocCoTaskMem")),
- ArgumentList(SingletonSeparatedList(Argument(IdentifierName(byteLengthIdentifier))))));
- }
-
- protected override ExpressionSyntax GenerateByteLengthCalculationExpression(TypePositionInfo info, StubCodeContext context)
- {
- string managedIdentifier = context.GetIdentifiers(info).managed;
- if (ShouldCacheManagedValue(info, context))
- {
- managedIdentifier += ArrayMarshallingCodeContext.LocalManagedIdentifierSuffix;
- }
- // checked(sizeof(<nativeElementType>) * <managedIdentifier>.Length)
- return CheckedExpression(SyntaxKind.CheckedExpression,
- BinaryExpression(SyntaxKind.MultiplyExpression,
- SizeOfExpression(GetNativeElementTypeSyntax(info)),
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName(managedIdentifier),
- IdentifierName("Length"))));
- }
-
- protected override StatementSyntax GenerateStackallocOnlyValueMarshalling(TypePositionInfo info, StubCodeContext context, SyntaxToken byteLengthIdentifier, SyntaxToken stackAllocPtrIdentifier)
- {
- return EmptyStatement();
- }
-
- protected override ExpressionSyntax GenerateFreeExpression(TypePositionInfo info, StubCodeContext context)
- {
- // Marshal.FreeCoTaskMem((IntPtr)<nativeIdentifier>)
- return InvocationExpression(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- ParseTypeName(TypeNames.System_Runtime_InteropServices_Marshal),
- IdentifierName("FreeCoTaskMem")),
- ArgumentList(SingletonSeparatedList(
- Argument(
- CastExpression(
- ParseTypeName("System.IntPtr"),
- IdentifierName(context.GetIdentifiers(info).native))))));
- }
-
- public override bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context)
- {
- return marshalKind.HasFlag(ByValueContentsMarshalKind.Out);
- }
-
- protected override ExpressionSyntax GenerateNullCheckExpression(TypePositionInfo info, StubCodeContext context)
- {
- string managedIdentifier = context.GetIdentifiers(info).managed;
- if (ShouldCacheManagedValue(info, context))
- {
- managedIdentifier += ArrayMarshallingCodeContext.LocalManagedIdentifierSuffix;
- }
-
- return BinaryExpression(
- SyntaxKind.NotEqualsExpression,
- IdentifierName(managedIdentifier),
- LiteralExpression(SyntaxKind.NullLiteralExpression));
- }
- }
-}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/PinnableManagedValueMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/PinnableManagedValueMarshaller.cs
new file mode 100644
index 00000000000..eff8a6dfa05
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/PinnableManagedValueMarshaller.cs
@@ -0,0 +1,84 @@
+ο»Ώusing System.Collections.Generic;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+ internal sealed class PinnableManagedValueMarshaller : IMarshallingGenerator
+ {
+ private readonly IMarshallingGenerator manualMarshallingGenerator;
+
+ public PinnableManagedValueMarshaller(IMarshallingGenerator manualMarshallingGenerator)
+ {
+ this.manualMarshallingGenerator = manualMarshallingGenerator;
+ }
+
+ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
+ {
+ if (IsPinningPathSupported(info, context))
+ {
+ string identifier = context.GetIdentifiers(info).native;
+ return Argument(CastExpression(AsNativeType(info), IdentifierName(identifier)));
+ }
+ return manualMarshallingGenerator.AsArgument(info, context);
+ }
+
+ public TypeSyntax AsNativeType(TypePositionInfo info)
+ {
+ return manualMarshallingGenerator.AsNativeType(info);
+ }
+
+ public ParameterSyntax AsParameter(TypePositionInfo info)
+ {
+ return manualMarshallingGenerator.AsParameter(info);
+ }
+
+ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
+ {
+ if (IsPinningPathSupported(info, context))
+ {
+ return GeneratePinningPath(info, context);
+ }
+ return manualMarshallingGenerator.Generate(info, context);
+ }
+
+ public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context)
+ {
+ return manualMarshallingGenerator.SupportsByValueMarshalKind(marshalKind, context);
+ }
+
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
+ {
+ if (IsPinningPathSupported(info, context))
+ {
+ return false;
+ }
+ return manualMarshallingGenerator.UsesNativeIdentifier(info, context);
+ }
+ private static bool IsPinningPathSupported(TypePositionInfo info, StubCodeContext context)
+ {
+ return context.PinningSupported && !info.IsByRef && !info.IsManagedReturnPosition;
+ }
+
+ private IEnumerable<StatementSyntax> GeneratePinningPath(TypePositionInfo info, StubCodeContext context)
+ {
+ if (context.CurrentStage == StubCodeContext.Stage.Pin)
+ {
+ var (managedIdentifier, nativeIdentifier) = context.GetIdentifiers(info);
+ yield return FixedStatement(
+ VariableDeclaration(
+ PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))),
+ SingletonSeparatedList(
+ VariableDeclarator(Identifier(nativeIdentifier))
+ .WithInitializer(EqualsValueClause(
+ IdentifierName(managedIdentifier)
+ ))
+ )
+ ),
+ EmptyStatement()
+ );
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs
index 652894e0569..441d2ce758b 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs
@@ -10,7 +10,6 @@ namespace Microsoft.Interop
{
internal class SafeHandleMarshaller : IMarshallingGenerator
{
- private static readonly TypeSyntax NativeType = ParseTypeName("global::System.IntPtr");
private readonly AnalyzerConfigOptions options;
public SafeHandleMarshaller(AnalyzerConfigOptions options)
@@ -20,7 +19,7 @@ namespace Microsoft.Interop
public TypeSyntax AsNativeType(TypePositionInfo info)
{
- return NativeType;
+ return MarshallerHelpers.SystemIntPtrType;
}
public ParameterSyntax AsParameter(TypePositionInfo info)
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs
index b12dcfcd023..96b7c615dfc 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs
@@ -126,7 +126,7 @@ namespace Microsoft.Interop
BinaryExpression(
SyntaxKind.EqualsExpression,
IdentifierName(nativeIdentifier),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
+ LiteralExpression(SyntaxKind.DefaultLiteralExpression)),
LiteralExpression(SyntaxKind.NullLiteralExpression),
ObjectCreationExpression(
PredefinedType(Token(SyntaxKind.StringKeyword)),
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs
index ffe989fddd2..c5a50066f8f 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs
@@ -112,7 +112,7 @@ namespace Microsoft.Interop
BinaryExpression(
SyntaxKind.EqualsExpression,
IdentifierName(nativeIdentifier),
- LiteralExpression(SyntaxKind.NullLiteralExpression)),
+ LiteralExpression(SyntaxKind.DefaultLiteralExpression)),
LiteralExpression(SyntaxKind.NullLiteralExpression),
ObjectCreationExpression(
PredefinedType(Token(SyntaxKind.StringKeyword)),
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/MarshallingAttributeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/MarshallingAttributeInfo.cs
index a9c036948d5..1590fc49f69 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/MarshallingAttributeInfo.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/MarshallingAttributeInfo.cs
@@ -1,13 +1,23 @@
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
+using System.Linq;
using System.Runtime.InteropServices;
namespace Microsoft.Interop
{
+
+ /// <summary>
+ /// Type used to pass on default marshalling details.
+ /// </summary>
+ internal sealed record DefaultMarshallingInfo(
+ CharEncoding CharEncoding
+ );
+
// The following types are modeled to fit with the current prospective spec
- // for C# 10 discriminated unions. Once discriminated unions are released,
+ // for C# vNext discriminated unions. Once discriminated unions are released,
// these should be updated to be implemented as a discriminated union.
internal abstract record MarshallingInfo
@@ -43,31 +53,12 @@ namespace Microsoft.Interop
/// <summary>
/// Simple User-application of System.Runtime.InteropServices.MarshalAsAttribute
/// </summary>
- internal record MarshalAsInfo(
+ internal sealed record MarshalAsInfo(
UnmanagedType UnmanagedType,
CharEncoding CharEncoding) : MarshallingInfoStringSupport(CharEncoding)
{
}
- enum UnmanagedArrayType
- {
- LPArray = UnmanagedType.LPArray,
- ByValArray = UnmanagedType.ByValArray
- }
-
- /// <summary>
- /// User-applied System.Runtime.InteropServices.MarshalAsAttribute with array marshalling info
- /// </summary>
- internal sealed record ArrayMarshalAsInfo(
- UnmanagedArrayType UnmanagedArrayType,
- int ArraySizeConst,
- short ArraySizeParamIndex,
- CharEncoding CharEncoding,
- MarshallingInfo ElementMarshallingInfo) : MarshalAsInfo((UnmanagedType)UnmanagedArrayType, CharEncoding)
- {
- public const short UnspecifiedData = -1;
- }
-
/// <summary>
/// User-applied System.Runtime.InteropServices.BlittableTypeAttribute
/// or System.Runtime.InteropServices.GeneratedMarshallingAttribute on a blittable type
@@ -78,20 +69,46 @@ namespace Microsoft.Interop
[Flags]
internal enum SupportedMarshallingMethods
{
+ None = 0,
ManagedToNative = 0x1,
NativeToManaged = 0x2,
ManagedToNativeStackalloc = 0x4,
Pinning = 0x8,
+ All = -1
+ }
+
+ internal abstract record CountInfo;
+
+ internal sealed record NoCountInfo : CountInfo
+ {
+ public static readonly NoCountInfo Instance = new NoCountInfo();
+
+ private NoCountInfo() { }
+ }
+
+ internal sealed record ConstSizeCountInfo(int Size) : CountInfo;
+
+ internal sealed record CountElementCountInfo(TypePositionInfo ElementInfo) : CountInfo
+ {
+ public const string ReturnValueElementName = "return-value";
+ }
+
+ internal sealed record SizeAndParamIndexInfo(int ConstSize, int ParamIndex) : CountInfo
+ {
+ public const int UnspecifiedData = -1;
+
+ public static readonly SizeAndParamIndexInfo Unspecified = new(UnspecifiedData, UnspecifiedData);
}
/// <summary>
/// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute
/// </summary>
- internal sealed record NativeMarshallingAttributeInfo(
+ internal record NativeMarshallingAttributeInfo(
ITypeSymbol NativeMarshallingType,
ITypeSymbol? ValuePropertyType,
SupportedMarshallingMethods MarshallingMethods,
- bool NativeTypePinnable) : MarshallingInfo;
+ bool NativeTypePinnable,
+ bool UseDefaultMarshalling) : MarshallingInfo;
/// <summary>
/// User-applied System.Runtime.InteropServices.GeneratedMarshallingAttribute
@@ -105,9 +122,605 @@ namespace Microsoft.Interop
/// </summary>
internal sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor) : MarshallingInfo;
-
/// <summary>
- /// Default marshalling for arrays
- /// </summary>
- internal sealed record ArrayMarshallingInfo(MarshallingInfo ElementMarshallingInfo) : MarshallingInfo;
+ /// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute
+ /// with a contiguous collection marshaller
+ internal sealed record NativeContiguousCollectionMarshallingInfo(
+ ITypeSymbol NativeMarshallingType,
+ ITypeSymbol? ValuePropertyType,
+ SupportedMarshallingMethods MarshallingMethods,
+ bool NativeTypePinnable,
+ bool UseDefaultMarshalling,
+ CountInfo ElementCountInfo,
+ ITypeSymbol ElementType,
+ MarshallingInfo ElementMarshallingInfo) : NativeMarshallingAttributeInfo(
+ NativeMarshallingType,
+ ValuePropertyType,
+ MarshallingMethods,
+ NativeTypePinnable,
+ UseDefaultMarshalling
+ );
+
+ internal class MarshallingAttributeInfoParser
+ {
+ private readonly Compilation compilation;
+ private readonly GeneratorDiagnostics diagnostics;
+ private readonly DefaultMarshallingInfo defaultInfo;
+ private readonly ISymbol contextSymbol;
+ private readonly ITypeSymbol marshalAsAttribute;
+ private readonly ITypeSymbol marshalUsingAttribute;
+
+ public MarshallingAttributeInfoParser(
+ Compilation compilation,
+ GeneratorDiagnostics diagnostics,
+ DefaultMarshallingInfo defaultInfo,
+ ISymbol contextSymbol)
+ {
+ this.compilation = compilation;
+ this.diagnostics = diagnostics;
+ this.defaultInfo = defaultInfo;
+ this.contextSymbol = contextSymbol;
+ marshalAsAttribute = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute)!;
+ marshalUsingAttribute = compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute)!;
+ }
+
+ public MarshallingInfo ParseMarshallingInfo(
+ ITypeSymbol managedType,
+ IEnumerable<AttributeData> useSiteAttributes)
+ {
+ return ParseMarshallingInfo(managedType, useSiteAttributes, ImmutableHashSet<string>.Empty);
+ }
+
+ private MarshallingInfo ParseMarshallingInfo(
+ ITypeSymbol managedType,
+ IEnumerable<AttributeData> useSiteAttributes,
+ ImmutableHashSet<string> inspectedElements)
+ {
+ Dictionary<int, AttributeData> marshallingAttributesByIndirectionLevel = new();
+ int maxIndirectionLevelDataProvided = 0;
+ foreach (AttributeData attribute in useSiteAttributes)
+ {
+ if (TryGetAttributeIndirectionLevel(attribute, out int indirectionLevel))
+ {
+ if (marshallingAttributesByIndirectionLevel.ContainsKey(indirectionLevel))
+ {
+ diagnostics.ReportConfigurationNotSupported(attribute, "Marshalling Data for Indirection Level", indirectionLevel.ToString());
+ return NoMarshallingInfo.Instance;
+ }
+ marshallingAttributesByIndirectionLevel.Add(indirectionLevel, attribute);
+ maxIndirectionLevelDataProvided = Math.Max(maxIndirectionLevelDataProvided, indirectionLevel);
+ }
+ }
+
+ int maxIndirectionLevelUsed = 0;
+ MarshallingInfo info = GetMarshallingInfo(
+ managedType,
+ marshallingAttributesByIndirectionLevel,
+ indirectionLevel: 0,
+ inspectedElements,
+ ref maxIndirectionLevelUsed);
+ if (maxIndirectionLevelUsed < maxIndirectionLevelDataProvided)
+ {
+ diagnostics.ReportConfigurationNotSupported(marshallingAttributesByIndirectionLevel[maxIndirectionLevelDataProvided], ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionLevel, maxIndirectionLevelDataProvided.ToString());
+ }
+ return info;
+ }
+
+ private MarshallingInfo GetMarshallingInfo(
+ ITypeSymbol type,
+ Dictionary<int, AttributeData> useSiteAttributes,
+ int indirectionLevel,
+ ImmutableHashSet<string> inspectedElements,
+ ref int maxIndirectionLevelUsed)
+ {
+ maxIndirectionLevelUsed = Math.Max(indirectionLevel, maxIndirectionLevelUsed);
+ CountInfo parsedCountInfo = NoCountInfo.Instance;
+
+ if (useSiteAttributes.TryGetValue(indirectionLevel, out AttributeData useSiteAttribute))
+ {
+ INamedTypeSymbol attributeClass = useSiteAttribute.AttributeClass!;
+
+ if (indirectionLevel == 0
+ && SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute), attributeClass))
+ {
+ // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute
+ return CreateInfoFromMarshalAs(type, useSiteAttribute, ref maxIndirectionLevelUsed);
+ }
+ else if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute), attributeClass))
+ {
+ if (parsedCountInfo != NoCountInfo.Instance)
+ {
+ diagnostics.ReportConfigurationNotSupported(useSiteAttribute, "Duplicate Count Info");
+ return NoMarshallingInfo.Instance;
+ }
+ parsedCountInfo = CreateCountInfo(useSiteAttribute, inspectedElements);
+ if (useSiteAttribute.ConstructorArguments.Length != 0)
+ {
+ return CreateNativeMarshallingInfo(
+ type,
+ useSiteAttribute,
+ isMarshalUsingAttribute: true,
+ indirectionLevel,
+ parsedCountInfo,
+ useSiteAttributes,
+ inspectedElements,
+ ref maxIndirectionLevelUsed);
+ }
+ }
+ }
+
+ // If we aren't overriding the marshalling at usage time,
+ // then fall back to the information on the element type itself.
+ foreach (var typeAttribute in type.GetAttributes())
+ {
+ INamedTypeSymbol attributeClass = typeAttribute.AttributeClass!;
+
+ if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.BlittableTypeAttribute), attributeClass))
+ {
+ // If type is generic, then we need to re-evaluate that it is blittable at usage time.
+ if (type is INamedTypeSymbol { IsGenericType: false } || type.HasOnlyBlittableFields())
+ {
+ return new BlittableTypeAttributeInfo();
+ }
+ break;
+ }
+ else if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.NativeMarshallingAttribute), attributeClass))
+ {
+ return CreateNativeMarshallingInfo(
+ type,
+ typeAttribute,
+ isMarshalUsingAttribute: false,
+ indirectionLevel,
+ parsedCountInfo,
+ useSiteAttributes,
+ inspectedElements,
+ ref maxIndirectionLevelUsed);
+ }
+ else if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.GeneratedMarshallingAttribute), attributeClass))
+ {
+ return type.IsConsideredBlittable() ? new BlittableTypeAttributeInfo() : new GeneratedNativeMarshallingAttributeInfo(null! /* TODO: determine naming convention */);
+ }
+ }
+
+ // If the type doesn't have custom attributes that dictate marshalling,
+ // then consider the type itself.
+ if (TryCreateTypeBasedMarshallingInfo(
+ type,
+ parsedCountInfo,
+ indirectionLevel,
+ useSiteAttributes,
+ inspectedElements,
+ ref maxIndirectionLevelUsed,
+ out MarshallingInfo infoMaybe))
+ {
+ return infoMaybe;
+ }
+
+ // No marshalling info was computed, but a character encoding was provided.
+ // If the type is a character or string then pass on these details.
+ if (defaultInfo.CharEncoding != CharEncoding.Undefined
+ && (type.SpecialType == SpecialType.System_Char
+ || type.SpecialType == SpecialType.System_String))
+ {
+ return new MarshallingInfoStringSupport(defaultInfo.CharEncoding);
+ }
+
+ return NoMarshallingInfo.Instance;
+ }
+
+ CountInfo CreateCountInfo(AttributeData marshalUsingData, ImmutableHashSet<string> inspectedElements)
+ {
+ int? constSize = null;
+ string? elementName = null;
+ foreach (var arg in marshalUsingData.NamedArguments)
+ {
+ if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ConstantElementCount)
+ {
+ constSize = (int)arg.Value.Value!;
+ }
+ else if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName)
+ {
+ if (arg.Value.Value is null)
+ {
+ diagnostics.ReportConfigurationNotSupported(marshalUsingData, ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName, "null");
+ return NoCountInfo.Instance;
+ }
+ elementName = (string)arg.Value.Value!;
+ }
+ }
+
+ if (constSize is not null && elementName is not null)
+ {
+ diagnostics.ReportConfigurationNotSupported(marshalUsingData, $"{ManualTypeMarshallingHelper.MarshalUsingProperties.ConstantElementCount} and {ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName} combined");
+ }
+ else if (constSize is not null)
+ {
+ return new ConstSizeCountInfo(constSize.Value);
+ }
+ else if (elementName is not null)
+ {
+ if (inspectedElements.Contains(elementName))
+ {
+ diagnostics.ReportConfigurationNotSupported(marshalUsingData, $"Cyclical {ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName}");
+ return NoCountInfo.Instance;
+ }
+
+ TypePositionInfo? elementInfo = CreateForElementName(elementName, inspectedElements.Add(elementName));
+ if (elementInfo is null)
+ {
+ diagnostics.ReportConfigurationNotSupported(marshalUsingData, ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName, elementName);
+ return NoCountInfo.Instance;
+ }
+ return new CountElementCountInfo(elementInfo);
+ }
+
+ return NoCountInfo.Instance;
+ }
+
+ private TypePositionInfo? CreateForElementName(string elementName, ImmutableHashSet<string> inspectedElements)
+ {
+ if (contextSymbol is IMethodSymbol method)
+ {
+ if (elementName == CountElementCountInfo.ReturnValueElementName)
+ {
+ return TypePositionInfo.CreateForType(
+ method.ReturnType,
+ ParseMarshallingInfo(method.ReturnType, method.GetReturnTypeAttributes(), inspectedElements)) with
+ {
+ ManagedIndex = TypePositionInfo.ReturnIndex
+ };
+ }
+
+ foreach (var param in method.Parameters)
+ {
+ if (param.Name == elementName)
+ {
+ return TypePositionInfo.CreateForParameter(param, ParseMarshallingInfo(param.Type, param.GetAttributes(), inspectedElements), compilation);
+ }
+ }
+ }
+ else if (contextSymbol is INamedTypeSymbol _)
+ {
+ // TODO: Handle when we create a struct marshalling generator
+ // Do we want to support CountElementName pointing to only fields, or properties as well?
+ // If only fields, how do we handle properties with generated backing fields?
+ }
+
+ return null;
+ }
+
+ MarshallingInfo CreateInfoFromMarshalAs(
+ ITypeSymbol type,
+ AttributeData attrData,
+ ref int maxIndirectionLevelUsed)
+ {
+ object unmanagedTypeObj = attrData.ConstructorArguments[0].Value!;
+ UnmanagedType unmanagedType = unmanagedTypeObj is short
+ ? (UnmanagedType)(short)unmanagedTypeObj
+ : (UnmanagedType)unmanagedTypeObj;
+ if (!Enum.IsDefined(typeof(UnmanagedType), unmanagedType)
+ || unmanagedType == UnmanagedType.CustomMarshaler
+ || unmanagedType == UnmanagedType.SafeArray)
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, nameof(UnmanagedType), unmanagedType.ToString());
+ }
+ bool isArrayType = unmanagedType == UnmanagedType.LPArray || unmanagedType == UnmanagedType.ByValArray;
+ UnmanagedType elementUnmanagedType = (UnmanagedType)SizeAndParamIndexInfo.UnspecifiedData;
+ SizeAndParamIndexInfo arraySizeInfo = SizeAndParamIndexInfo.Unspecified;
+
+ // All other data on attribute is defined as NamedArguments.
+ foreach (var namedArg in attrData.NamedArguments)
+ {
+ switch (namedArg.Key)
+ {
+ default:
+ Debug.Fail($"An unknown member was found on {nameof(MarshalAsAttribute)}");
+ continue;
+ case nameof(MarshalAsAttribute.SafeArraySubType):
+ case nameof(MarshalAsAttribute.SafeArrayUserDefinedSubType):
+ case nameof(MarshalAsAttribute.IidParameterIndex):
+ case nameof(MarshalAsAttribute.MarshalTypeRef):
+ case nameof(MarshalAsAttribute.MarshalType):
+ case nameof(MarshalAsAttribute.MarshalCookie):
+ diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
+ break;
+ case nameof(MarshalAsAttribute.ArraySubType):
+ if (!isArrayType)
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
+ }
+ elementUnmanagedType = (UnmanagedType)namedArg.Value.Value!;
+ break;
+ case nameof(MarshalAsAttribute.SizeConst):
+ if (!isArrayType)
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
+ }
+ arraySizeInfo = arraySizeInfo with { ConstSize = (int)namedArg.Value.Value! };
+ break;
+ case nameof(MarshalAsAttribute.SizeParamIndex):
+ if (!isArrayType)
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
+ }
+ arraySizeInfo = arraySizeInfo with { ParamIndex = (short)namedArg.Value.Value! };
+ break;
+ }
+ }
+
+ if (!isArrayType)
+ {
+ return new MarshalAsInfo(unmanagedType, defaultInfo.CharEncoding);
+ }
+
+ if (type is not IArrayTypeSymbol { ElementType: ITypeSymbol elementType })
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, nameof(UnmanagedType), unmanagedType.ToString());
+ return NoMarshallingInfo.Instance;
+ }
+
+ MarshallingInfo elementMarshallingInfo = NoMarshallingInfo.Instance;
+ if (elementUnmanagedType != (UnmanagedType)SizeAndParamIndexInfo.UnspecifiedData)
+ {
+ elementMarshallingInfo = new MarshalAsInfo(elementUnmanagedType, defaultInfo.CharEncoding);
+ }
+ else
+ {
+ maxIndirectionLevelUsed = 1;
+ elementMarshallingInfo = GetMarshallingInfo(elementType, new Dictionary<int, AttributeData>(), 1, ImmutableHashSet<string>.Empty, ref maxIndirectionLevelUsed);
+ }
+
+ INamedTypeSymbol? arrayMarshaller;
+
+ if (elementType is IPointerTypeSymbol { PointedAtType: ITypeSymbol pointedAt })
+ {
+ arrayMarshaller = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_GeneratedMarshalling_PtrArrayMarshaller_Metadata)?.Construct(pointedAt);
+ }
+ else
+ {
+ arrayMarshaller = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_GeneratedMarshalling_ArrayMarshaller_Metadata)?.Construct(elementType);
+ }
+
+ if (arrayMarshaller is null)
+ {
+ // If the array marshaler type is not available, then we cannot marshal arrays.
+ return NoMarshallingInfo.Instance;
+ }
+
+ return new NativeContiguousCollectionMarshallingInfo(
+ NativeMarshallingType: arrayMarshaller,
+ ValuePropertyType: ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type,
+ MarshallingMethods: ~SupportedMarshallingMethods.Pinning,
+ NativeTypePinnable: true,
+ UseDefaultMarshalling: true,
+ ElementCountInfo: arraySizeInfo,
+ ElementType: elementType,
+ ElementMarshallingInfo: elementMarshallingInfo);
+ }
+
+ MarshallingInfo CreateNativeMarshallingInfo(
+ ITypeSymbol type,
+ AttributeData attrData,
+ bool isMarshalUsingAttribute,
+ int indirectionLevel,
+ CountInfo parsedCountInfo,
+ Dictionary<int, AttributeData> useSiteAttributes,
+ ImmutableHashSet<string> inspectedElements,
+ ref int maxIndirectionLevelUsed)
+ {
+ SupportedMarshallingMethods methods = SupportedMarshallingMethods.None;
+
+ if (!isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null)
+ {
+ methods |= SupportedMarshallingMethods.Pinning;
+ }
+
+ ITypeSymbol spanOfByte = compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!.Construct(compilation.GetSpecialType(SpecialType.System_Byte));
+
+ INamedTypeSymbol nativeType = (INamedTypeSymbol)attrData.ConstructorArguments[0].Value!;
+
+ if (nativeType.IsUnboundGenericType)
+ {
+ if (isMarshalUsingAttribute)
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, "Native Type", nativeType.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+ else if (type is INamedTypeSymbol namedType)
+ {
+ if (namedType.Arity != nativeType.Arity)
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, "Native Type", nativeType.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+ else
+ {
+ nativeType = nativeType.ConstructedFrom.Construct(namedType.TypeArguments.ToArray());
+ }
+ }
+ else
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, "Native Type", nativeType.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+ }
+
+ ITypeSymbol contiguousCollectionMarshalerAttribute = compilation.GetTypeByMetadataName(TypeNames.GenericContiguousCollectionMarshallerAttribute)!;
+
+ bool isContiguousCollectionMarshaller = nativeType.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, contiguousCollectionMarshalerAttribute));
+ IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType);
+
+ var marshallingVariant = isContiguousCollectionMarshaller
+ ? ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.ContiguousCollection
+ : ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard;
+
+ bool hasInt32Constructor = false;
+ foreach (var ctor in nativeType.Constructors)
+ {
+ if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallingVariant) && (valueProperty is null or { GetMethod: not null }))
+ {
+ methods |= SupportedMarshallingMethods.ManagedToNative;
+ }
+ else if (ManualTypeMarshallingHelper.IsStackallocConstructor(ctor, type, spanOfByte, marshallingVariant)
+ && (valueProperty is null or { GetMethod: not null }))
+ {
+ methods |= SupportedMarshallingMethods.ManagedToNativeStackalloc;
+ }
+ else if (ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.SpecialType == SpecialType.System_Int32)
+ {
+ hasInt32Constructor = true;
+ }
+ }
+
+ // The constructor that takes only the native element size is required for collection marshallers
+ // in the native-to-managed scenario.
+ if ((!isContiguousCollectionMarshaller
+ || (hasInt32Constructor && ManualTypeMarshallingHelper.HasSetUnmarshalledCollectionLengthMethod(nativeType)))
+ && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type)
+ && (valueProperty is null or { SetMethod: not null }))
+ {
+ methods |= SupportedMarshallingMethods.NativeToManaged;
+ }
+
+ if (methods == SupportedMarshallingMethods.None)
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, "Native Type", nativeType.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+
+ if (isContiguousCollectionMarshaller)
+ {
+ if (!ManualTypeMarshallingHelper.HasNativeValueStorageProperty(nativeType, spanOfByte))
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, "Native Type", nativeType.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+
+ if (!ManualTypeMarshallingHelper.TryGetElementTypeFromContiguousCollectionMarshaller(nativeType, out ITypeSymbol elementType))
+ {
+ diagnostics.ReportConfigurationNotSupported(attrData, "Native Type", nativeType.ToDisplayString());
+ return NoMarshallingInfo.Instance;
+ }
+
+ return new NativeContiguousCollectionMarshallingInfo(
+ nativeType,
+ valueProperty?.Type,
+ methods,
+ NativeTypePinnable: ManualTypeMarshallingHelper.FindGetPinnableReference(nativeType) is not null,
+ UseDefaultMarshalling: !isMarshalUsingAttribute,
+ parsedCountInfo,
+ elementType,
+ GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed));
+ }
+
+ return new NativeMarshallingAttributeInfo(
+ nativeType,
+ valueProperty?.Type,
+ methods,
+ NativeTypePinnable: ManualTypeMarshallingHelper.FindGetPinnableReference(nativeType) is not null,
+ UseDefaultMarshalling: !isMarshalUsingAttribute);
+ }
+
+ bool TryCreateTypeBasedMarshallingInfo(
+ ITypeSymbol type,
+ CountInfo parsedCountInfo,
+ int indirectionLevel,
+ Dictionary<int, AttributeData> useSiteAttributes,
+ ImmutableHashSet<string> inspectedElements,
+ ref int maxIndirectionLevelUsed,
+ out MarshallingInfo marshallingInfo)
+ {
+ // Check for an implicit SafeHandle conversion.
+ var conversion = compilation.ClassifyCommonConversion(type, compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_SafeHandle)!);
+ if (conversion.Exists
+ && conversion.IsImplicit
+ && (conversion.IsReference || conversion.IsIdentity))
+ {
+ bool hasAccessibleDefaultConstructor = false;
+ if (type is INamedTypeSymbol named && !named.IsAbstract && named.InstanceConstructors.Length > 0)
+ {
+ foreach (var ctor in named.InstanceConstructors)
+ {
+ if (ctor.Parameters.Length == 0)
+ {
+ hasAccessibleDefaultConstructor = compilation.IsSymbolAccessibleWithin(ctor, contextSymbol.ContainingType);
+ break;
+ }
+ }
+ }
+ marshallingInfo = new SafeHandleMarshallingInfo(hasAccessibleDefaultConstructor);
+ return true;
+ }
+
+ if (type is IArrayTypeSymbol { ElementType: ITypeSymbol elementType })
+ {
+ INamedTypeSymbol? arrayMarshaller;
+
+ if (elementType is IPointerTypeSymbol { PointedAtType: ITypeSymbol pointedAt })
+ {
+ arrayMarshaller = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_GeneratedMarshalling_PtrArrayMarshaller_Metadata)?.Construct(pointedAt);
+ }
+ else
+ {
+ arrayMarshaller = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_GeneratedMarshalling_ArrayMarshaller_Metadata)?.Construct(elementType);
+ }
+
+ if (arrayMarshaller is null)
+ {
+ // If the array marshaler type is not available, then we cannot marshal arrays.
+ marshallingInfo = NoMarshallingInfo.Instance;
+ return false;
+ }
+
+ marshallingInfo = new NativeContiguousCollectionMarshallingInfo(
+ NativeMarshallingType: arrayMarshaller,
+ ValuePropertyType: ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type,
+ MarshallingMethods: ~SupportedMarshallingMethods.Pinning,
+ NativeTypePinnable: true,
+ UseDefaultMarshalling: true,
+ ElementCountInfo: parsedCountInfo,
+ ElementType: elementType,
+ ElementMarshallingInfo: GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed));
+ return true;
+ }
+
+ if (type is INamedTypeSymbol { IsValueType: true } valueType
+ && !valueType.IsExposedOutsideOfCurrentCompilation()
+ && valueType.IsConsideredBlittable())
+ {
+ // Allow implicit [BlittableType] on internal value types.
+ marshallingInfo = new BlittableTypeAttributeInfo();
+ return true;
+ }
+
+ marshallingInfo = NoMarshallingInfo.Instance;
+ return false;
+ }
+
+ private bool TryGetAttributeIndirectionLevel(AttributeData attrData, out int indirectionLevel)
+ {
+ if (SymbolEqualityComparer.Default.Equals(attrData.AttributeClass, marshalAsAttribute))
+ {
+ indirectionLevel = 0;
+ return true;
+ }
+
+ if (!SymbolEqualityComparer.Default.Equals(attrData.AttributeClass, marshalUsingAttribute))
+ {
+ indirectionLevel = 0;
+ return false;
+ }
+
+ foreach (var arg in attrData.NamedArguments)
+ {
+ if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionLevel)
+ {
+ indirectionLevel = (int)arg.Value.Value!;
+ return true;
+ }
+ }
+ indirectionLevel = 0;
+ return true;
+ }
+ }
}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs
index 2acea21707c..6e9b0ab11cb 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs
@@ -61,7 +61,7 @@ namespace Microsoft.Interop {
}
/// <summary>
- /// Looks up a localized string similar to Marshalling an array from unmanaged to managed requires either the &apos;SizeParamIndex&apos; or &apos;SizeConst&apos; fields to be set on a &apos;MarshalAsAttribute&apos;..
+ /// Looks up a localized string similar to Marshalling an array from unmanaged to managed requires either the &apos;SizeParamIndex&apos; or &apos;SizeConst&apos; fields to be set on a &apos;MarshalAsAttribute&apos; or the &apos;ConstantElementCount&apos; or &apos;CountElementName&apos; properties to be set on a &apos;MarshalUsingAttribute&apos;..
/// </summary>
internal static string ArraySizeMustBeSpecified {
get {
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx
index 698f32820bf..f9b39dd6a92 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx
@@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ArraySizeMustBeSpecified" xml:space="preserve">
- <value>Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute'.</value>
+ <value>Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.</value>
</data>
<data name="ArraySizeParamIndexOutOfRange" xml:space="preserve">
<value>The 'SizeParamIndex' value in the 'MarshalAsAttribute' is out of range.</value>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/StubCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/StubCodeGenerator.cs
index dce737b7d5a..15746deb0cb 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/StubCodeGenerator.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/StubCodeGenerator.cs
@@ -94,18 +94,25 @@ namespace Microsoft.Interop
public override (string managed, string native) GetIdentifiers(TypePositionInfo info)
{
- if (info.IsManagedReturnPosition && !info.IsNativeReturnPosition)
+ // If the info is in the managed return position, then we need to generate a name to use
+ // for both the managed and native values since there is no name in the signature for the return value.
+ if (info.IsManagedReturnPosition)
{
return (ReturnIdentifier, ReturnNativeIdentifier);
}
- else if (!info.IsManagedReturnPosition && info.IsNativeReturnPosition)
- {
+ // If the info is in the native return position but is not in the managed return position,
+ // then that means that the stub is introducing an additional info for the return position.
+ // This means that there is no name in source for this info, so we must provide one here.
+ // We can't use ReturnIdentifier or ReturnNativeIdentifier since that will be used by the managed return value.
+ // Additionally, since all use cases today of a TypePositionInfo in the native position but not the managed
+ // are for infos that aren't in the managed signature at all (PreserveSig scenario), we don't have a name
+ // that we can use from source. As a result, we generate another name for the native return value
+ // and use the same name for native and managed.
+ else if (info.IsNativeReturnPosition)
+ {
+ Debug.Assert(info.ManagedIndex == TypePositionInfo.UnsetIndex);
return (InvokeReturnIdentifier, InvokeReturnIdentifier);
}
- else if (info.IsManagedReturnPosition && info.IsNativeReturnPosition)
- {
- return (ReturnIdentifier, ReturnNativeIdentifier);
- }
else
{
// If the info isn't in either the managed or native return position,
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs
index 509969a0523..ad75ffadb8c 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeNames.cs
@@ -17,6 +17,8 @@ namespace Microsoft.Interop
public const string MarshalUsingAttribute = "System.Runtime.InteropServices.MarshalUsingAttribute";
+ public const string GenericContiguousCollectionMarshallerAttribute = "System.Runtime.InteropServices.GenericContiguousCollectionMarshallerAttribute";
+
public const string LCIDConversionAttribute = "System.Runtime.InteropServices.LCIDConversionAttribute";
public const string System_Span_Metadata = "System.Span`1";
@@ -36,7 +38,11 @@ namespace Microsoft.Interop
{
return options.UseMarshalType() ? System_Runtime_InteropServices_Marshal : System_Runtime_InteropServices_MarshalEx;
}
-
+
+ public const string System_Runtime_InteropServices_GeneratedMarshalling_ArrayMarshaller_Metadata = "System.Runtime.InteropServices.GeneratedMarshalling.ArrayMarshaller`1";
+
+ public const string System_Runtime_InteropServices_GeneratedMarshalling_PtrArrayMarshaller_Metadata = "System.Runtime.InteropServices.GeneratedMarshalling.PtrArrayMarshaller`1";
+
public const string System_Runtime_InteropServices_MemoryMarshal = "System.Runtime.InteropServices.MemoryMarshal";
public const string System_Runtime_InteropServices_SafeHandle = "System.Runtime.InteropServices.SafeHandle";
@@ -46,5 +52,9 @@ namespace Microsoft.Interop
public const string System_Runtime_InteropServices_InAttribute = "System.Runtime.InteropServices.InAttribute";
public const string System_Runtime_CompilerServices_SkipLocalsInitAttribute = "System.Runtime.CompilerServices.SkipLocalsInitAttribute";
+
+ // TODO: Add configuration for using Internal.Runtime.CompilerServices.Unsafe to support
+ // running against System.Private.CoreLib
+ public const string System_Runtime_CompilerServices_Unsafe = "System.Runtime.CompilerServices.Unsafe";
}
}
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypePositionInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypePositionInfo.cs
index 0fc8788e1c9..0f011d9f084 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypePositionInfo.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypePositionInfo.cs
@@ -1,7 +1,5 @@
ο»Ώusing System;
using System.Collections.Generic;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
@@ -9,12 +7,6 @@ using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Microsoft.Interop
{
- /// <summary>
- /// Type used to pass on default marshalling details.
- /// </summary>
- internal sealed record DefaultMarshallingInfo (
- CharEncoding CharEncoding
- );
/// <summary>
/// Describes how to marshal the contents of a value in comparison to the value itself.
@@ -82,9 +74,8 @@ namespace Microsoft.Interop
public MarshallingInfo MarshallingAttributeInfo { get; init; }
- public static TypePositionInfo CreateForParameter(IParameterSymbol paramSymbol, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics, INamedTypeSymbol scopeSymbol)
+ public static TypePositionInfo CreateForParameter(IParameterSymbol paramSymbol, MarshallingInfo marshallingInfo, Compilation compilation)
{
- var marshallingInfo = GetMarshallingInfo(paramSymbol.Type, paramSymbol.GetAttributes(), defaultInfo, compilation, diagnostics, scopeSymbol);
var typeInfo = new TypePositionInfo()
{
ManagedType = paramSymbol.Type,
@@ -98,27 +89,12 @@ namespace Microsoft.Interop
return typeInfo;
}
- public static TypePositionInfo CreateForType(ITypeSymbol type, IEnumerable<AttributeData> attributes, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics, INamedTypeSymbol scopeSymbol)
- {
- var marshallingInfo = GetMarshallingInfo(type, attributes, defaultInfo, compilation, diagnostics, scopeSymbol);
- var typeInfo = new TypePositionInfo()
- {
- ManagedType = type,
- InstanceIdentifier = string.Empty,
- RefKind = RefKind.None,
- RefKindSyntax = SyntaxKind.None,
- MarshallingAttributeInfo = marshallingInfo
- };
-
- return typeInfo;
- }
-
- public static TypePositionInfo CreateForType(ITypeSymbol type, MarshallingInfo marshallingInfo)
+ public static TypePositionInfo CreateForType(ITypeSymbol type, MarshallingInfo marshallingInfo, string identifier = "")
{
var typeInfo = new TypePositionInfo()
{
ManagedType = type,
- InstanceIdentifier = string.Empty,
+ InstanceIdentifier = identifier,
RefKind = RefKind.None,
RefKindSyntax = SyntaxKind.None,
MarshallingAttributeInfo = marshallingInfo
@@ -127,235 +103,6 @@ namespace Microsoft.Interop
return typeInfo;
}
- private static MarshallingInfo GetMarshallingInfo(ITypeSymbol type, IEnumerable<AttributeData> attributes, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics, INamedTypeSymbol scopeSymbol)
- {
- // Look at attributes passed in - usage specific.
- foreach (var attrData in attributes)
- {
- INamedTypeSymbol attributeClass = attrData.AttributeClass!;
-
- if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute), attributeClass))
- {
- // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute
- return CreateMarshalAsInfo(type, attrData, defaultInfo, compilation, diagnostics, scopeSymbol);
- }
- else if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute), attributeClass))
- {
- return CreateNativeMarshallingInfo(type, compilation, attrData, allowGetPinnableReference: false);
- }
- }
-
- // If we aren't overriding the marshalling at usage time,
- // then fall back to the information on the element type itself.
- foreach (var attrData in type.GetAttributes())
- {
- INamedTypeSymbol attributeClass = attrData.AttributeClass!;
-
- if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.BlittableTypeAttribute), attributeClass))
- {
- // If type is generic, then we need to re-evaluate that it is blittable at usage time.
- if (type is INamedTypeSymbol { IsGenericType: false } || type.HasOnlyBlittableFields())
- {
- return new BlittableTypeAttributeInfo();
- }
- break;
- }
- else if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.NativeMarshallingAttribute), attributeClass))
- {
- return CreateNativeMarshallingInfo(type, compilation, attrData, allowGetPinnableReference: true);
- }
- else if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.GeneratedMarshallingAttribute), attributeClass))
- {
- return type.IsConsideredBlittable() ? new BlittableTypeAttributeInfo() : new GeneratedNativeMarshallingAttributeInfo(null! /* TODO: determine naming convention */);
- }
- }
-
- // If the type doesn't have custom attributes that dictate marshalling,
- // then consider the type itself.
- if (TryCreateTypeBasedMarshallingInfo(type, defaultInfo, compilation, diagnostics, scopeSymbol, out MarshallingInfo infoMaybe))
- {
- return infoMaybe;
- }
-
- // No marshalling info was computed, but a character encoding was provided.
- // If the type is a character or string then pass on these details.
- if (defaultInfo.CharEncoding != CharEncoding.Undefined
- && (type.SpecialType == SpecialType.System_Char
- || type.SpecialType == SpecialType.System_String))
- {
- return new MarshallingInfoStringSupport(defaultInfo.CharEncoding);
- }
-
- return NoMarshallingInfo.Instance;
-
- static MarshalAsInfo CreateMarshalAsInfo(ITypeSymbol type, AttributeData attrData, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics, INamedTypeSymbol scopeSymbol)
- {
- object unmanagedTypeObj = attrData.ConstructorArguments[0].Value!;
- UnmanagedType unmanagedType = unmanagedTypeObj is short
- ? (UnmanagedType)(short)unmanagedTypeObj
- : (UnmanagedType)unmanagedTypeObj;
- if (!Enum.IsDefined(typeof(UnmanagedType), unmanagedType)
- || unmanagedType == UnmanagedType.CustomMarshaler
- || unmanagedType == UnmanagedType.SafeArray)
- {
- diagnostics.ReportConfigurationNotSupported(attrData, nameof(UnmanagedType), unmanagedType.ToString());
- }
- bool isArrayType = unmanagedType == UnmanagedType.LPArray || unmanagedType == UnmanagedType.ByValArray;
- UnmanagedType unmanagedArraySubType = (UnmanagedType)ArrayMarshalAsInfo.UnspecifiedData;
- int arraySizeConst = ArrayMarshalAsInfo.UnspecifiedData;
- short arraySizeParamIndex = ArrayMarshalAsInfo.UnspecifiedData;
-
- // All other data on attribute is defined as NamedArguments.
- foreach (var namedArg in attrData.NamedArguments)
- {
- switch (namedArg.Key)
- {
- default:
- Debug.Fail($"An unknown member was found on {nameof(MarshalAsAttribute)}");
- continue;
- case nameof(MarshalAsAttribute.SafeArraySubType):
- case nameof(MarshalAsAttribute.SafeArrayUserDefinedSubType):
- case nameof(MarshalAsAttribute.IidParameterIndex):
- case nameof(MarshalAsAttribute.MarshalTypeRef):
- case nameof(MarshalAsAttribute.MarshalType):
- case nameof(MarshalAsAttribute.MarshalCookie):
- diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
- break;
- case nameof(MarshalAsAttribute.ArraySubType):
- if (!isArrayType)
- {
- diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
- }
- unmanagedArraySubType = (UnmanagedType)namedArg.Value.Value!;
- break;
- case nameof(MarshalAsAttribute.SizeConst):
- if (!isArrayType)
- {
- diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
- }
- arraySizeConst = (int)namedArg.Value.Value!;
- break;
- case nameof(MarshalAsAttribute.SizeParamIndex):
- if (!isArrayType)
- {
- diagnostics.ReportConfigurationNotSupported(attrData, $"{attrData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
- }
- arraySizeParamIndex = (short)namedArg.Value.Value!;
- break;
- }
- }
-
- if (!isArrayType)
- {
- return new MarshalAsInfo(unmanagedType, defaultInfo.CharEncoding);
- }
-
- MarshallingInfo elementMarshallingInfo = NoMarshallingInfo.Instance;
- if (unmanagedArraySubType != (UnmanagedType)ArrayMarshalAsInfo.UnspecifiedData)
- {
- elementMarshallingInfo = new MarshalAsInfo(unmanagedArraySubType, defaultInfo.CharEncoding);
- }
- else if (type is IArrayTypeSymbol { ElementType: ITypeSymbol elementType })
- {
- elementMarshallingInfo = GetMarshallingInfo(elementType, Array.Empty<AttributeData>(), defaultInfo, compilation, diagnostics, scopeSymbol);
- }
-
- return new ArrayMarshalAsInfo(
- UnmanagedArrayType: (UnmanagedArrayType)unmanagedType,
- ArraySizeConst: arraySizeConst,
- ArraySizeParamIndex: arraySizeParamIndex,
- CharEncoding: defaultInfo.CharEncoding,
- ElementMarshallingInfo: elementMarshallingInfo
- );
- }
-
- static NativeMarshallingAttributeInfo CreateNativeMarshallingInfo(ITypeSymbol type, Compilation compilation, AttributeData attrData, bool allowGetPinnableReference)
- {
- ITypeSymbol spanOfByte = compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!.Construct(compilation.GetSpecialType(SpecialType.System_Byte));
- INamedTypeSymbol nativeType = (INamedTypeSymbol)attrData.ConstructorArguments[0].Value!;
- SupportedMarshallingMethods methods = 0;
- IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType);
- foreach (var ctor in nativeType.Constructors)
- {
- if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type)
- && (valueProperty is null or { GetMethod: not null }))
- {
- methods |= SupportedMarshallingMethods.ManagedToNative;
- }
- else if (ManualTypeMarshallingHelper.IsStackallocConstructor(ctor, type, spanOfByte)
- && (valueProperty is null or { GetMethod: not null }))
- {
- methods |= SupportedMarshallingMethods.ManagedToNativeStackalloc;
- }
- }
-
- if (ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type)
- && (valueProperty is null or { SetMethod: not null }))
- {
- methods |= SupportedMarshallingMethods.NativeToManaged;
- }
-
- if (allowGetPinnableReference && ManualTypeMarshallingHelper.FindGetPinnableReference(type) != null)
- {
- methods |= SupportedMarshallingMethods.Pinning;
- }
-
- if (methods == 0)
- {
- // TODO: Diagnostic since no marshalling methods are supported.
- }
-
- return new NativeMarshallingAttributeInfo(
- nativeType,
- valueProperty?.Type,
- methods,
- NativeTypePinnable: ManualTypeMarshallingHelper.FindGetPinnableReference(nativeType) is not null);
- }
-
- static bool TryCreateTypeBasedMarshallingInfo(ITypeSymbol type, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics, INamedTypeSymbol scopeSymbol, out MarshallingInfo marshallingInfo)
- {
- // Check for an implicit SafeHandle conversion.
- var conversion = compilation.ClassifyCommonConversion(type, compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_SafeHandle)!);
- if (conversion.Exists
- && conversion.IsImplicit
- && (conversion.IsReference || conversion.IsIdentity))
- {
- bool hasAccessibleDefaultConstructor = false;
- if (type is INamedTypeSymbol named && !named.IsAbstract && named.InstanceConstructors.Length > 0)
- {
- foreach (var ctor in named.InstanceConstructors)
- {
- if (ctor.Parameters.Length == 0)
- {
- hasAccessibleDefaultConstructor = compilation.IsSymbolAccessibleWithin(ctor, scopeSymbol);
- break;
- }
- }
- }
- marshallingInfo = new SafeHandleMarshallingInfo(hasAccessibleDefaultConstructor);
- return true;
- }
-
- if (type is IArrayTypeSymbol { ElementType: ITypeSymbol elementType })
- {
- marshallingInfo = new ArrayMarshallingInfo(GetMarshallingInfo(elementType, Array.Empty<AttributeData>(), defaultInfo, compilation, diagnostics, scopeSymbol));
- return true;
- }
-
- if (type is INamedTypeSymbol { IsValueType: true } valueType
- && !valueType.IsExposedOutsideOfCurrentCompilation()
- && valueType.IsConsideredBlittable())
- {
- // Allow implicit [BlittableType] on internal value types.
- marshallingInfo = new BlittableTypeAttributeInfo();
- return true;
- }
-
- marshallingInfo = NoMarshallingInfo.Instance;
- return false;
- }
- }
-
private static ByValueContentsMarshalKind GetByValueContentsMarshalKind(IEnumerable<AttributeData> attributes, Compilation compilation)
{
INamedTypeSymbol outAttributeType = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_OutAttribute)!;
diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs
index 616d87c521c..2cf28efc943 100644
--- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs
+++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/TypeSymbolExtensions.cs
@@ -13,9 +13,9 @@ namespace Microsoft.Interop
{
static class TypeSymbolExtensions
{
- public static bool HasOnlyBlittableFields(this ITypeSymbol type) => HasOnlyBlittableFields(type, new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default));
+ public static bool HasOnlyBlittableFields(this ITypeSymbol type) => HasOnlyBlittableFields(type, ImmutableHashSet.Create<ITypeSymbol>(SymbolEqualityComparer.Default));
- private static bool HasOnlyBlittableFields(this ITypeSymbol type, HashSet<ITypeSymbol> seenTypes)
+ private static bool HasOnlyBlittableFields(this ITypeSymbol type, ImmutableHashSet<ITypeSymbol> seenTypes)
{
if (seenTypes.Contains(type))
{
@@ -24,7 +24,7 @@ namespace Microsoft.Interop
// before that is detected, so we check here to avoid a stack overflow.
return false;
}
- seenTypes.Add(type);
+
foreach (var field in type.GetMembers().OfType<IFieldSymbol>())
{
if (!field.IsStatic)
@@ -39,18 +39,16 @@ namespace Microsoft.Interop
// We'll re-evaluate blittability for generic fields of generic types at instantation time.
{ Type: ITypeParameterSymbol } => true,
{ Type: { IsValueType: false } } => false,
- _ => IsConsideredBlittable(field.Type, seenTypes)
+ _ => IsConsideredBlittable(field.Type, seenTypes.Add(type))
};
if (!fieldBlittable)
{
- seenTypes.Remove(type);
return false;
}
}
}
- seenTypes.Remove(type);
return true;
}
@@ -73,9 +71,9 @@ namespace Microsoft.Interop
_ => false
};
- public static bool IsConsideredBlittable(this ITypeSymbol type) => IsConsideredBlittable(type, new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default));
+ public static bool IsConsideredBlittable(this ITypeSymbol type) => IsConsideredBlittable(type, ImmutableHashSet.Create<ITypeSymbol>(SymbolEqualityComparer.Default));
- private static bool IsConsideredBlittable(this ITypeSymbol type, HashSet<ITypeSymbol> seenTypes)
+ private static bool IsConsideredBlittable(this ITypeSymbol type, ImmutableHashSet<ITypeSymbol> seenTypes)
{
if (type.SpecialType != SpecialType.None)
{
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ArrayMarshaller.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ArrayMarshaller.cs
new file mode 100644
index 00000000000..370e90efb3d
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ArrayMarshaller.cs
@@ -0,0 +1,217 @@
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices.GeneratedMarshalling
+{
+ public unsafe ref struct ArrayMarshaller<T>
+ {
+ private T[]? managedArray;
+ private readonly int sizeOfNativeElement;
+ private IntPtr allocatedMemory;
+
+ public ArrayMarshaller(int sizeOfNativeElement)
+ :this()
+ {
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ }
+
+ public ArrayMarshaller(T[]? managed, int sizeOfNativeElement)
+ {
+ allocatedMemory = default;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ if (managed is null)
+ {
+ managedArray = null;
+ NativeValueStorage = default;
+ return;
+ }
+ managedArray = managed;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ // Always allocate at least one byte when the array is zero-length.
+ int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1);
+ allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
+ NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate);
+ }
+
+ public ArrayMarshaller(T[]? managed, Span<byte> stackSpace, int sizeOfNativeElement)
+ {
+ allocatedMemory = default;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ if (managed is null)
+ {
+ managedArray = null;
+ NativeValueStorage = default;
+ return;
+ }
+ managedArray = managed;
+ // Always allocate at least one byte when the array is zero-length.
+ int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1);
+ if (spaceToAllocate <= stackSpace.Length)
+ {
+ NativeValueStorage = stackSpace[0..spaceToAllocate];
+ }
+ else
+ {
+ allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
+ NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate);
+ }
+ }
+
+ /// <summary>
+ /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
+ /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
+ /// blow the stack since this is a new optimization in the code-generated interop.
+ /// </summary>
+ public const int StackBufferSize = 0x200;
+
+ public Span<T> ManagedValues => managedArray;
+
+ public Span<byte> NativeValueStorage { get; private set; }
+
+ public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage);
+
+ public void SetUnmarshalledCollectionLength(int length)
+ {
+ managedArray = new T[length];
+ }
+
+ public byte* Value
+ {
+ get
+ {
+ Debug.Assert(managedArray is null || allocatedMemory != IntPtr.Zero);
+ return (byte*)allocatedMemory;
+ }
+ set
+ {
+ if (value == null)
+ {
+ managedArray = null;
+ NativeValueStorage = default;
+ }
+ else
+ {
+ allocatedMemory = (IntPtr)value;
+ NativeValueStorage = new Span<byte>(value, (managedArray?.Length ?? 0) * sizeOfNativeElement);
+ }
+ }
+ }
+
+ public T[]? ToManaged() => managedArray;
+
+ public void FreeNative()
+ {
+ if (allocatedMemory != IntPtr.Zero)
+ {
+ Marshal.FreeCoTaskMem(allocatedMemory);
+ }
+ }
+ }
+
+ public unsafe ref struct PtrArrayMarshaller<T> where T : unmanaged
+ {
+ private T*[]? managedArray;
+ private readonly int sizeOfNativeElement;
+ private IntPtr allocatedMemory;
+
+ public PtrArrayMarshaller(int sizeOfNativeElement)
+ : this()
+ {
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ }
+
+ public PtrArrayMarshaller(T*[]? managed, int sizeOfNativeElement)
+ {
+ allocatedMemory = default;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ if (managed is null)
+ {
+ managedArray = null;
+ NativeValueStorage = default;
+ return;
+ }
+ managedArray = managed;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ // Always allocate at least one byte when the array is zero-length.
+ int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1);
+ allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
+ NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate);
+ }
+
+ public PtrArrayMarshaller(T*[]? managed, Span<byte> stackSpace, int sizeOfNativeElement)
+ {
+ allocatedMemory = default;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ if (managed is null)
+ {
+ managedArray = null;
+ NativeValueStorage = default;
+ return;
+ }
+ managedArray = managed;
+ // Always allocate at least one byte when the array is zero-length.
+ int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1);
+ if (spaceToAllocate <= stackSpace.Length)
+ {
+ NativeValueStorage = stackSpace[0..spaceToAllocate];
+ }
+ else
+ {
+ allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
+ NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate);
+ }
+ }
+
+ /// <summary>
+ /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
+ /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
+ /// blow the stack since this is a new optimization in the code-generated interop.
+ /// </summary>
+ public const int StackBufferSize = 0x200;
+
+ public Span<IntPtr> ManagedValues => Unsafe.As<IntPtr[]>(managedArray);
+
+ public Span<byte> NativeValueStorage { get; private set; }
+
+ public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage);
+
+ public void SetUnmarshalledCollectionLength(int length)
+ {
+ managedArray = new T*[length];
+ }
+
+ public byte* Value
+ {
+ get
+ {
+ Debug.Assert(managedArray is null || allocatedMemory != IntPtr.Zero);
+ return (byte*)allocatedMemory;
+ }
+ set
+ {
+ if (value == null)
+ {
+ managedArray = null;
+ NativeValueStorage = default;
+ }
+ else
+ {
+ allocatedMemory = (IntPtr)value;
+ NativeValueStorage = new Span<byte>(value, (managedArray?.Length ?? 0) * sizeOfNativeElement);
+ }
+
+ }
+ }
+
+ public T*[]? ToManaged() => managedArray;
+
+ public void FreeNative()
+ {
+ if (allocatedMemory != IntPtr.Zero)
+ {
+ Marshal.FreeCoTaskMem(allocatedMemory);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs
index 11e1b29639f..dd605b9e60b 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs
@@ -2,17 +2,17 @@
namespace System.Runtime.InteropServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
- class GeneratedMarshallingAttribute : Attribute
+ sealed class GeneratedMarshallingAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Struct)]
- public class BlittableTypeAttribute : Attribute
+ public sealed class BlittableTypeAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
- public class NativeMarshallingAttribute : Attribute
+ public sealed class NativeMarshallingAttribute : Attribute
{
public NativeMarshallingAttribute(Type nativeType)
{
@@ -22,14 +22,36 @@ namespace System.Runtime.InteropServices
public Type NativeType { get; }
}
- [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field)]
- public class MarshalUsingAttribute : Attribute
+ [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field, AllowMultiple = true)]
+ public sealed class MarshalUsingAttribute : Attribute
{
+ public MarshalUsingAttribute()
+ {
+ CountElementName = string.Empty;
+ }
+
public MarshalUsingAttribute(Type nativeType)
+ :this()
{
NativeType = nativeType;
}
- public Type NativeType { get; }
+ public Type? NativeType { get; }
+
+ public string CountElementName { get; set; }
+
+ public int ConstantElementCount { get; set; }
+
+ public int ElementIndirectionLevel { get; set; }
+
+ public const string ReturnsCountValue = "return-value";
+ }
+
+ [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
+ public sealed class GenericContiguousCollectionMarshallerAttribute : Attribute
+ {
+ public GenericContiguousCollectionMarshallerAttribute()
+ {
+ }
}
} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs
new file mode 100644
index 00000000000..a5be7cf5d1c
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs
@@ -0,0 +1,161 @@
+ο»Ώusing SharedTypes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+using Xunit;
+
+namespace DllImportGenerator.IntegrationTests
+{
+ partial class NativeExportsNE
+ {
+ public partial class Collections
+ {
+ [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
+ public static partial int Sum([MarshalUsing(typeof(ListMarshaller<int>))] List<int> values, int numValues);
+
+ [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
+ public static partial int Sum(ref int values, int numValues);
+
+ [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
+ public static partial int SumInArray([MarshalUsing(typeof(ListMarshaller<int>))] in List<int> values, int numValues);
+
+ [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "duplicate_int_array")]
+ public static partial void Duplicate([MarshalUsing(typeof(ListMarshaller<int>), CountElementName = "numValues")] ref List<int> values, int numValues);
+
+ [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "create_range_array")]
+ [return:MarshalUsing(typeof(ListMarshaller<int>), CountElementName = "numValues")]
+ public static partial List<int> CreateRange(int start, int end, out int numValues);
+
+ [GeneratedDllImport(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);
+
+ [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
+ [return:MarshalUsing(typeof(ListMarshaller<byte>), ConstantElementCount = sizeof(long))]
+ public static partial List<byte> GetLongBytes(long l);
+
+ [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")]
+ [return:MarshalAs(UnmanagedType.U1)]
+ public static partial bool AndAllMembers([MarshalUsing(typeof(ListMarshaller<BoolStruct>))] List<BoolStruct> pArray, int length);
+ }
+ }
+
+ public class CollectionTests
+ {
+ [Fact]
+ public void BlittableElementColllectionMarshalledToNativeAsExpected()
+ {
+ var list = new List<int> { 1, 5, 79, 165, 32, 3 };
+ Assert.Equal(list.Sum(), NativeExportsNE.Collections.Sum(list, list.Count));
+ }
+
+ [Fact]
+ public void NullBlittableElementColllectionMarshalledToNativeAsExpected()
+ {
+ Assert.Equal(-1, NativeExportsNE.Collections.Sum(null, 0));
+ }
+
+ [Fact]
+ public void BlittableElementColllectionInParameter()
+ {
+ var list = new List<int> { 1, 5, 79, 165, 32, 3 };
+ Assert.Equal(list.Sum(), NativeExportsNE.Collections.SumInArray(list, list.Count));
+ }
+
+ [Fact]
+ public void BlittableElementCollectionRefParameter()
+ {
+ var list = new List<int> { 1, 5, 79, 165, 32, 3 };
+ var newList = list;
+ NativeExportsNE.Collections.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.CreateRange(start, end, out _));
+
+ List<int> res;
+ NativeExportsNE.Collections.CreateRange_Out(start, end, out _, out res);
+ Assert.Equal(expected, res);
+ }
+
+ [Fact]
+ public void NullBlittableElementCollectionReturnedFromNative()
+ {
+ Assert.Null(NativeExportsNE.Collections.CreateRange(1, 0, out _));
+
+ List<int> res;
+ NativeExportsNE.Collections.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 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>
+ {
+ new BoolStruct
+ {
+ b1 = true,
+ b2 = true,
+ b3 = true,
+ },
+ new BoolStruct
+ {
+ b1 = true,
+ b2 = true,
+ b3 = true,
+ },
+ new BoolStruct
+ {
+ 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/DllImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs
index d19caf2e5f5..863eb9ae447 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs
@@ -522,7 +522,7 @@ delegate int MyDelegate(int a);";
{BlittableMyStruct()}
";
- public static string ArrayParametersAndModifiers(string elementType) => $@"
+ public static string MarshalAsArrayParametersAndModifiers(string elementType) => $@"
using System.Runtime.InteropServices;
partial class Test
{{
@@ -538,9 +538,9 @@ partial class Test
);
}}";
- public static string ArrayParametersAndModifiers<T>() => ArrayParametersAndModifiers(typeof(T).ToString());
+ public static string MarshalAsArrayParametersAndModifiers<T>() => MarshalAsArrayParametersAndModifiers(typeof(T).ToString());
- public static string ArrayParameterWithSizeParam(string sizeParamType, bool isByRef) => $@"
+ public static string MarshalAsArrayParameterWithSizeParam(string sizeParamType, bool isByRef) => $@"
using System.Runtime.InteropServices;
partial class Test
{{
@@ -551,10 +551,10 @@ partial class Test
);
}}";
- public static string ArrayParameterWithSizeParam<T>(bool isByRef) => ArrayParameterWithSizeParam(typeof(T).ToString(), isByRef);
+ public static string MarshalAsArrayParameterWithSizeParam<T>(bool isByRef) => MarshalAsArrayParameterWithSizeParam(typeof(T).ToString(), isByRef);
- public static string ArrayParameterWithNestedMarshalInfo(string elementType, UnmanagedType nestedMarshalInfo) => $@"
+ public static string MarshalAsArrayParameterWithNestedMarshalInfo(string elementType, UnmanagedType nestedMarshalInfo) => $@"
using System.Runtime.InteropServices;
partial class Test
{{
@@ -564,7 +564,7 @@ partial class Test
);
}}";
- public static string ArrayParameterWithNestedMarshalInfo<T>(UnmanagedType nestedMarshalType) => ArrayParameterWithNestedMarshalInfo(typeof(T).ToString(), nestedMarshalType);
+ public static string MarshalAsArrayParameterWithNestedMarshalInfo<T>(UnmanagedType nestedMarshalType) => MarshalAsArrayParameterWithNestedMarshalInfo(typeof(T).ToString(), nestedMarshalType);
public static string ArrayPreserveSigFalse(string elementType) => $@"
using System.Runtime.InteropServices;
@@ -925,7 +925,7 @@ struct Native
}
";
- public static string ArrayMarshallingWithCustomStructElementWithValueProperty => ArrayParametersAndModifiers("IntStructWrapper") + @"
+ public static string ArrayMarshallingWithCustomStructElementWithValueProperty => MarshalAsArrayParametersAndModifiers("IntStructWrapper") + @"
[NativeMarshalling(typeof(IntStructWrapperNative))]
public struct IntStructWrapper
{
@@ -945,7 +945,7 @@ public struct IntStructWrapperNative
}
";
- public static string ArrayMarshallingWithCustomStructElement => ArrayParametersAndModifiers("IntStructWrapper") + @"
+ public static string ArrayMarshallingWithCustomStructElement => MarshalAsArrayParametersAndModifiers("IntStructWrapper") + @"
[NativeMarshalling(typeof(IntStructWrapperNative))]
public struct IntStructWrapper
{
@@ -1090,5 +1090,246 @@ struct RecursiveStruct2
RecursiveStruct1 s;
int i;
}";
+
+ public static string CollectionByValue(string elementType) => BasicParameterByValue($"TestCollection<{elementType}>") + @"
+[NativeMarshalling(typeof(Marshaller<>))]
+class TestCollection<T> {}
+
+[GenericContiguousCollectionMarshaller]
+ref struct Marshaller<T>
+{
+ public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {}
+ public System.Span<T> ManagedValues { get; }
+ public System.Span<byte> NativeValueStorage { get; }
+ public System.IntPtr Value { get; }
+}
+";
+
+ public static string CollectionByValue<T>() => CollectionByValue(typeof(T).ToString());
+
+ public static string MarshalUsingCollectionCountInfoParametersAndModifiers(string collectionType) => $@"
+using System.Runtime.InteropServices;
+partial class Test
+{{
+ [GeneratedDllImport(""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 CustomCollectionWithMarshaller(bool enableDefaultMarshalling)
+ {
+ string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty;
+ return nativeMarshallingAttribute + @"class TestCollection<T> {}
+
+[GenericContiguousCollectionMarshaller]
+ref struct Marshaller<T>
+{
+ public Marshaller(int nativeElementSize) : this() {}
+ public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {}
+ public System.Span<T> ManagedValues { get; }
+ public System.Span<byte> NativeValueStorage { get; }
+ public System.IntPtr Value { get; set; }
+ public void SetUnmarshalledCollectionLength(int length) {}
+ public 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;
+partial class Test
+{{
+ [GeneratedDllImport(""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 CustomCollectionCustomMarshallerParametersAndModifiers<T>() => CustomCollectionCustomMarshallerParametersAndModifiers(typeof(T).ToString());
+
+ public static string MarshalUsingCollectionReturnValueLength(string collectionType, string marshallerType) => $@"
+using System.Runtime.InteropServices;
+partial class Test
+{{
+ [GeneratedDllImport(""DoesNotExist"")]
+ public static partial int Method(
+ [MarshalUsing(typeof({marshallerType}), CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out {collectionType} pOut
+ );
+}}";
+
+ public static string CustomCollectionCustomMarshallerReturnValueLength(string elementType) => MarshalUsingCollectionReturnValueLength($"TestCollection<{elementType}>", $"Marshaller<{elementType}>") + CustomCollectionWithMarshaller(enableDefaultMarshalling: false);
+
+ public static string CustomCollectionCustomMarshallerReturnValueLength<T>() => CustomCollectionCustomMarshallerReturnValueLength(typeof(T).ToString());
+
+ public static string MarshalUsingArrayParameterWithSizeParam(string sizeParamType, bool isByRef) => $@"
+using System.Runtime.InteropServices;
+partial class Test
+{{
+ [GeneratedDllImport(""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;
+partial class Test
+{{
+ [GeneratedDllImport(""DoesNotExist"")]
+ public static partial void Method(
+ int pRefSize,
+ [MarshalUsing(ConstantElementCount = 10, CountElementName = ""pRefSize"")] ref int[] pRef
+ );
+}}";
+
+ public static string MarshalUsingCollectionWithNullElementName => $@"
+using System.Runtime.InteropServices;
+partial class Test
+{{
+ [GeneratedDllImport(""DoesNotExist"")]
+ public static partial void Method(
+ int pRefSize,
+ [MarshalUsing(CountElementName = null)] ref int[] pRef
+ );
+}}";
+
+ public static string GenericCollectionMarshallingArityMismatch => BasicParameterByValue("TestCollection<int>") + @"
+[NativeMarshalling(typeof(Marshaller<,>))]
+class TestCollection<T> {}
+
+[GenericContiguousCollectionMarshaller]
+ref struct Marshaller<T, U>
+{
+ public Marshaller(TestCollection<T> managed, int nativeElementSize) : this() {}
+ public System.Span<T> ManagedValues { get; }
+ public System.Span<byte> NativeValueStorage { get; }
+ public System.IntPtr Value { get; }
+ public TestCollection<T> ToManaged() => throw null;
+}";
+
+ public static string GenericCollectionWithCustomElementMarshalling => @"
+using System.Runtime.InteropServices;
+partial class Test
+{
+ [GeneratedDllImport(""DoesNotExist"")]
+ [return:MarshalUsing(ConstantElementCount=10)]
+ [return:MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)]
+ public static partial TestCollection<int> Method(
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection<int> p,
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] in TestCollection<int> pIn,
+ int pRefSize,
+ [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] ref TestCollection<int> pRef,
+ [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] out TestCollection<int> pOut,
+ out int pOutSize
+ );
+}
+
+struct IntWrapper
+{
+ public IntWrapper(int i){}
+ public int ToManaged() => throw null;
+}
+
+" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
+
+ public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel => @"
+using System.Runtime.InteropServices;
+partial class Test
+{
+ [GeneratedDllImport(""DoesNotExist"")]
+ public static partial void Method(
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection<int> p);
+}
+
+struct IntWrapper
+{
+ public IntWrapper(int i){}
+ public int ToManaged() => throw null;
+}
+
+" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
+
+ public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel => @"
+using System.Runtime.InteropServices;
+partial class Test
+{
+ [GeneratedDllImport(""DoesNotExist"")]
+ public static partial void Method(
+ [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 2)] TestCollection<int> p);
+}
+
+struct IntWrapper
+{
+ public IntWrapper(int i){}
+ public int ToManaged() => throw null;
+}
+
+" + CustomCollectionWithMarshaller(enableDefaultMarshalling: true);
+
+ public static string MarshalAsAndMarshalUsingOnReturnValue => @"
+using System.Runtime.InteropServices;
+partial class Test
+{
+ [GeneratedDllImport(""DoesNotExist"")]
+ [return:MarshalUsing(ConstantElementCount=10)]
+ [return:MarshalAs(UnmanagedType.LPArray, SizeConst=10)]
+ public static partial int[] Method();
+}
+";
+
+ public static string RecursiveCountElementNameOnReturnValue => @"
+using System.Runtime.InteropServices;
+partial class Test
+{
+ [GeneratedDllImport(""DoesNotExist"")]
+ [return:MarshalUsing(CountElementName=MarshalUsingAttribute.ReturnsCountValue)]
+ public static partial int[] Method();
+}
+";
+
+ public static string RecursiveCountElementNameOnParameter => @"
+using System.Runtime.InteropServices;
+partial class Test
+{
+ [GeneratedDllImport(""DoesNotExist"")]
+ public static partial void Method(
+ [MarshalUsing(CountElementName=""arr"")] ref int[] arr
+ );
+}
+";
+ public static string MutuallyRecursiveCountElementNameOnParameter => @"
+using System.Runtime.InteropServices;
+partial class Test
+{
+ [GeneratedDllImport(""DoesNotExist"")]
+ public static partial void Method(
+ [MarshalUsing(CountElementName=""arr2"")] ref int[] arr,
+ [MarshalUsing(CountElementName=""arr"")] ref int[] arr2
+ );
+}
+";
}
}
diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs
index 19736372a25..d34b33c6381 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs
@@ -78,10 +78,14 @@ namespace DllImportGenerator.UnitTests
yield return new object[] { CodeSnippets.BasicParametersAndModifiers<IntPtr[]>(), 3, 0 };
yield return new object[] { CodeSnippets.BasicParametersAndModifiers<UIntPtr[]>(), 3, 0 };
- // Array with non-integer size param
- yield return new object[] { CodeSnippets.ArrayParameterWithSizeParam<float>(isByRef: false), 1, 0 };
- yield return new object[] { CodeSnippets.ArrayParameterWithSizeParam<double>(isByRef: false), 1, 0 };
- yield return new object[] { CodeSnippets.ArrayParameterWithSizeParam<bool>(isByRef: false), 1, 0 };
+ // Collection with non-integer size param
+ yield return new object[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<float>(isByRef: false), 1, 0 };
+ yield return new object[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<double>(isByRef: false), 1, 0 };
+ yield return new object[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<bool>(isByRef: false), 1, 0 };
+ yield return new object[] { CodeSnippets.MarshalUsingArrayParameterWithSizeParam<float>(isByRef: false), 1, 0 };
+ yield return new object[] { CodeSnippets.MarshalUsingArrayParameterWithSizeParam<double>(isByRef: false), 1, 0 };
+ yield return new object[] { CodeSnippets.MarshalUsingArrayParameterWithSizeParam<bool>(isByRef: false), 1, 0 };
+
// Custom type marshalling with invalid members
yield return new object[] { CodeSnippets.CustomStructMarshallingByRefValueProperty, 3, 0 };
@@ -104,6 +108,22 @@ namespace DllImportGenerator.UnitTests
yield return new object[] { CodeSnippets.ImplicitlyBlittableStructParametersAndModifiers("public"), 5, 0 };
yield return new object[] { CodeSnippets.ImplicitlyBlittableGenericTypeParametersAndModifiers<bool>(), 5, 0 };
yield return new object[] { CodeSnippets.ImplicitlyBlittableGenericTypeParametersAndModifiers<int>("public"), 5, 0 };
+
+ // Collection with constant and element size parameter
+ yield return new object[] { CodeSnippets.MarshalUsingCollectionWithConstantAndElementCount, 2, 0 };
+
+ // Collection with null element size parameter name
+ 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.MarshalAsAndMarshalUsingOnReturnValue, 2, 0 };
+ yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel, 2, 0 };
+ yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel, 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 };
}
[Theory]
diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/Compiles.cs
index a785ed75ac6..5f593857556 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/Compiles.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/Compiles.cs
@@ -37,39 +37,39 @@ namespace DllImportGenerator.UnitTests
yield return new[] { CodeSnippets.BasicParametersAndModifiers<UIntPtr>() };
// Arrays
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<byte>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<sbyte>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<short>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<ushort>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<int>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<uint>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<long>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<ulong>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<float>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<double>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<bool>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<IntPtr>() };
- yield return new[] { CodeSnippets.ArrayParametersAndModifiers<UIntPtr>() };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<byte>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<sbyte>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<short>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<ushort>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<int>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<uint>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<long>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<ulong>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<IntPtr>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<UIntPtr>(isByRef: false) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<byte>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<sbyte>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<short>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<ushort>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<int>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<uint>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<long>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<ulong>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<IntPtr>(isByRef: true) };
- yield return new[] { CodeSnippets.ArrayParameterWithSizeParam<UIntPtr>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<byte>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<sbyte>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<short>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<ushort>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<int>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<uint>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<long>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<ulong>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<float>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<double>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<bool>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<IntPtr>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParametersAndModifiers<UIntPtr>() };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<byte>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<sbyte>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<short>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<ushort>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<int>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<uint>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<long>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<ulong>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<IntPtr>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<UIntPtr>(isByRef: false) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<byte>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<sbyte>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<short>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<ushort>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<int>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<uint>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<long>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<ulong>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<IntPtr>(isByRef: true) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithSizeParam<UIntPtr>(isByRef: true) };
// CharSet
yield return new[] { CodeSnippets.BasicParametersAndModifiersWithCharSet<char>(CharSet.Unicode) };
@@ -89,9 +89,9 @@ namespace DllImportGenerator.UnitTests
yield return new[] { CodeSnippets.MarshalAsParametersAndModifiers<string>(UnmanagedType.LPTStr) };
yield return new[] { CodeSnippets.MarshalAsParametersAndModifiers<string>(UnmanagedType.LPUTF8Str) };
yield return new[] { CodeSnippets.MarshalAsParametersAndModifiers<string>(UnmanagedType.LPStr) };
- yield return new[] { CodeSnippets.ArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPWStr) };
- yield return new[] { CodeSnippets.ArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPUTF8Str) };
- yield return new[] { CodeSnippets.ArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPStr) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPWStr) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPUTF8Str) };
+ yield return new[] { CodeSnippets.MarshalAsArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPStr) };
// [In, Out] attributes
// By value non-blittable array
@@ -203,6 +203,62 @@ namespace DllImportGenerator.UnitTests
yield return new[] { CodeSnippets.ImplicitlyBlittableStructParametersAndModifiers("internal") };
yield return new[] { CodeSnippets.ImplicitlyBlittableGenericTypeParametersAndModifiers<int>() };
yield return new[] { CodeSnippets.ImplicitlyBlittableGenericTypeParametersAndModifiers<int>("internal") };
+
+ // 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<bool>() };
+ yield return new[] { CodeSnippets.CollectionByValue<IntPtr>() };
+ yield return new[] { CodeSnippets.CollectionByValue<UIntPtr>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<byte[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<sbyte[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<short[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<ushort[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<int[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<uint[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<long[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<ulong[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<float[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<double[]>() };
+ yield return new[] { CodeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<bool[]>() };
+ 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<bool>() };
+ 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<bool>() };
+ yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<IntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerParametersAndModifiers<UIntPtr>() };
+ yield return new[] { CodeSnippets.CustomCollectionCustomMarshallerReturnValueLength<int>() };
+ yield return new[] { CodeSnippets.GenericCollectionWithCustomElementMarshalling };
}
[Theory]
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 aee951b165e..35275afa091 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs
+++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SharedTypes
@@ -217,4 +219,114 @@ namespace SharedTypes
public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value };
}
+
+ [GenericContiguousCollectionMarshaller]
+ public unsafe ref struct ListMarshaller<T>
+ {
+ private List<T> managedList;
+ private readonly int sizeOfNativeElement;
+ private IntPtr allocatedMemory;
+
+ public ListMarshaller(int sizeOfNativeElement)
+ : this()
+ {
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ }
+
+ public ListMarshaller(List<T> managed, int sizeOfNativeElement)
+ {
+ allocatedMemory = default;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ if (managed is null)
+ {
+ managedList = null;
+ NativeValueStorage = default;
+ return;
+ }
+ managedList = managed;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ // Always allocate at least one byte when the array is zero-length.
+ int spaceToAllocate = Math.Max(managed.Count * sizeOfNativeElement, 1);
+ allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
+ NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate);
+ }
+
+ public ListMarshaller(List<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
+ {
+ allocatedMemory = default;
+ this.sizeOfNativeElement = sizeOfNativeElement;
+ if (managed is null)
+ {
+ managedList = null;
+ NativeValueStorage = default;
+ return;
+ }
+ managedList = managed;
+ // Always allocate at least one byte when the array is zero-length.
+ int spaceToAllocate = Math.Max(managed.Count * sizeOfNativeElement, 1);
+ if (spaceToAllocate <= stackSpace.Length)
+ {
+ NativeValueStorage = stackSpace[0..spaceToAllocate];
+ }
+ else
+ {
+ allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
+ NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate);
+ }
+ }
+
+ /// <summary>
+ /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
+ /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
+ /// blow the stack since this is a new optimization in the code-generated interop.
+ /// </summary>
+ public const int StackBufferSize = 0x200;
+
+ public Span<T> ManagedValues => CollectionsMarshal.AsSpan(managedList);
+
+ public Span<byte> NativeValueStorage { get; private set; }
+
+ public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference();
+
+ public void SetUnmarshalledCollectionLength(int length)
+ {
+ managedList = new List<T>(length);
+ for (int i = 0; i < length; i++)
+ {
+ managedList.Add(default);
+ }
+ }
+
+ public byte* Value
+ {
+ get
+ {
+ Debug.Assert(managedList is null || allocatedMemory != IntPtr.Zero);
+ return (byte*)allocatedMemory;
+ }
+ set
+ {
+ if (value == null)
+ {
+ managedList = null;
+ NativeValueStorage = default;
+ }
+ else
+ {
+ allocatedMemory = (IntPtr)value;
+ NativeValueStorage = new Span<byte>(value, (managedList?.Count ?? 0) * sizeOfNativeElement);
+ }
+ }
+ }
+
+ public List<T> ToManaged() => managedList;
+
+ public void FreeNative()
+ {
+ if (allocatedMemory != IntPtr.Zero)
+ {
+ Marshal.FreeCoTaskMem(allocatedMemory);
+ }
+ }
+ }
}