diff --git a/DllImportGenerator/DllImportGenerator.IntegrationTests/ArrayTests.cs b/DllImportGenerator/DllImportGenerator.IntegrationTests/ArrayTests.cs index 778e30b6bfd4..7a7b738e71dc 100644 --- a/DllImportGenerator/DllImportGenerator.IntegrationTests/ArrayTests.cs +++ b/DllImportGenerator/DllImportGenerator.IntegrationTests/ArrayTests.cs @@ -1,4 +1,5 @@ -using System; +using SharedTypes; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -37,6 +38,10 @@ public partial class Arrays [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "append_int_to_array")] public static partial void Append([MarshalAs(UnmanagedType.LPArray, SizeConst = 1, SizeParamIndex = 1)] ref int[] values, int numOriginalValues, int newValue); + + [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")] + [return:MarshalAs(UnmanagedType.U1)] + public static partial bool AndAllMembers(BoolStruct[] pArray, int length); } } @@ -151,6 +156,36 @@ public void DynamicSizedArrayWithConstantComponent() Assert.Equal(array.Concat(new [] { newValue }), newArray); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ArrayWithSimpleNonBlittableTypeMarshalling(bool result) + { + var boolValues = new[] + { + 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.Arrays.AndAllMembers(boolValues, boolValues.Length)); + } + private static string ReverseChars(string value) { if (value == null) diff --git a/DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs b/DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs index 3933c1528ae9..214537e518e1 100644 --- a/DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs @@ -864,6 +864,46 @@ struct Native private int i; public S ToManaged() => new S { b = i != 0 }; } +"; + + public static string ArrayMarshallingWithCustomStructElementWithValueProperty => ArrayParametersAndModifiers("IntStructWrapper") + @" +[NativeMarshalling(typeof(IntStructWrapperNative))] +public struct IntStructWrapper +{ + public int Value; +} + +public struct IntStructWrapperNative +{ + public IntStructWrapperNative(IntStructWrapper managed) + { + Value = managed.Value; + } + + public int Value { get; set; } + + public IntStructWrapper ToManaged() => new IntStructWrapper { Value = Value }; +} +"; + + public static string ArrayMarshallingWithCustomStructElement => ArrayParametersAndModifiers("IntStructWrapper") + @" +[NativeMarshalling(typeof(IntStructWrapperNative))] +public struct IntStructWrapper +{ + public int Value; +} + +public struct IntStructWrapperNative +{ + private int value; + + public IntStructWrapperNative(IntStructWrapper managed) + { + value = managed.Value; + } + + public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value }; +} "; } } diff --git a/DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs b/DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs index d050b72617d9..41fe64dc27c8 100644 --- a/DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs @@ -73,6 +73,9 @@ public static IEnumerable CodeSnippetsToCompile() yield return new object[] { CodeSnippets.CustomStructMarshallingManagedToNativeOnlyReturnValue, 1, 0 }; yield return new object[] { CodeSnippets.CustomStructMarshallingNativeToManagedOnlyInParameter, 1, 0 }; yield return new object[] { CodeSnippets.CustomStructMarshallingStackallocOnlyRefParameter, 1, 0 }; + + // Custom type marshalling in arrays (complex case with Value property) + yield return new object[] { CodeSnippets.ArrayMarshallingWithCustomStructElementWithValueProperty, 5, 0 }; } [Theory] diff --git a/DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs b/DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs index 07fef3b9500e..c44f97ea528a 100644 --- a/DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs +++ b/DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs @@ -159,6 +159,7 @@ public static IEnumerable CodeSnippetsToCompile() yield return new[] { CodeSnippets.CustomStructMarshallingPinnableParametersAndModifiers }; yield return new[] { CodeSnippets.CustomStructMarshallingNativeTypePinnable }; yield return new[] { CodeSnippets.CustomStructMarshallingMarshalUsingParametersAndModifiers }; + yield return new[] { CodeSnippets.ArrayMarshallingWithCustomStructElement }; } [Theory] diff --git a/DllImportGenerator/DllImportGenerator/ArrayMarshallingCodeContext.cs b/DllImportGenerator/DllImportGenerator/ArrayMarshallingCodeContext.cs index 30e262e03e23..9e8d1ab5a200 100644 --- a/DllImportGenerator/DllImportGenerator/ArrayMarshallingCodeContext.cs +++ b/DllImportGenerator/DllImportGenerator/ArrayMarshallingCodeContext.cs @@ -22,6 +22,15 @@ internal sealed class ArrayMarshallingCodeContext : StubCodeContext public override bool StackSpaceUsable => false; + /// + /// Additional variables other than the {managedIdentifier} and {nativeIdentifier} variables + /// can be added to the stub to track additional state for the marshaller in the stub. + /// + /// + /// Currently, array 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. + /// public override bool CanUseAdditionalTemporaryState => false; /// diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs b/DllImportGenerator/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs index 033aa7b7d71a..2a7e54ac1b15 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs +++ b/DllImportGenerator/DllImportGenerator/Marshalling/BlittableArrayMarshaller.cs @@ -75,6 +75,13 @@ public override IEnumerable Generate(TypePositionInfo info, Stu } 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) { @@ -116,14 +123,15 @@ public override IEnumerable Generate(TypePositionInfo info, Stu GenericName(TypeNames.System_Span) .WithTypeArgumentList( TypeArgumentList( - SingletonSeparatedList( - GetElementTypeSyntax(info))))) + SingletonSeparatedList(spanElementTypeSyntax)))) .WithArgumentList( ArgumentList( SeparatedList( new []{ Argument( - IdentifierName(nativeIdentifier)), + CastExpression( + PointerType(spanElementTypeSyntax), + IdentifierName(nativeIdentifier))), Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, @@ -156,13 +164,15 @@ public override IEnumerable Generate(TypePositionInfo info, Stu GenericName(Identifier(TypeNames.System_Span), TypeArgumentList( SingletonSeparatedList( - GetElementTypeSyntax(info))))) + spanElementTypeSyntax)))) .WithArgumentList( ArgumentList( SeparatedList( new[]{ Argument( - IdentifierName(nativeIdentifier)), + CastExpression( + PointerType(spanElementTypeSyntax), + IdentifierName(nativeIdentifier))), Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/MarshallingGenerator.cs b/DllImportGenerator/DllImportGenerator/Marshalling/MarshallingGenerator.cs index d4c2409e61d8..d2dd817bd3ec 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/MarshallingGenerator.cs +++ b/DllImportGenerator/DllImportGenerator/Marshalling/MarshallingGenerator.cs @@ -182,16 +182,6 @@ public static IMarshallingGenerator Create( } return SafeHandle; - case { ManagedType: IArrayTypeSymbol { IsSZArray: true, ElementType: ITypeSymbol elementType }, MarshallingAttributeInfo: NoMarshallingInfo }: - return CreateArrayMarshaller(info, context, elementType, NoMarshallingInfo.Instance); - - case { ManagedType: IArrayTypeSymbol { IsSZArray: true, ElementType: ITypeSymbol elementType }, MarshallingAttributeInfo: ArrayMarshalAsInfo marshalAsInfo }: - if (marshalAsInfo.UnmanagedArrayType != UnmanagedArrayType.LPArray) - { - throw new MarshallingNotSupportedException(info, context); - } - return CreateArrayMarshaller(info, context, elementType, marshalAsInfo.CreateArraySubTypeMarshalAsInfo()); - // Marshalling in new model. // 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. @@ -205,11 +195,16 @@ public static IMarshallingGenerator Create( case { MarshallingAttributeInfo: GeneratedNativeMarshallingAttributeInfo(string nativeTypeName) }: return Forwarder; + // Cases that just match on type must come after the checks that match only on marshalling attribute info. + // The checks below do not account for generic marshalling overrides like [MarshalUsing], so those checks must come first. case { ManagedType: { SpecialType: SpecialType.System_Char } }: return CreateCharMarshaller(info, context); case { ManagedType: { SpecialType: SpecialType.System_String } }: return CreateStringMarshaller(info, context); + + case { ManagedType: IArrayTypeSymbol { IsSZArray: true, ElementType: ITypeSymbol elementType } }: + return CreateArrayMarshaller(info, context, elementType); case { ManagedType: { SpecialType: SpecialType.System_Void } }: return Forwarder; @@ -362,9 +357,17 @@ private static ExpressionSyntax GetNumElementsExpressionFromMarshallingInfo(Type return numElementsExpression; } - private static IMarshallingGenerator CreateArrayMarshaller(TypePositionInfo info, StubCodeContext context, ITypeSymbol elementType, MarshallingInfo elementMarshallingInfo) + private static IMarshallingGenerator CreateArrayMarshaller(TypePositionInfo info, StubCodeContext context, ITypeSymbol elementType) { - var elementMarshaller = Create(TypePositionInfo.CreateForType(elementType, elementMarshallingInfo), context); + var elementMarshallingInfo = info.MarshallingAttributeInfo switch + { + ArrayMarshalAsInfo(UnmanagedType.LPArray, _) marshalAs => marshalAs.ElementMarshallingInfo, + ArrayMarshallingInfo marshalInfo => marshalInfo.ElementMarshallingInfo, + NoMarshallingInfo _ => NoMarshallingInfo.Instance, + _ => throw new MarshallingNotSupportedException(info, context) + }; + + var elementMarshaller = Create(TypePositionInfo.CreateForType(elementType, elementMarshallingInfo), new ArrayMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context, false)); ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)); if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) { @@ -379,6 +382,14 @@ private static IMarshallingGenerator CreateArrayMarshaller(TypePositionInfo info private static IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo) { + if (marshalInfo.ValuePropertyType is not null && !context.CanUseAdditionalTemporaryState) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.ValuePropertyMarshallingRequiresAdditionalState + }; + } + // 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) diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs b/DllImportGenerator/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs index d4f9beebe406..95b7fa170766 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs +++ b/DllImportGenerator/DllImportGenerator/Marshalling/NonBlittableArrayMarshaller.cs @@ -95,16 +95,47 @@ public override IEnumerable Generate(TypePositionInfo info, Stu yield return statement; } + TypeSyntax spanElementTypeSyntax = GetNativeElementTypeSyntax(info); + if (spanElementTypeSyntax is PointerTypeSyntax) + { + // Pointers cannot be passed to generics, so use IntPtr for this case. + spanElementTypeSyntax = ParseTypeName("System.IntPtr"); + } + // Iterate through the elements of the array to marshal them var arraySubContext = new ArrayMarshallingCodeContext(context.CurrentStage, IndexerIdentifier, context, appendLocalManagedIdentifierSuffix: cacheManagedValue); yield return IfStatement(BinaryExpression(SyntaxKind.NotEqualsExpression, IdentifierName(managedLocal), LiteralExpression(SyntaxKind.NullLiteralExpression)), + Block( + // new Span(, .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) }, - arraySubContext))))); + List(_elementMarshaller.Generate(info with { ManagedType = GetElementTypeSymbol(info) }, arraySubContext)))))); } break; case StubCodeContext.Stage.Unmarshal: diff --git a/DllImportGenerator/DllImportGenerator/MarshallingAttributeInfo.cs b/DllImportGenerator/DllImportGenerator/MarshallingAttributeInfo.cs index 40998a3ba64f..891eed70a4ac 100644 --- a/DllImportGenerator/DllImportGenerator/MarshallingAttributeInfo.cs +++ b/DllImportGenerator/DllImportGenerator/MarshallingAttributeInfo.cs @@ -60,20 +60,11 @@ enum UnmanagedArrayType /// internal sealed record ArrayMarshalAsInfo( UnmanagedArrayType UnmanagedArrayType, - UnmanagedType UnmanagedArraySubType, int ArraySizeConst, short ArraySizeParamIndex, - CharEncoding CharEncoding) : MarshalAsInfo((UnmanagedType)UnmanagedArrayType, CharEncoding) - { - public MarshallingInfo CreateArraySubTypeMarshalAsInfo() - { - if (UnmanagedArraySubType == (UnmanagedType)UnspecifiedData) - { - return NoMarshallingInfo.Instance; - } - return new MarshalAsInfo(UnmanagedArraySubType, CharEncoding); - } - + CharEncoding CharEncoding, + MarshallingInfo ElementMarshallingInfo) : MarshalAsInfo((UnmanagedType)UnmanagedArrayType, CharEncoding) + { public const short UnspecifiedData = -1; } @@ -113,5 +104,10 @@ internal sealed record GeneratedNativeMarshallingAttributeInfo( /// The type of the element is a SafeHandle-derived type with no marshalling attributes. /// internal sealed record SafeHandleMarshallingInfo : MarshallingInfo; - + + + /// + /// Default marshalling for arrays + /// + internal sealed record ArrayMarshallingInfo(MarshallingInfo ElementMarshallingInfo) : MarshallingInfo; } diff --git a/DllImportGenerator/DllImportGenerator/Resources.Designer.cs b/DllImportGenerator/DllImportGenerator/Resources.Designer.cs index 2123b4d92ff1..d7e9b3f4bc20 100644 --- a/DllImportGenerator/DllImportGenerator/Resources.Designer.cs +++ b/DllImportGenerator/DllImportGenerator/Resources.Designer.cs @@ -508,6 +508,15 @@ internal static string TypeNotSupportedTitle { } } + /// + /// Looks up a localized string similar to Marshalling a value between managed and native with a native type with a 'Value' property requires extra state, which is not supported in this context.. + /// + internal static string ValuePropertyMarshallingRequiresAdditionalState { + get { + return ResourceManager.GetString("ValuePropertyMarshallingRequiresAdditionalState", resourceCulture); + } + } + /// /// Looks up a localized string similar to The native type's 'Value' property must have a getter to support marshalling from managed to native.. /// diff --git a/DllImportGenerator/DllImportGenerator/Resources.resx b/DllImportGenerator/DllImportGenerator/Resources.resx index 5e4d1bc8c9c0..a524a25381e8 100644 --- a/DllImportGenerator/DllImportGenerator/Resources.resx +++ b/DllImportGenerator/DllImportGenerator/Resources.resx @@ -273,6 +273,9 @@ Specified type is not supported by source-generated P/Invokes + + Marshalling a value between managed and native with a native type with a 'Value' property requires extra state, which is not supported in this context. + The native type's 'Value' property must have a getter to support marshalling from managed to native. diff --git a/DllImportGenerator/DllImportGenerator/StubCodeContext.cs b/DllImportGenerator/DllImportGenerator/StubCodeContext.cs index ea84f868b16a..3ddef8d5e331 100644 --- a/DllImportGenerator/DllImportGenerator/StubCodeContext.cs +++ b/DllImportGenerator/DllImportGenerator/StubCodeContext.cs @@ -62,10 +62,26 @@ public enum Stage public Stage CurrentStage { get; protected set; } = Stage.Invalid; + /// + /// A fixed statement can be used on an individual value and the pointer + /// can be passed to native code. + /// public abstract bool PinningSupported { get; } + /// + /// Memory can be allocated via the stackalloc keyword and will live through + /// the full native context of the call. + /// public abstract bool StackSpaceUsable { get; } + /// + /// Additional variables other than the {managedIdentifier} and {nativeIdentifier} variables + /// can be added to the stub to track additional state for the marshaller in the stub. + /// + /// + /// In scenarios where the stub is defined within a single function, additional local variables + /// can be defined. + /// public abstract bool CanUseAdditionalTemporaryState { get; } protected const string GeneratedNativeIdentifierSuffix = "_gen_native"; diff --git a/DllImportGenerator/DllImportGenerator/TypePositionInfo.cs b/DllImportGenerator/DllImportGenerator/TypePositionInfo.cs index bb9cfde5b81b..92e909b62ff3 100644 --- a/DllImportGenerator/DllImportGenerator/TypePositionInfo.cs +++ b/DllImportGenerator/DllImportGenerator/TypePositionInfo.cs @@ -105,7 +105,7 @@ private static MarshallingInfo GetMarshallingInfo(ITypeSymbol type, IEnumerable< if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute), attributeClass)) { // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute - return CreateMarshalAsInfo(attrData, defaultInfo, diagnostics); + return CreateMarshalAsInfo(type, attrData, defaultInfo, compilation, diagnostics); } else if (SymbolEqualityComparer.Default.Equals(compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute), attributeClass)) { @@ -135,7 +135,7 @@ private static MarshallingInfo GetMarshallingInfo(ITypeSymbol type, IEnumerable< // If the type doesn't have custom attributes that dictate marshalling, // then consider the type itself. - if (TryCreateTypeBasedMarshallingInfo(type, compilation, out MarshallingInfo infoMaybe)) + if (TryCreateTypeBasedMarshallingInfo(type, defaultInfo, compilation, diagnostics, out MarshallingInfo infoMaybe)) { return infoMaybe; } @@ -151,7 +151,7 @@ private static MarshallingInfo GetMarshallingInfo(ITypeSymbol type, IEnumerable< return NoMarshallingInfo.Instance; - static MarshalAsInfo CreateMarshalAsInfo(AttributeData attrData, DefaultMarshallingInfo defaultInfo, GeneratorDiagnostics diagnostics) + static MarshalAsInfo CreateMarshalAsInfo(ITypeSymbol type, AttributeData attrData, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics) { object unmanagedTypeObj = attrData.ConstructorArguments[0].Value!; UnmanagedType unmanagedType = unmanagedTypeObj is short @@ -208,15 +208,28 @@ static MarshalAsInfo CreateMarshalAsInfo(AttributeData attrData, DefaultMarshall } } - return isArrayType - ? new ArrayMarshalAsInfo( - UnmanagedArrayType: (UnmanagedArrayType)unmanagedType, - UnmanagedArraySubType: unmanagedArraySubType, - ArraySizeConst: arraySizeConst, - ArraySizeParamIndex: arraySizeParamIndex, - CharEncoding: defaultInfo.CharEncoding - ) - : new MarshalAsInfo(unmanagedType, defaultInfo.CharEncoding); + 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(), defaultInfo, compilation, diagnostics); + } + + 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) @@ -262,7 +275,7 @@ static NativeMarshallingAttributeInfo CreateNativeMarshallingInfo(ITypeSymbol ty NativeTypePinnable: ManualTypeMarshallingHelper.FindGetPinnableReference(nativeType) is not null); } - static bool TryCreateTypeBasedMarshallingInfo(ITypeSymbol type, Compilation compilation, out MarshallingInfo marshallingInfo) + static bool TryCreateTypeBasedMarshallingInfo(ITypeSymbol type, DefaultMarshallingInfo defaultInfo, Compilation compilation, GeneratorDiagnostics diagnostics, out MarshallingInfo marshallingInfo) { var conversion = compilation.ClassifyCommonConversion(type, compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_SafeHandle)!); if (conversion.Exists @@ -273,6 +286,12 @@ static bool TryCreateTypeBasedMarshallingInfo(ITypeSymbol type, Compilation comp marshallingInfo = new SafeHandleMarshallingInfo(); return true; } + + if (type is IArrayTypeSymbol { ElementType: ITypeSymbol elementType }) + { + marshallingInfo = new ArrayMarshallingInfo(GetMarshallingInfo(elementType, Array.Empty(), defaultInfo, compilation, diagnostics)); + return true; + } marshallingInfo = NoMarshallingInfo.Instance; return false; } diff --git a/DllImportGenerator/TestAssets/NativeExports/Arrays.cs b/DllImportGenerator/TestAssets/NativeExports/Arrays.cs index 97d09ef53e66..c48e1241cc07 100644 --- a/DllImportGenerator/TestAssets/NativeExports/Arrays.cs +++ b/DllImportGenerator/TestAssets/NativeExports/Arrays.cs @@ -1,3 +1,4 @@ +using SharedTypes; using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -120,5 +121,18 @@ public static void Append(int** values, int numOriginalValues, int newValue) newArray[numOriginalValues] = newValue; *values = newArray; } + + [UnmanagedCallersOnly(EntryPoint = "and_all_members")] + [DNNE.C99DeclCode("struct bool_struct;")] + public static byte AndAllMembers([DNNE.C99Type("struct bool_struct*")] BoolStructNative* pArray, int length) + { + bool result = true; + for (int i = 0; i < length; i++) + { + BoolStruct managed = pArray[i].ToManaged(); + result &= managed.b1 && managed.b2 && managed.b3; + } + return result ? 1 : 0; + } } } \ No newline at end of file diff --git a/DllImportGenerator/designs/Compatibility.md b/DllImportGenerator/designs/Compatibility.md index cea5adb2d918..7579d77fff19 100644 --- a/DllImportGenerator/designs/Compatibility.md +++ b/DllImportGenerator/designs/Compatibility.md @@ -54,6 +54,8 @@ Specifying array-specific marshalling members on the `MarshalAsAttribute` such a Only single-dimensional arrays are supported for source generated marshalling. +Jagged arrays (arrays of arrays) are technically unsupported as was the case in the built-in marshalling system, but currently are not explicitly blocked by the source generator since they are not blocked at an architectural level, which was the case in the built-in system. + In the source-generated marshalling, arrays will be allocated on the stack (instead of through [`AllocCoTaskMem`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem)) if they are passed by value or by read-only reference if they contain at most 256 bytes of data. The built-in system does not support this optimization for arrays. ### `LCIDConversion` support