Skip to content
Draft
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 @@ -105,6 +105,16 @@ ExpressionSyntax GetExpressionForParam(TypePositionInfo paramInfo)

public abstract StatementSyntax GenerateUnmarshalStatement(StubIdentifierContext context);
public abstract StatementSyntax GenerateElementCleanupStatement(StubIdentifierContext context);

/// <summary>
/// <code>
/// &lt;numElements&gt; = &lt;GetManagedValuesSource&gt;.Length;
/// </code>
/// </summary>
public StatementSyntax GenerateNumElementsAssignmentFromManagedValuesSource(StubIdentifierContext context)
{
return CollectionSource.GetNumElementsAssignmentFromManagedValuesSource(CollectionSource.TypeInfo, context);
}
}

file static class ElementsMarshallingCollectionSourceExtensions
Expand Down Expand Up @@ -366,6 +376,7 @@ public override StatementSyntax GenerateManagedToUnmanagedByValueOutUnmarshalSta
public override StatementSyntax GenerateElementCleanupStatement(StubIdentifierContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(CollectionSource.TypeInfo, context);
string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(CollectionSource.TypeInfo, context);
ExpressionSyntax indexConstraintName;
if (!UsesLastIndexMarshalled(CollectionSource.TypeInfo, CollectionSource.CodeContext))
{
Expand Down Expand Up @@ -394,13 +405,32 @@ public override StatementSyntax GenerateElementCleanupStatement(StubIdentifierCo
return EmptyStatement();
}

// Declare the managed span so that nested element cleanup can access it via <managedSpan>[<index>]
// (e.g., when an inner stateless collection with NoCountInfo needs to compute its length from the
// managed source during ManagedToUnmanaged cleanup).
bool isManagedToUnmanaged = MarshallerHelpers.GetMarshalDirection(CollectionSource.TypeInfo, CollectionSource.CodeContext) == MarshalDirection.ManagedToUnmanaged;
LocalDeclarationStatementSyntax nativeSpanDeclaration = Declare(
ReadOnlySpanOf(unmanagedElementType),
nativeSpanIdentifier,
isManagedToUnmanaged
? CollectionSource.GetUnmanagedValuesDestination(context)
: CollectionSource.GetUnmanagedValuesSource(context));

if (isManagedToUnmanaged)
{
LocalDeclarationStatementSyntax managedSpanDeclaration = Declare(
ReadOnlySpanOf(elementMarshaller.TypeInfo.ManagedType.Syntax),
managedSpanIdentifier,
CollectionSource.GetManagedValuesSource(context));

return Block(
nativeSpanDeclaration,
managedSpanDeclaration,
contentsCleanupStatements);
}

return Block(
Declare(
ReadOnlySpanOf(unmanagedElementType),
nativeSpanIdentifier,
MarshallerHelpers.GetMarshalDirection(CollectionSource.TypeInfo, CollectionSource.CodeContext) == MarshalDirection.ManagedToUnmanaged
? CollectionSource.GetUnmanagedValuesDestination(context)
: CollectionSource.GetUnmanagedValuesSource(context)),
nativeSpanDeclaration,
contentsCleanupStatements);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,15 +581,9 @@ public IEnumerable<StatementSyntax> GenerateCleanupCallerAllocatedResourcesState
if (countInfo is NoCountInfo && MarshallerHelpers.GetMarshalDirection(TypeInfo, CodeContext) == MarshalDirection.ManagedToUnmanaged)
{
// When marshalling from managed to unmanaged, we may not have count info.
// For now, just set <numElements> to 0.
// See https://github.com/dotnet/runtime/issues/93423 for a tracking issue.

// <numElements> = 0;
yield return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(numElementsIdentifier),
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))));
// Use the managed collection's length to determine the number of elements to clean up.
// <numElements> = <GetManagedValuesSource>.Length;
yield return elementsMarshalling.GenerateNumElementsAssignmentFromManagedValuesSource(context);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,13 @@ public static IEnumerable<object[]> CustomCollectionsManagedToUnmanaged(Generato
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.NativeToManagedOnlyOutParameter<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.NativeToManagedOnlyReturnValue<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.NonBlittableElementByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.NonBlittableElementWithFreeByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.NonBlittableElementNativeToManagedOnlyOutParameter };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.NonBlittableElementNativeToManagedOnlyReturnValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NativeToManagedOnlyOutParameter<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NativeToManagedOnlyReturnValue<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementWithFreeByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementNativeToManagedOnlyOutParameter };
Comment on lines 316 to 324
yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementNativeToManagedOnlyReturnValue };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ public struct Native { }
public static Element ConvertToManaged(Native n) => throw null;
}
""";
public const string ElementInWithFree = """
[CustomMarshaller(typeof(Element), MarshalMode.ElementIn, typeof(ElementMarshaller))]
static class ElementMarshaller
{
public struct Native { }
public static Native ConvertToUnmanaged(Element e) => throw null;
public static Element ConvertToManaged(Native n) => throw null;
public static void Free(Native unmanaged) { }
}
""";
public const string ElementOut = """
[CustomMarshaller(typeof(Element), MarshalMode.ElementOut, typeof(ElementMarshaller))]
static class ElementMarshaller
Expand Down Expand Up @@ -252,6 +262,10 @@ public string NestedMarshallerParametersAndModifiers(string elementType) => _pro
+ NonBlittableElement
+ ElementIn;

