Skip to content

Commit

Permalink
Add support for null arrays in the blittable array marshaller's pinni…
Browse files Browse the repository at this point in the history
…ng optimization. (dotnet/runtimelab#1063)

* Add support for null arrays in the blittable array marshaller's pinning optimization.

* Add comment block about the behavior.

* Use pointer casting instead of Unsafe.NullRef since we are already using pointers.

Commit migrated from dotnet/runtimelab@c436fda
  • Loading branch information
jkoritzinsky authored May 4, 2021
1 parent a4e4279 commit 1e3f71c
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,58 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
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> = &MemoryMarshal.GetArrayDataReference(<managedIdentifer>))
// fixed (<nativeType> <nativeIdentifier> = &<byrefIdentifier>)
yield return FixedStatement(
VariableDeclaration(AsNativeType(info), SingletonSeparatedList(
VariableDeclarator(nativeIdentifier)
.WithInitializer(EqualsValueClause(
PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal),
IdentifierName("GetArrayDataReference")),
ArgumentList(
SingletonSeparatedList(Argument(IdentifierName(managedIdentifer)))
))))))),
IdentifierName(byRefIdentifier)))))),
EmptyStatement());
}
yield break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static string MarshalEx(AnalyzerConfigOptions options)
public const string System_Runtime_InteropServices_OutAttribute = "System.Runtime.InteropServices.OutAttribute";

public const string System_Runtime_InteropServices_InAttribute = "System.Runtime.InteropServices.InAttribute";

public const string System_Runtime_CompilerServices_SkipLocalsInitAttribute = "System.Runtime.CompilerServices.SkipLocalsInitAttribute";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,16 @@ public class ArrayTests
[Fact]
public void IntArrayMarshalledToNativeAsExpected()
{
var array = new [] { 1, 5, 79, 165, 32, 3 };
var array = new[] { 1, 5, 79, 165, 32, 3 };
Assert.Equal(array.Sum(), NativeExportsNE.Arrays.Sum(array, array.Length));
}

[Fact]
public void NullIntArrayMarshalledToNativeAsExpected()
{
Assert.Equal(-1, NativeExportsNE.Arrays.Sum(null, 0));
}

[Fact]
public void ZeroLengthArrayMarshalledAsNonNull()
{
Expand Down

0 comments on commit 1e3f71c

Please sign in to comment.