Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add handling for [In, Out] attributes. #380

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ 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 = "fill_range_array")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool FillRangeArray([Out] IntStructWrapper[] array, int length, int start);

[GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial bool DoubleValues([In, Out] IntStructWrapper[] array, int length);

[GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")]
[return:MarshalAs(UnmanagedType.U1)]
public static partial bool AndAllMembers(BoolStruct[] pArray, int length);
Expand Down Expand Up @@ -156,6 +163,29 @@ public void DynamicSizedArrayWithConstantComponent()
Assert.Equal(array.Concat(new [] { newValue }), newArray);
}

[Fact]
public void ArrayByValueOutParameter()
{
var testArray = new IntStructWrapper[10];
int start = 5;

NativeExportsNE.Arrays.FillRangeArray(testArray, testArray.Length, start);

Assert.Equal(Enumerable.Range(start, 10), testArray.Select(wrapper => wrapper.Value));
}

[Fact]
public void ArrayByValueInOutParameter()
{
var testValues = Enumerable.Range(42, 15).Select(i => new IntStructWrapper { Value = i });

var testArray = testValues.ToArray();

NativeExportsNE.Arrays.DoubleValues(testArray, testArray.Length);

Assert.Equal(testValues.Select(wrapper => wrapper.Value * 2), testArray.Select(wrapper => wrapper.Value));
}

[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down
14 changes: 14 additions & 0 deletions DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,20 @@ partial class Test

public static string BasicParametersAndModifiers<T>() => BasicParametersAndModifiers(typeof(T).ToString());

/// <summary>
/// Declaration with [In, Out] style attributes on a by-value parameter.
/// </summary>
public static string ByValueParameterWithModifier(string typeName, string attributeName) => @$"
using System.Runtime.InteropServices;
partial class Test
{{
[GeneratedDllImport(""DoesNotExist"")]
public static partial void Method(
[{attributeName}] {typeName} p);
}}";

public static string ByValueParameterWithModifier<T>(string attributeName) => ByValueParameterWithModifier(typeof(T).ToString(), attributeName);

/// <summary>
/// Declaration with parameters with MarshalAs.
/// </summary>
Expand Down
16 changes: 16 additions & 0 deletions DllImportGenerator/DllImportGenerator.UnitTests/CompileFails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()
// * UnmanagedType.CustomMarshaler, MarshalTypeRef, MarshalType, MarshalCookie
yield return new object[] { CodeSnippets.MarshalAsCustomMarshalerOnTypes, 16, 0 };

// Unsupported [In, Out] attributes usage
// Blittable array
yield return new object[] { CodeSnippets.ByValueParameterWithModifier<int[]>("Out"), 1, 0 };
yield return new object[] { CodeSnippets.ByValueParameterWithModifier<int[]>("In, Out"), 1, 0 };

// By ref with [In, Out] attributes
yield return new object[] { CodeSnippets.ByValueParameterWithModifier("in int", "In"), 1, 0 };
yield return new object[] { CodeSnippets.ByValueParameterWithModifier("ref int", "In"), 1, 0 };
yield return new object[] { CodeSnippets.ByValueParameterWithModifier("ref int", "In, Out"), 1, 0 };
yield return new object[] { CodeSnippets.ByValueParameterWithModifier("out int", "Out"), 1, 0 };

// By value non-array with [In, Out] attributes
yield return new object[] { CodeSnippets.ByValueParameterWithModifier<byte>("In"), 1, 0 };
yield return new object[] { CodeSnippets.ByValueParameterWithModifier<byte>("Out"), 1, 0 };
yield return new object[] { CodeSnippets.ByValueParameterWithModifier<byte>("In, Out"), 1, 0 };

jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
// Unsupported named arguments
// * BestFitMapping, ThrowOnUnmappableChar
yield return new object[] { CodeSnippets.AllDllImportNamedArguments, 2, 0 };
Expand Down
5 changes: 5 additions & 0 deletions DllImportGenerator/DllImportGenerator.UnitTests/Compiles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()
yield return new[] { CodeSnippets.ArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPUTF8Str) };
yield return new[] { CodeSnippets.ArrayParameterWithNestedMarshalInfo<string>(UnmanagedType.LPStr) };

// [In, Out] attributes
// By value non-blittable array
yield return new[] { CodeSnippets.ByValueParameterWithModifier<bool[]>("Out") };
yield return new[] { CodeSnippets.ByValueParameterWithModifier<bool[]>("In, Out") };
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved

// Enums
yield return new[] { CodeSnippets.EnumParameters };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,92 +101,116 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
yield return statement;
}

// new Span<T>(managedIdentifier).CopyTo(new Span<T>(nativeIdentifier, managedIdentifier.Length));
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
ObjectCreationExpression(
GenericName(Identifier(TypeNames.System_Span),
TypeArgumentList(
SingletonSeparatedList(
GetElementTypeSyntax(info)))))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(managedIdentifer))))),
IdentifierName("CopyTo")))
.WithArgumentList(
ArgumentList(
SingletonSeparatedList(
// new Span<T>(nativeIdentifier, managedIdentifier.Length)
var nativeSpan = ObjectCreationExpression(
GenericName(TypeNames.System_Span)
.WithTypeArgumentList(
TypeArgumentList(
SingletonSeparatedList(spanElementTypeSyntax))))
.WithArgumentList(
ArgumentList(
SeparatedList(
new []{
Argument(
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")))}))))))));
}
break;
case StubCodeContext.Stage.Unmarshal:
if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
{
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(nativeIdentifier),
LiteralExpression(SyntaxKind.NullLiteralExpression)),
Block(
// <managedIdentifier> = new <managedElementType>[<numElementsExpression>];
ExpressionStatement(
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
IdentifierName(managedIdentifer),
ArrayCreationExpression(
ArrayType(GetElementTypeSyntax(info),
SingletonList(ArrayRankSpecifier(
SingletonSeparatedList(_numElementsExpr))))))),
// new Span<T>(nativeIdentifier, managedIdentifier.Length).CopyTo(managedIdentifier);
BinaryExpression(SyntaxKind.NotEqualsExpression,
IdentifierName(managedIdentifer),
LiteralExpression(SyntaxKind.NullLiteralExpression)),
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")))}))),
ObjectCreationExpression(
GenericName(Identifier(TypeNames.System_Span),
TypeArgumentList(
SingletonSeparatedList(
spanElementTypeSyntax))))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(managedIdentifer))))),
IdentifierName("CopyTo")))
.WithArgumentList(
ArgumentList(
SingletonSeparatedList(
Argument(IdentifierName(managedIdentifer))))))),
ElseClause(
ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
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)))));
LiteralExpression(SyntaxKind.NullLiteralExpression)),
unmarshalContentsStatement);
}

}
break;
case StubCodeContext.Stage.Cleanup:
Expand Down Expand Up @@ -242,6 +266,11 @@ protected override ExpressionSyntax GenerateFreeExpression(TypePositionInfo info
ParseTypeName("System.IntPtr"),
IdentifierName(context.GetIdentifiers(info).native))))));
}

public override bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context)
{
return !context.PinningSupported && marshalKind.HasFlag(ByValueContentsMarshalKind.Out);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
{
return info.IsByRef && !info.IsManagedReturnPosition && !context.PinningSupported;
}

public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
}

}
Loading