public string NonBlittableElementWithFreeByValue => ByValue("Element")
+ NonBlittableElement
+ ElementInWithFree;

public string NonBlittableElementNativeToManagedOnlyOutParameter => NativeToManagedOnlyOutParameter("Element")
+ NonBlittableElement
+ ElementOut;
Expand Down Expand Up @@ -508,6 +522,10 @@ public string NativeToManagedFinallyOnlyReturnValue(string elementType) => _snip
+ NonBlittableElement
+ ElementIn;

public string NonBlittableElementWithFreeByValue => ByValue("Element")
+ NonBlittableElement
+ ElementInWithFree;

public string NonBlittableElementNativeToManagedOnlyOutParameter => NativeToManagedOnlyOutParameter("Element")
+ NonBlittableElement
+ ElementOut;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,33 @@ public void MultidimensionalArray_CheckOuterArrayIsIndexTracked()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/93423")]
public void MultidimensionalArray_CheckInnerArraysAreCleared()
{
var arr = GetMultiDimensionalArray<BoolStruct>(10, 10);
foreach (var throwOn in new int[] { 0, 1, 45, 99 })
{
BoolStructInMarshallerAllowNull.Marshaller.MarshallingFailsIndex = throwOn;
// https://github.com/dotnet/runtime/issues/93431
// We currently only clean up inner arrays that were fully marshalled
// (i.e. all elements in the outer collection up to the last fully completed inner array).
BoolStructInMarshallerAllowNull.Marshaller.ExpectedFreeCount = throwOn - throwOn % 10;
Assert.Throws<ArgumentException>(() =>
{
NativeExportsNE.MarshallingFails.MarshalMultidimensionalArray_CheckInnerArraysAreCleared(arr);
});
BoolStructInMarshallerAllowNull.Marshaller.AssertAllHaveBeenCleaned();
}
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/93431")]
public void MultidimensionalArray_CheckInnerArraysAreCleared_ProperCleanup()
{
var arr = GetMultiDimensionalArray<BoolStruct>(10, 10);
foreach (var throwOn in new int[] { 0, 1, 45, 99 })
{
BoolStructInMarshallerAllowNull.Marshaller.MarshallingFailsIndex = throwOn;
// Expected Behavior - Should free all elements of inner arrays that were partially marshalled
BoolStructInMarshallerAllowNull.Marshaller.ExpectedFreeCount = throwOn;
Assert.Throws<ArgumentException>(() =>
{
Expand Down Expand Up @@ -230,27 +250,20 @@ public void MultiDimensionalOutArray_EnsureAllCleaned()
// Set up unmarshalling asserts
BoolStructOutMarshaller.Marshaller.UnmarshallingFailsIndex = throwOn;
BoolStructOutMarshaller.Marshaller.ExpectedFreedValues = Enumerable.Range(0, 100).Select(_ => new BoolStructNative() { b1 = 1, b2 = 1, b3 = 1 }).ToArray();
// https://github.com/dotnet/runtime/issues/93423
//NegateBoolStructInMarshaller.Marshaller.ExpectedFreedValues = Enumerable.Range(0, 100).Select(_ => new BoolStructNative() { b1 = 0, b2 = 0, b3 = 0 }).ToArray();
BoolStructInMarshaller.Marshaller.ExpectedFreedValues = Enumerable.Range(0, 100).Select(_ => new BoolStructNative() { b1 = 0, b2 = 0, b3 = 0 }).ToArray();
Assert.Throws<ArgumentException>(() =>
{
NativeExportsNE.MarshallingFails.NegateBoolsOut2D(arr, arr.Length, widths, out BoolStruct[][] boolsOut);
});
// https://github.com/dotnet/runtime/issues/93423
//NegateBoolStructInMarshaller.Marshaller.AssertAllHaveBeenCleaned();
BoolStructInMarshaller.Marshaller.Reset();
BoolStructInMarshaller.Marshaller.AssertAllHaveBeenCleaned();
BoolStructOutMarshaller.Marshaller.AssertAllHaveBeenCleaned();
}
// Run without throwing - this is okay only because the native code doesn't actually use the array, it creates a whole new one
BoolStructOutMarshaller.Marshaller.UnmarshallingFailsIndex = -1;
BoolStructOutMarshaller.Marshaller.ExpectedFreedValues = Enumerable.Range(0, 100).Select(_ => new BoolStructNative() { b1 = 1, b2 = 1, b3 = 1 }).ToArray();
// https://github.com/dotnet/runtime/issues/93423
//NegateBoolStructInMarshaller.Marshaller.UnmarshallingFailsIndex = -1;
//NegateBoolStructInMarshaller.Marshaller.ExpectedFreeCount = 100;
BoolStructInMarshaller.Marshaller.ExpectedFreedValues = Enumerable.Range(0, 100).Select(_ => new BoolStructNative() { b1 = 0, b2 = 0, b3 = 0 }).ToArray();
NativeExportsNE.MarshallingFails.NegateBoolsOut2D(arr, arr.Length, widths, out BoolStruct[][] boolsOut);
// https://github.com/dotnet/runtime/issues/93423
//NegateBoolStructInMarshaller.Marshaller.AssertAllHaveBeenCleaned();
BoolStructInMarshaller.Marshaller.Reset();
BoolStructInMarshaller.Marshaller.AssertAllHaveBeenCleaned();
BoolStructOutMarshaller.Marshaller.AssertAllHaveBeenCleaned();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ public static IEnumerable<object[]> CustomCollections()
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.NativeToManagedFinallyOnlyReturnValue<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.NestedMarshallerParametersAndModifiers<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.NonBlittableElementByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.NonBlittableElementWithFreeByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.NonBlittableElementParametersAndModifiers };
Comment on lines 394 to 398
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.NonBlittableElementNativeToManagedOnlyOutParameter };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.NonBlittableElementNativeToManagedFinallyOnlyOutParameter };
Expand Down Expand Up @@ -432,6 +433,7 @@ public static IEnumerable<object[]> CustomCollections()
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateful.NativeToManagedOnlyReturnValue<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateful.NativeToManagedFinallyOnlyReturnValue<int>() };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateful.NonBlittableElementByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateful.NonBlittableElementWithFreeByValue };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateful.NonBlittableElementParametersAndModifiers };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateful.NonBlittableElementNativeToManagedOnlyOutParameter };
yield return new[] { ID(), customCollectionMarshallingCodeSnippets.Stateful.NonBlittableElementNativeToManagedFinallyOnlyOutParameter };
Expand Down
Loading