Skip to content

Commit

Permalink
Add handling for [In, Out] attributes. (#380)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoritzinsky authored Jan 4, 2021
1 parent 9649d43 commit 18aa6f1
Show file tree
Hide file tree
Showing 28 changed files with 481 additions and 88 deletions.
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 };

// 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") };

// 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

0 comments on commit 18aa6f1

Please sign in to comment.