From bd4c73e79ed2db2548f0179e2cfa76dcc60cbea1 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:46:47 -0700 Subject: [PATCH 01/20] Add CleanupIn and CleanupOut --- .../JSExportCodeGenerator.cs | 12 +++++------ .../JSImportCodeGenerator.cs | 12 +++++------ .../ManagedToNativeVTableMethodGenerator.cs | 12 +++++------ .../UnmanagedToManagedStubGenerator.cs | 7 +++++-- .../PInvokeStubCodeGenerator.cs | 13 ++++++------ .../GeneratedStatements.cs | 20 ++++++++++++++++--- .../CustomTypeMarshallingGenerator.cs | 4 +++- .../Marshalling/ElementsMarshalling.cs | 6 ++++-- .../Marshalling/MarshallerHelpers.cs | 10 ++++++++++ .../Marshalling/SafeHandleMarshaller.cs | 2 +- ...nagedToManagedOwnershipTrackingStrategy.cs | 6 +++--- .../StubCodeContext.cs | 9 +++++++-- .../RcwAroundCcwTests.cs | 2 +- 13 files changed, 76 insertions(+), 39 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs index 9221bef508f3b..5a948c271fa49 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs @@ -61,13 +61,13 @@ public BlockSyntax GenerateJSExportBody() { StatementSyntax invoke = InvokeSyntax(); GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty; + bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupIn.IsEmpty || !statements.CleanupOut.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables); var setupStatements = new List(); SetupSyntax(setupStatements); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); } @@ -81,7 +81,7 @@ public BlockSyntax GenerateJSExportBody() tryStatements.Add(invoke); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -94,12 +94,12 @@ public BlockSyntax GenerateJSExportBody() List allStatements = setupStatements; List finallyStatements = new List(); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); } - finallyStatements.AddRange(statements.Cleanup); + finallyStatements.AddRange(statements.CleanupIn); if (finallyStatements.Count > 0) { allStatements.Add( diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs index 2c1d49fd3dd12..0d2b17dace229 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs @@ -67,14 +67,14 @@ public BlockSyntax GenerateJSImportBody() { StatementSyntax invoke = InvokeSyntax(); GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty; + bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupIn.IsEmpty || !statements.CleanupOut.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToUnmanaged(_marshallers, _context, shouldInitializeVariables); var setupStatements = new List(); BindSyntax(setupStatements); SetupSyntax(setupStatements); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); } @@ -88,7 +88,7 @@ public BlockSyntax GenerateJSImportBody() tryStatements.AddRange(statements.PinnedMarshal); tryStatements.Add(invoke); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -100,12 +100,12 @@ public BlockSyntax GenerateJSImportBody() List allStatements = setupStatements; List finallyStatements = new List(); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); } - finallyStatements.AddRange(statements.Cleanup); + finallyStatements.AddRange(statements.CleanupIn); if (finallyStatements.Count > 0) { // Add try-finally block if there are any statements in the finally block diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs index 5acdc3072a225..6c656c19d045d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs @@ -131,7 +131,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray().NestFixedStatements(fixedBlock)); // = true; - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -197,12 +197,12 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray allStatements = setupStatements; List finallyStatements = new List(); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); } - finallyStatements.AddRange(statements.Cleanup); + finallyStatements.AddRange(statements.CleanupIn); if (finallyStatements.Count > 0) { // Add try-finally block if there are any statements in the finally block diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs index 6d255912aedaa..76e77843e5079 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -54,9 +55,11 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke) _marshallers, _context, methodToInvoke); + Debug.Assert(statements.CleanupOut.IsEmpty); + bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty - || !statements.Cleanup.IsEmpty + || !statements.CleanupIn.IsEmpty || !statements.ManagedExceptionCatchClauses.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables); @@ -94,7 +97,7 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke) SyntaxList catchClauses = List(statements.ManagedExceptionCatchClauses); - finallyStatements.AddRange(statements.Cleanup); + finallyStatements.AddRange(statements.CleanupIn); if (finallyStatements.Count > 0) { allStatements.Add( diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs index 34d09a70a66e6..d55b100b664ff 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp; @@ -107,7 +108,7 @@ public PInvokeStubCodeGenerator( public BlockSyntax GeneratePInvokeBody(string dllImportName) { GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context, IdentifierName(dllImportName)); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty; + bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupIn.IsEmpty || !statements.CleanupOut.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToUnmanaged(_marshallers, _context, shouldInitializeVariables); var setupStatements = new List(); @@ -121,7 +122,7 @@ public BlockSyntax GeneratePInvokeBody(string dllImportName) initializeToDefault: false)); } - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); } @@ -148,7 +149,7 @@ public BlockSyntax GeneratePInvokeBody(string dllImportName) } tryStatements.Add(statements.Pin.NestFixedStatements(fixedBlock)); // = true; - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -160,12 +161,12 @@ public BlockSyntax GeneratePInvokeBody(string dllImportName) List allStatements = setupStatements; List finallyStatements = new List(); - if (!statements.GuaranteedUnmarshal.IsEmpty) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); } - finallyStatements.AddRange(statements.Cleanup); + finallyStatements.AddRange(statements.CleanupIn); if (finallyStatements.Count > 0) { // Add try-finally block if there are any statements in the finally block diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs index e8e3de05ffff2..8a3033a1a8fea 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs @@ -21,7 +21,8 @@ public struct GeneratedStatements public ImmutableArray Unmarshal { get; init; } public ImmutableArray NotifyForSuccessfulInvoke { get; init; } public ImmutableArray GuaranteedUnmarshal { get; init; } - public ImmutableArray Cleanup { get; init; } + public ImmutableArray CleanupIn { get; init; } + public ImmutableArray CleanupOut { get; init; } public ImmutableArray ManagedExceptionCatchClauses { get; init; } @@ -38,7 +39,8 @@ public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeCo .AddRange(GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Unmarshal })), NotifyForSuccessfulInvoke = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.NotifyForSuccessfulInvoke }), GuaranteedUnmarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.GuaranteedUnmarshal }), - Cleanup = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Cleanup }), + CleanupIn = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.CleanupIn }), + CleanupOut = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.CleanupOut }), ManagedExceptionCatchClauses = GenerateCatchClauseForManagedException(marshallers, context) }; } @@ -71,6 +73,17 @@ private static ImmutableArray GenerateStatementsForStubContext( ImmutableArray.Builder statementsToUpdate = ImmutableArray.CreateBuilder(); foreach (BoundGenerator marshaller in marshallers.SignatureMarshallers) { + // if (context.CurrentStage is StubCodeContext.Stage.CleanupIn or StubCodeContext.Stage.CleanupOut) + // { + // if (MarshallerHelpers.CleansUpInStage(marshaller.TypeInfo, context)) + // { + // context = context with { CurrentStage = StubCodeContext.Stage.CleanupIn }; + // } + // else + // { + // continue; + // } + // } statementsToUpdate.AddRange(marshaller.Generator.Generate(marshaller.TypeInfo, context)); } @@ -182,7 +195,8 @@ private static SyntaxTriviaList GenerateStageTrivia(StubCodeContext.Stage stage) StubCodeContext.Stage.Invoke => "Call the P/Invoke.", StubCodeContext.Stage.UnmarshalCapture => "Capture the native data into marshaller instances in case conversion to managed data throws an exception.", StubCodeContext.Stage.Unmarshal => "Convert native data to managed data.", - StubCodeContext.Stage.Cleanup => "Perform required cleanup.", + StubCodeContext.Stage.CleanupIn => "Perform required cleanup.", + StubCodeContext.Stage.CleanupOut => "Perform required cleanup.", StubCodeContext.Stage.NotifyForSuccessfulInvoke => "Keep alive any managed objects that need to stay alive across the call.", StubCodeContext.Stage.GuaranteedUnmarshal => "Convert native data to managed data even in the case of an exception during the non-cleanup phases.", _ => throw new ArgumentOutOfRangeException(nameof(stage)) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs index 7873781e475df..f2bb0ca0eb709 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs @@ -97,7 +97,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return _nativeTypeMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); } break; - case StubCodeContext.Stage.Cleanup: + case StubCodeContext.Stage.CleanupIn when MarshallerHelpers.CleansUpInStage(info, context): + return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); + case StubCodeContext.Stage.CleanupOut when MarshallerHelpers.CleansUpInStage(info, context): return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); default: break; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs index d457e37a7f2b2..03e8679db83e7 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs @@ -440,7 +440,8 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo indexConstraintName, _elementInfo, _elementMarshaller, - StubCodeContext.Stage.Cleanup); + StubCodeContext.Stage.CleanupIn, + StubCodeContext.Stage.CleanupOut); if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) { @@ -543,7 +544,8 @@ public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalState new FreeAlwaysOwnedOriginalValueGenerator(_elementMarshaller), StubCodeContext.Stage.Marshal, StubCodeContext.Stage.PinnedMarshal, - StubCodeContext.Stage.Cleanup)); + StubCodeContext.Stage.CleanupIn, + StubCodeContext.Stage.CleanupOut)); } private static List GenerateElementStages( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 86275796f208e..7ae2a4d7bb3f3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -394,5 +394,15 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo } throw new UnreachableException("An element is either a return value or passed by value or by ref."); } + public static bool CleansUpInStage(TypePositionInfo info, StubCodeContext context) + { + if (context.Direction is MarshalDirection.UnmanagedToManaged) + return context.CurrentStage is StubCodeContext.Stage.CleanupIn; + + if (GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) + return context.CurrentStage is StubCodeContext.Stage.CleanupOut; + + return context.CurrentStage is StubCodeContext.Stage.CleanupIn; + } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs index 4e0b3bcf40f92..2f9a277a93f2a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs @@ -214,7 +214,7 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont IdentifierName(newHandleObjectIdentifier))))); } break; - case StubCodeContext.Stage.Cleanup: + case StubCodeContext.Stage.CleanupIn: if (!info.IsManagedReturnPosition && (!info.IsByRef || info.RefKind == RefKind.In)) { yield return IfStatement( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs index 5b9d0866f743c..927c8b1785c57 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs @@ -78,7 +78,7 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf /// /// Marshalling strategy that uses the tracking variables introduced by to cleanup the original value if the original value is owned - /// in the stage. + /// in the stage. /// internal sealed class CleanupOwnedOriginalValueMarshalling : ICustomTypeMarshallingStrategy { @@ -119,7 +119,7 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i /// /// Marshalling strategy to cache the initial value of a given in a local variable and cleanup that value in the cleanup stage. - /// Useful in scenarios where the value is always owned in all code-paths that reach the stage, so additional ownership tracking is extraneous. + /// Useful in scenarios where the value is always owned in all code-paths that reach the stage, so additional ownership tracking is extraneous. /// internal sealed class FreeAlwaysOwnedOriginalValueGenerator : IMarshallingGenerator { @@ -138,7 +138,7 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return GenerateSetupStatements(); } - if (context.CurrentStage == StubCodeContext.Stage.Cleanup) + if (context.CurrentStage == StubCodeContext.Stage.CleanupIn) { return GenerateStatementsFromInner(new OwnedValueCodeContext(context)); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs index 35e07f1f7dc47..fb7cc299f083c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs @@ -64,9 +64,14 @@ public enum Stage NotifyForSuccessfulInvoke, /// - /// Perform any cleanup required + /// Perform any cleanup required on caller allocated resources /// - Cleanup, + CleanupIn, + + /// + /// Perform any cleanup required on callee allocated resources + /// + CleanupOut, /// /// Convert native data to managed data even in the case of an exception during diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index cca4aa23ea2a9..c2368fd5b229b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -338,7 +338,7 @@ public void Set( [GeneratedComClass] public partial class ICollectionMarshallingFailsImpl : ICollectionMarshallingFails { - int[] _data = new[] { 1, 2, 3 }; + int[] _data = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; public int[] Get() => _data; public void Set(int[] value) => _data = value; } From ad4f5d40d763f58d16c14653bcd389352819e7d6 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:53:33 -0700 Subject: [PATCH 02/20] Rename to CleanupCallerAllocated and CleanupCalleeAllocated --- .../JSExportCodeGenerator.cs | 16 ++++++------- .../JSImportCodeGenerator.cs | 12 +++++----- .../ManagedToNativeVTableMethodGenerator.cs | 12 +++++----- .../UnmanagedToManagedStubGenerator.cs | 6 ++--- .../PInvokeStubCodeGenerator.cs | 12 +++++----- .../GeneratedStatements.cs | 23 +++++-------------- .../CustomTypeMarshallingGenerator.cs | 4 ++-- .../Marshalling/ElementsMarshalling.cs | 8 +++---- .../Marshalling/MarshallerHelpers.cs | 6 ++--- .../Marshalling/SafeHandleMarshaller.cs | 2 +- ...nagedToManagedOwnershipTrackingStrategy.cs | 6 ++--- .../StubCodeContext.cs | 4 ++-- 12 files changed, 50 insertions(+), 61 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs index 5a948c271fa49..0a18241ef7c76 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using Microsoft.CodeAnalysis; namespace Microsoft.Interop.JavaScript { @@ -61,13 +61,13 @@ public BlockSyntax GenerateJSExportBody() { StatementSyntax invoke = InvokeSyntax(); GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupIn.IsEmpty || !statements.CleanupOut.IsEmpty; + bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupCallerAllocated.IsEmpty || !statements.CleanupCalleeAllocated.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables); var setupStatements = new List(); SetupSyntax(setupStatements); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); } @@ -81,7 +81,7 @@ public BlockSyntax GenerateJSExportBody() tryStatements.Add(invoke); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -94,12 +94,12 @@ public BlockSyntax GenerateJSExportBody() List allStatements = setupStatements; List finallyStatements = new List(); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupCalleeAllocated)))); } - finallyStatements.AddRange(statements.CleanupIn); + finallyStatements.AddRange(statements.CleanupCallerAllocated); if (finallyStatements.Count > 0) { allStatements.Add( diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs index 0d2b17dace229..1f415b8338008 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs @@ -67,14 +67,14 @@ public BlockSyntax GenerateJSImportBody() { StatementSyntax invoke = InvokeSyntax(); GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupIn.IsEmpty || !statements.CleanupOut.IsEmpty; + bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupCallerAllocated.IsEmpty || !statements.CleanupCalleeAllocated.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToUnmanaged(_marshallers, _context, shouldInitializeVariables); var setupStatements = new List(); BindSyntax(setupStatements); SetupSyntax(setupStatements); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); } @@ -88,7 +88,7 @@ public BlockSyntax GenerateJSImportBody() tryStatements.AddRange(statements.PinnedMarshal); tryStatements.Add(invoke); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -100,12 +100,12 @@ public BlockSyntax GenerateJSImportBody() List allStatements = setupStatements; List finallyStatements = new List(); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupCalleeAllocated)))); } - finallyStatements.AddRange(statements.CleanupIn); + finallyStatements.AddRange(statements.CleanupCallerAllocated); if (finallyStatements.Count > 0) { // Add try-finally block if there are any statements in the finally block diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs index 6c656c19d045d..f6439d4221142 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs @@ -131,7 +131,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray().NestFixedStatements(fixedBlock)); // = true; - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -197,12 +197,12 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray allStatements = setupStatements; List finallyStatements = new List(); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupCalleeAllocated)))); } - finallyStatements.AddRange(statements.CleanupIn); + finallyStatements.AddRange(statements.CleanupCallerAllocated); if (finallyStatements.Count > 0) { // Add try-finally block if there are any statements in the finally block diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs index 76e77843e5079..21887ac354d9a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs @@ -55,11 +55,11 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke) _marshallers, _context, methodToInvoke); - Debug.Assert(statements.CleanupOut.IsEmpty); + Debug.Assert(statements.CleanupCalleeAllocated.IsEmpty); bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty - || !statements.CleanupIn.IsEmpty + || !statements.CleanupCallerAllocated.IsEmpty || !statements.ManagedExceptionCatchClauses.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables); @@ -97,7 +97,7 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke) SyntaxList catchClauses = List(statements.ManagedExceptionCatchClauses); - finallyStatements.AddRange(statements.CleanupIn); + finallyStatements.AddRange(statements.CleanupCallerAllocated); if (finallyStatements.Count > 0) { allStatements.Add( diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs index d55b100b664ff..939a623ab7dba 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs @@ -108,7 +108,7 @@ public PInvokeStubCodeGenerator( public BlockSyntax GeneratePInvokeBody(string dllImportName) { GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context, IdentifierName(dllImportName)); - bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupIn.IsEmpty || !statements.CleanupOut.IsEmpty; + bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupCallerAllocated.IsEmpty || !statements.CleanupCalleeAllocated.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToUnmanaged(_marshallers, _context, shouldInitializeVariables); var setupStatements = new List(); @@ -122,7 +122,7 @@ public BlockSyntax GeneratePInvokeBody(string dllImportName) initializeToDefault: false)); } - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); } @@ -149,7 +149,7 @@ public BlockSyntax GeneratePInvokeBody(string dllImportName) } tryStatements.Add(statements.Pin.NestFixedStatements(fixedBlock)); // = true; - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(InvokeSucceededIdentifier), @@ -161,12 +161,12 @@ public BlockSyntax GeneratePInvokeBody(string dllImportName) List allStatements = setupStatements; List finallyStatements = new List(); - if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty)) + if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupOut)))); + finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal.Concat(statements.CleanupCalleeAllocated)))); } - finallyStatements.AddRange(statements.CleanupIn); + finallyStatements.AddRange(statements.CleanupCallerAllocated); if (finallyStatements.Count > 0) { // Add try-finally block if there are any statements in the finally block diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs index 8a3033a1a8fea..691309dde5487 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs @@ -21,8 +21,8 @@ public struct GeneratedStatements public ImmutableArray Unmarshal { get; init; } public ImmutableArray NotifyForSuccessfulInvoke { get; init; } public ImmutableArray GuaranteedUnmarshal { get; init; } - public ImmutableArray CleanupIn { get; init; } - public ImmutableArray CleanupOut { get; init; } + public ImmutableArray CleanupCallerAllocated { get; init; } + public ImmutableArray CleanupCalleeAllocated { get; init; } public ImmutableArray ManagedExceptionCatchClauses { get; init; } @@ -39,8 +39,8 @@ public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeCo .AddRange(GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Unmarshal })), NotifyForSuccessfulInvoke = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.NotifyForSuccessfulInvoke }), GuaranteedUnmarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.GuaranteedUnmarshal }), - CleanupIn = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.CleanupIn }), - CleanupOut = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.CleanupOut }), + CleanupCallerAllocated = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.CleanupCallerAllocated }), + CleanupCalleeAllocated = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.CleanupCalleeAllocated }), ManagedExceptionCatchClauses = GenerateCatchClauseForManagedException(marshallers, context) }; } @@ -73,17 +73,6 @@ private static ImmutableArray GenerateStatementsForStubContext( ImmutableArray.Builder statementsToUpdate = ImmutableArray.CreateBuilder(); foreach (BoundGenerator marshaller in marshallers.SignatureMarshallers) { - // if (context.CurrentStage is StubCodeContext.Stage.CleanupIn or StubCodeContext.Stage.CleanupOut) - // { - // if (MarshallerHelpers.CleansUpInStage(marshaller.TypeInfo, context)) - // { - // context = context with { CurrentStage = StubCodeContext.Stage.CleanupIn }; - // } - // else - // { - // continue; - // } - // } statementsToUpdate.AddRange(marshaller.Generator.Generate(marshaller.TypeInfo, context)); } @@ -195,8 +184,8 @@ private static SyntaxTriviaList GenerateStageTrivia(StubCodeContext.Stage stage) StubCodeContext.Stage.Invoke => "Call the P/Invoke.", StubCodeContext.Stage.UnmarshalCapture => "Capture the native data into marshaller instances in case conversion to managed data throws an exception.", StubCodeContext.Stage.Unmarshal => "Convert native data to managed data.", - StubCodeContext.Stage.CleanupIn => "Perform required cleanup.", - StubCodeContext.Stage.CleanupOut => "Perform required cleanup.", + StubCodeContext.Stage.CleanupCallerAllocated => "Perform required cleanup.", + StubCodeContext.Stage.CleanupCalleeAllocated => "Perform required cleanup.", StubCodeContext.Stage.NotifyForSuccessfulInvoke => "Keep alive any managed objects that need to stay alive across the call.", StubCodeContext.Stage.GuaranteedUnmarshal => "Convert native data to managed data even in the case of an exception during the non-cleanup phases.", _ => throw new ArgumentOutOfRangeException(nameof(stage)) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs index f2bb0ca0eb709..18b5bcce0813d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs @@ -97,9 +97,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return _nativeTypeMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); } break; - case StubCodeContext.Stage.CleanupIn when MarshallerHelpers.CleansUpInStage(info, context): + case StubCodeContext.Stage.CleanupCallerAllocated when MarshallerHelpers.CleansUpInStage(info, context): return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); - case StubCodeContext.Stage.CleanupOut when MarshallerHelpers.CleansUpInStage(info, context): + case StubCodeContext.Stage.CleanupCalleeAllocated when MarshallerHelpers.CleansUpInStage(info, context): return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); default: break; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs index 03e8679db83e7..c132fa2c75d66 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs @@ -440,8 +440,8 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo indexConstraintName, _elementInfo, _elementMarshaller, - StubCodeContext.Stage.CleanupIn, - StubCodeContext.Stage.CleanupOut); + StubCodeContext.Stage.CleanupCallerAllocated, + StubCodeContext.Stage.CleanupCalleeAllocated); if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) { @@ -544,8 +544,8 @@ public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalState new FreeAlwaysOwnedOriginalValueGenerator(_elementMarshaller), StubCodeContext.Stage.Marshal, StubCodeContext.Stage.PinnedMarshal, - StubCodeContext.Stage.CleanupIn, - StubCodeContext.Stage.CleanupOut)); + StubCodeContext.Stage.CleanupCallerAllocated, + StubCodeContext.Stage.CleanupCalleeAllocated)); } private static List GenerateElementStages( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 7ae2a4d7bb3f3..235013c72c956 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -397,12 +397,12 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo public static bool CleansUpInStage(TypePositionInfo info, StubCodeContext context) { if (context.Direction is MarshalDirection.UnmanagedToManaged) - return context.CurrentStage is StubCodeContext.Stage.CleanupIn; + return context.CurrentStage is StubCodeContext.Stage.CleanupCallerAllocated; if (GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) - return context.CurrentStage is StubCodeContext.Stage.CleanupOut; + return context.CurrentStage is StubCodeContext.Stage.CleanupCalleeAllocated; - return context.CurrentStage is StubCodeContext.Stage.CleanupIn; + return context.CurrentStage is StubCodeContext.Stage.CleanupCallerAllocated; } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs index 2f9a277a93f2a..9f4700ec00f93 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs @@ -214,7 +214,7 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont IdentifierName(newHandleObjectIdentifier))))); } break; - case StubCodeContext.Stage.CleanupIn: + case StubCodeContext.Stage.CleanupCallerAllocated: if (!info.IsManagedReturnPosition && (!info.IsByRef || info.RefKind == RefKind.In)) { yield return IfStatement( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs index 927c8b1785c57..c6b5c7685b027 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs @@ -78,7 +78,7 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf /// /// Marshalling strategy that uses the tracking variables introduced by to cleanup the original value if the original value is owned - /// in the stage. + /// in the stage. /// internal sealed class CleanupOwnedOriginalValueMarshalling : ICustomTypeMarshallingStrategy { @@ -119,7 +119,7 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i /// /// Marshalling strategy to cache the initial value of a given in a local variable and cleanup that value in the cleanup stage. - /// Useful in scenarios where the value is always owned in all code-paths that reach the stage, so additional ownership tracking is extraneous. + /// Useful in scenarios where the value is always owned in all code-paths that reach the stage, so additional ownership tracking is extraneous. /// internal sealed class FreeAlwaysOwnedOriginalValueGenerator : IMarshallingGenerator { @@ -138,7 +138,7 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return GenerateSetupStatements(); } - if (context.CurrentStage == StubCodeContext.Stage.CleanupIn) + if (context.CurrentStage == StubCodeContext.Stage.CleanupCallerAllocated) { return GenerateStatementsFromInner(new OwnedValueCodeContext(context)); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs index fb7cc299f083c..50af716eb3a51 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs @@ -66,12 +66,12 @@ public enum Stage /// /// Perform any cleanup required on caller allocated resources /// - CleanupIn, + CleanupCallerAllocated, /// /// Perform any cleanup required on callee allocated resources /// - CleanupOut, + CleanupCalleeAllocated, /// /// Convert native data to managed data even in the case of an exception during From ba4e7bc72b01ba3b827248a0038b29ac46effab8 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:55:16 -0700 Subject: [PATCH 03/20] Rename CleansUpInStage to ShouldCleanupInCurrentStage --- .../Marshalling/CustomTypeMarshallingGenerator.cs | 4 ++-- .../Marshalling/MarshallerHelpers.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs index 18b5bcce0813d..bf993d5f40aee 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs @@ -97,9 +97,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return _nativeTypeMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); } break; - case StubCodeContext.Stage.CleanupCallerAllocated when MarshallerHelpers.CleansUpInStage(info, context): + case StubCodeContext.Stage.CleanupCallerAllocated when MarshallerHelpers.ShouldCleanupInCurrentStage(info, context): return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); - case StubCodeContext.Stage.CleanupCalleeAllocated when MarshallerHelpers.CleansUpInStage(info, context): + case StubCodeContext.Stage.CleanupCalleeAllocated when MarshallerHelpers.ShouldCleanupInCurrentStage(info, context): return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); default: break; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 235013c72c956..80fa7d867c2ec 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -394,7 +394,8 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo } throw new UnreachableException("An element is either a return value or passed by value or by ref."); } - public static bool CleansUpInStage(TypePositionInfo info, StubCodeContext context) + + public static bool ShouldCleanupInCurrentStage(TypePositionInfo info, StubCodeContext context) { if (context.Direction is MarshalDirection.UnmanagedToManaged) return context.CurrentStage is StubCodeContext.Stage.CleanupCallerAllocated; From a0f63df50b0bc32ce25f89e5594655845d78dc51 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 1 Aug 2023 16:16:33 -0700 Subject: [PATCH 04/20] Add more info in stage description comment --- .../GeneratedStatements.cs | 4 ++-- .../tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs index 691309dde5487..d611eb59cd3b4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs @@ -184,8 +184,8 @@ private static SyntaxTriviaList GenerateStageTrivia(StubCodeContext.Stage stage) StubCodeContext.Stage.Invoke => "Call the P/Invoke.", StubCodeContext.Stage.UnmarshalCapture => "Capture the native data into marshaller instances in case conversion to managed data throws an exception.", StubCodeContext.Stage.Unmarshal => "Convert native data to managed data.", - StubCodeContext.Stage.CleanupCallerAllocated => "Perform required cleanup.", - StubCodeContext.Stage.CleanupCalleeAllocated => "Perform required cleanup.", + StubCodeContext.Stage.CleanupCallerAllocated => "Perform cleanup of caller allocated resources.", + StubCodeContext.Stage.CleanupCalleeAllocated => "Perform cleanup of callee allocated resources.", StubCodeContext.Stage.NotifyForSuccessfulInvoke => "Keep alive any managed objects that need to stay alive across the call.", StubCodeContext.Stage.GuaranteedUnmarshal => "Convert native data to managed data even in the case of an exception during the non-cleanup phases.", _ => throw new ArgumentOutOfRangeException(nameof(stage)) diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs index 54669fa6c380e..fb1c6ebba8b3a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs @@ -2,19 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; namespace SharedTypes.ComInterfaces { - [GeneratedComInterface] + [GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper)] [Guid(_guid)] internal partial interface IDerived : IGetAndSetInt { void SetName([MarshalUsing(typeof(Utf16StringMarshaller))] string name); - [return:MarshalUsing(typeof(Utf16StringMarshaller))] + [return: MarshalUsing(typeof(Utf16StringMarshaller))] string GetName(); internal new const string _guid = "7F0DB364-3C04-4487-9193-4BB05DC7B654"; From b4bdfb704562024cc354c64d7e304198c0d1ea59 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 2 Aug 2023 10:36:56 -0700 Subject: [PATCH 05/20] Add test types --- .../RcwAroundCcwTests.cs | 363 ++++------------- .../ComInterfaceGenerator/ArrayMarshalling.cs | 7 +- .../ComInterfaceGenerator/GetAndSetInt.cs | 9 +- .../StringMarshalling.cs | 370 +++++++++--------- .../StringMarshallingOverride.cs | 10 +- .../IArrayOfStatelessElements.cs | 75 ++++ .../SharedTypes/ComInterfaces/IBool.cs | 26 ++ .../ICustomStringMarshallingUtf16.cs | 4 +- .../SharedTypes/ComInterfaces/IDerived.cs | 6 +- .../SharedTypes/ComInterfaces/IEmpty.cs | 4 +- .../SharedTypes/ComInterfaces/IFloat.cs | 25 ++ .../ComInterfaces/IGetAndSetInt.cs | 4 +- .../SharedTypes/ComInterfaces/IGetIntArray.cs | 4 +- .../SharedTypes/ComInterfaces/IInt.cs | 47 +++ .../SharedTypes/ComInterfaces/IIntArray.cs | 72 ++++ .../SharedTypes/ComInterfaces/IInterface.cs | 51 +++ .../ComInterfaces/IJaggedIntArray.cs | 60 +++ .../SharedTypes/ComInterfaces/IRefStrings.cs | 18 + .../SharedTypes/ComInterfaces/ISafeHandles.cs | 19 + .../IStatefulCallerAllocatedBuffer.cs | 58 +++ .../IStatefulCollectionBlittableElement.cs | 30 ++ .../IStatefulCollectionStatelessElement.cs | 57 +++ .../IStatefulFinallyMarshalling.cs | 56 +++ .../ComInterfaces/IStatefulMarshalling.cs | 57 +++ .../IStatefulPinnedMarshalling.cs | 61 +++ ...tatelessCallerAllocateBufferMarshalling.cs | 53 +++ .../IStatelessCollectionBlittableElement.cs | 30 ++ .../IStatelessCollectionStatelessElement.cs | 80 ++++ .../IStatelessFinallyMarshalling.cs | 50 +++ .../ComInterfaces/IStatelessMarshalling.cs | 50 +++ .../IStatelessPinnedMarshalling.cs | 62 +++ .../IStringMarshallingOverride.cs | 8 +- .../IStringMarshallingOverrideDerived.cs | 10 +- .../ComInterfaces/IUTF16Marshalling.cs | 4 +- .../ComInterfaces/IUTF8Marshalling.cs | 4 +- .../ICollectionMarshallingFails.cs | 47 +++ .../IJaggedIntArrayMarshallingFails.cs | 80 ++++ .../IStringArrayMarshallingFails.cs | 2 +- .../MarshallingFailureException.cs | 13 + .../ThrowOn4thElementMarshalled.cs | 37 ++ 40 files changed, 1509 insertions(+), 514 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index c2368fd5b229b..a820e032e798f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -7,7 +7,6 @@ using SharedTypes.ComInterfaces; using SharedTypes.ComInterfaces.MarshallingFails; using Xunit; -using static ComInterfaceGenerator.Tests.ComInterfaces; namespace ComInterfaceGenerator.Tests { @@ -23,17 +22,26 @@ public partial class RcwAroundCcwTests } [Fact] - public void IGetAndSetInt() - { - var obj = CreateWrapper(); - obj.SetInt(1); - Assert.Equal(1, obj.GetInt()); + public void IInt() + { + var obj = CreateWrapper(); + obj.Set(1); + Assert.Equal(1, obj.Get()); + var local = 4; + obj.SwapRef(ref local); + Assert.Equal(1, local); + Assert.Equal(4, obj.Get()); + local = 2; + obj.SetIn(in local); + local = 0; + obj.GetOut(out local); + Assert.Equal(2, local); } [Fact] public void IDerived() { - var obj = CreateWrapper(); + IDerived obj = CreateWrapper(); obj.SetInt(1); Assert.Equal(1, obj.GetInt()); obj.SetName("A"); @@ -63,10 +71,22 @@ public void IIntArray() var obj = CreateWrapper(); int[] data = new int[] { 1, 2, 3 }; int length = data.Length; - obj.Set(data, length); - Assert.Equal(data, obj.Get(out int _)); - obj.Get2(out var value); + obj.SetContents(data, length); + Assert.Equal(data, obj.GetReturn(out int _)); + obj.GetOut(out var value); Assert.Equal(data, value); + obj.SwapArray(ref data, data.Length); + obj.PassIn(in data, data.Length); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/89265")] + public void IIntArray_Failing() + { + var obj = CreateWrapper(); + int[] data = new int[] { 1, 2, 3 }; + obj.Double(data, data.Length); + Assert.True(data is [2, 4, 6]); } [Fact] @@ -89,36 +109,49 @@ public void IInterface() { var iint = CreateWrapper(); var obj = CreateWrapper(); - obj.Set(iint); + obj.SetInt(iint); _ = obj.Get(); + obj.SwapRef(ref iint); + obj.InInt(in iint); + obj.GetOut(out var _); } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/89747")] public void ICollectionMarshallingFails() { var obj = CreateWrapper(); - Assert.Throws(() => - _ = obj.Get() + Assert.Throws(() => + _ = obj.GetConstSize() ); - Assert.Throws(() => - obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }) + + Assert.Throws(() => + _ = obj.Get(out _) + ); + + Assert.Throws(() => + obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, 10) ); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/88111")] [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/89747")] public void IJaggedArrayMarshallingFails() { var obj = CreateWrapper(); - Assert.Throws(() => + Assert.Throws(() => + _ = obj.GetConstSize() + ); + + Assert.Throws(() => _ = obj.Get(out _, out _) ); var array = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5, }, new int[] { 6, 7, 8, 9 } }; - var length = 3; var widths = new int[] { 3, 2, 4 }; - Assert.Throws(() => + var length = 3; + Assert.Throws(() => obj.Set(array, widths, length) ); } @@ -131,291 +164,67 @@ public void IStringArrayMarshallingFails() var strings = IStringArrayMarshallingFailsImpl.StartingStrings; // All of these will marshal either to COM or the CCW will marshal on the return - Assert.Throws(() => + Assert.Throws(() => { obj.Param(strings); }); - Assert.Throws(() => + Assert.Throws(() => { obj.RefParam(ref strings); }); - Assert.Throws(() => + Assert.Throws(() => { obj.InParam(in strings); }); - Assert.Throws(() => - { - obj.OutParam(out strings); - }); - // https://github.com/dotnet/runtime/issues/87845 - //Assert.Throws(() => - //{ - // obj.ByValueOutParam(strings); - //}); - Assert.Throws(() => + Assert.Throws(() => { obj.ByValueInOutParam(strings); }); - Assert.Throws(() => - { - _ = obj.ReturnValue(); - }); } - } - public static partial class ComInterfaces - { - [GeneratedComInterface] - [Guid("EE6D1F2A-3418-4317-A87C-35488F6546AB")] - internal partial interface IInt + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows))] + public void IStringArrayMarshallingFailsOutWindows() { - public int Get(); - public void Set(int value); - } - - [GeneratedComClass] - internal partial class IIntImpl : IInt - { - int _data; - public int Get() => _data; - public void Set(int value) => _data = value; - } - - [GeneratedComInterface] - [Guid("5A9D3ED6-CC17-4FB9-8F82-0070489B7213")] - internal partial interface IBool - { - [return: MarshalAs(UnmanagedType.I1)] - bool Get(); - void Set([MarshalAs(UnmanagedType.I1)] bool value); - } - - [GeneratedComClass] - internal partial class IBoolImpl : IBool - { - bool _data; - public bool Get() => _data; - public void Set(bool value) => _data = value; - } - - [GeneratedComInterface] - [Guid("9FA4A8A9-2D8F-48A8-B6FB-B44B5F1B9FB6")] - internal partial interface IFloat - { - float Get(); - void Set(float value); - } - - [GeneratedComClass] - internal partial class IFloatImpl : IFloat - { - float _data; - public float Get() => _data; - public void Set(float value) => _data = value; - } - - [GeneratedComInterface] - [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] - internal partial interface IIntArray - { - [return: MarshalUsing(CountElementName = nameof(size))] - int[] Get(out int size); - int Get2([MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out int[] array); - void Set([MarshalUsing(CountElementName = nameof(size))] int[] array, int size); - } - - [GeneratedComClass] - internal partial class IIntArrayImpl : IIntArray - { - int[] _data; - public int[] Get(out int size) - { - size = _data.Length; - return _data; - } - public int Get2(out int[] array) - { - array = _data; - return array.Length; - } - public void Set(int[] array, int size) - { - _data = array; - } - } - - [GeneratedComInterface] - [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] - internal partial interface IJaggedIntArray - { - [return: MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - int[][] Get( - [MarshalUsing(CountElementName = nameof(length))] - out int[] widths, - out int length); - - int Get2( - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - out int[][] array, - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] - out int[] widths); - - void Set( - [MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - int[][] array, - [MarshalUsing(CountElementName = nameof(length))] - int[] widths, - int length); - } - - [GeneratedComClass] - internal partial class IJaggedIntArrayImpl : IJaggedIntArray - { - int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; - int[] _widths = new int[] { 3, 2, 4 }; - public int[][] Get(out int[] widths, out int length) - { - widths = _widths; - length = _data.Length; - return _data; - } - public int Get2(out int[][] array, out int[] widths) - { - array = _data; - widths = _widths; - return array.Length; - } - public void Set(int[][] array, int[] widths, int length) - { - _data = array; - _widths = widths; - } - } - - [CustomMarshaller(typeof(int), MarshalMode.ElementIn, typeof(ThrowOn4thElementMarshalled))] - [CustomMarshaller(typeof(int), MarshalMode.ElementOut, typeof(ThrowOn4thElementMarshalled))] - internal static class ThrowOn4thElementMarshalled - { - static int _marshalledCount = 0; - static int _unmarshalledCount = 0; - public static nint ConvertToUnmanaged(int managed) + var obj = CreateWrapper(); + var strings = IStringArrayMarshallingFailsImpl.StartingStrings; + // This will fail in the native side and throw for HR on the managed to unmanaged stub. In Windows environments, this is will unwrap the exception. + Assert.Throws(() => { - if (_marshalledCount++ == 3) - { - _marshalledCount = 0; - throw new ArgumentException("The element was the 4th element (with 0-based index 3)"); - } - return managed; - } - - public static int ConvertToManaged(nint unmanaged) + obj.OutParam(out strings); + }); + Assert.Throws(() => { - if (_unmarshalledCount++ == 3) - { - _unmarshalledCount = 0; - throw new ArgumentException("The element was the 4th element (with 0-based index 3)"); - } - return (int)unmanaged; - } - } - - [GeneratedComInterface] - [Guid("A4857395-06FB-4A6E-81DB-35461BE999C5")] - internal partial interface ICollectionMarshallingFails - { - [return: MarshalUsing(ConstantElementCount = 10)] - [return: MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] - public int[] Get(); - public void Set( - [MarshalUsing(ConstantElementCount = 10)] - [MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] - int[] value); - } - - [GeneratedComClass] - public partial class ICollectionMarshallingFailsImpl : ICollectionMarshallingFails - { - int[] _data = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - public int[] Get() => _data; - public void Set(int[] value) => _data = value; - } - - [GeneratedComInterface] - [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] - internal partial interface IJaggedIntArrayMarshallingFails - { - [return: MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), - MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - int[][] Get( - [MarshalUsing(CountElementName = nameof(length))] - out int[] widths, - out int length); - - int Get2( - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), - MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - out int[][] array, - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] - out int[] widths); - - void Set( - [MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), - MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - int[][] array, - [MarshalUsing(CountElementName = nameof(length))] - int[] widths, - int length); + _ = obj.ReturnValue(); + }); } - [GeneratedComClass] - internal partial class IJaggedIntArrayMarshallingFailsImpl : IJaggedIntArrayMarshallingFails + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows))] + public void IStringArrayMarshallingFailsOutNonWindows() { - int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; - int[] _widths = new int[] { 3, 2, 4 }; - public int[][] Get(out int[] widths, out int length) - { - widths = _widths; - length = _data.Length; - return _data; - } - public int Get2(out int[][] array, out int[] widths) + var obj = CreateWrapper(); + var strings = IStringArrayMarshallingFailsImpl.StartingStrings; + // This will fail in the native side and throw for HR on the managed to unmanaged stub. In non-Windows environments, this is a plain Exception. + Assert.Throws(() => { - array = _data; - widths = _widths; - return array.Length; - } - public void Set(int[][] array, int[] widths, int length) + obj.OutParam(out strings); + }); + Assert.Throws(() => { - _data = array; - _widths = widths; - } + _ = obj.ReturnValue(); + }); } - [GeneratedComInterface] - [Guid("A4857398-06FB-4A6E-81DB-35461BE999C5")] - internal partial interface IInterface + [ActiveIssue("https://github.com/dotnet/runtime/issues/87845")] + [Fact] + public void IStringArrayMarshallingFails_Failing() { - public IInt Get(); - public void Set(IInt value); - } + var obj = CreateWrapper(); - [GeneratedComClass] - public partial class IInterfaceImpl : IInterface - { - IInt _data = new IIntImpl(); - IInt IInterface.Get() => _data; - void IInterface.Set(IInt value) + var strings = IStringArrayMarshallingFailsImpl.StartingStrings; + Assert.Throws(() => { - int x = value.Get(); - value.Set(x); - _data = value; - } + obj.ByValueOutParam(strings); + }); } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/ArrayMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/ArrayMarshalling.cs index c8f2292a6985c..ac900cd202d2c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/ArrayMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/ArrayMarshalling.cs @@ -3,13 +3,8 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; -using System.Text; -using System.Threading.Tasks; using SharedTypes.ComInterfaces; using static System.Runtime.InteropServices.ComWrappers; @@ -53,7 +48,7 @@ static void* GetIntArrayVTable if (obj is ImplementingObject) { ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ImplementingObject), sizeof(ComInterfaceEntry)); - comInterfaceEntry->IID = new Guid(IGetIntArray._guid); + comInterfaceEntry->IID = new Guid(IGetIntArray.IID); comInterfaceEntry->Vtable = (nint)GetIntArrayVTable; count = 1; return comInterfaceEntry; diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetAndSetInt.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetAndSetInt.cs index 66faa8c577e7c..c9a149e12798c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetAndSetInt.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetAndSetInt.cs @@ -3,15 +3,8 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; -using System.Runtime.InteropServices.ObjectiveC; -using System.Text; -using System.Threading.Tasks; using SharedTypes.ComInterfaces; using static System.Runtime.InteropServices.ComWrappers; @@ -55,7 +48,7 @@ static void* s_comInterface1VTable if (obj is ImplementingObject) { ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ImplementingObject), sizeof(ComInterfaceEntry)); - comInterfaceEntry->IID = new Guid(IGetAndSetInt._guid); + comInterfaceEntry->IID = new Guid(IGetAndSetInt.IID); comInterfaceEntry->Vtable = (nint)s_comInterface1VTable; count = 1; return comInterfaceEntry; diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshalling.cs index 9059f53c4cc8a..75c7db67fd137 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshalling.cs @@ -3,199 +3,195 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -using System.Text; -using System.Threading.Tasks; using SharedTypes.ComInterfaces; using static System.Runtime.InteropServices.ComWrappers; namespace NativeExports.ComInterfaceGenerator { - public unsafe class StringMarshalling - { - [UnmanagedCallersOnly(EntryPoint = "new_utf8_marshalling")] - public static void* CreateUtf8ComObject() - { - MyComWrapper cw = new(); - var myObject = new Utf8Implementation(); - nint ptr = cw.GetOrCreateComInterfaceForObject(myObject, CreateComInterfaceFlags.None); - - return (void*)ptr; - } - - [UnmanagedCallersOnly(EntryPoint = "new_utf16_marshalling")] - public static void* CreateUtf16ComObject() - { - MyComWrapper cw = new(); - var myObject = new Utf16Implementation(); - nint ptr = cw.GetOrCreateComInterfaceForObject(myObject, CreateComInterfaceFlags.None); - - return (void*)ptr; - } - - class MyComWrapper : ComWrappers - { - static void* _s_comInterface1VTable = null; - static void* _s_comInterface2VTable = null; - static void* S_Utf8VTable - { - get - { - if (_s_comInterface1VTable != null) - return _s_comInterface1VTable; - void** vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(GetAndSetInt), sizeof(void*) * 5); - GetIUnknownImpl(out var fpQueryInterface, out var fpAddReference, out var fpRelease); - vtable[0] = (void*)fpQueryInterface; - vtable[1] = (void*)fpAddReference; - vtable[2] = (void*)fpRelease; - vtable[3] = (delegate* unmanaged)&Utf8Implementation.ABI.GetStringUtf8; - vtable[4] = (delegate* unmanaged)&Utf8Implementation.ABI.SetStringUtf8; - _s_comInterface1VTable = vtable; - return _s_comInterface1VTable; - } - } - static void* S_Utf16VTable - { - get - { - if (_s_comInterface2VTable != null) - return _s_comInterface2VTable; - void** vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(GetAndSetInt), sizeof(void*) * 5); - GetIUnknownImpl(out var fpQueryInterface, out var fpAddReference, out var fpRelease); - vtable[0] = (void*)fpQueryInterface; - vtable[1] = (void*)fpAddReference; - vtable[2] = (void*)fpRelease; - vtable[3] = (delegate* unmanaged)&Utf16Implementation.ABI.GetStringUtf16; - vtable[4] = (delegate* unmanaged)&Utf16Implementation.ABI.SetStringUtf16; - _s_comInterface2VTable = vtable; - return _s_comInterface2VTable; - } - } - - protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) - { - if (obj is IUTF8Marshalling) - { - ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Utf8Implementation), sizeof(ComInterfaceEntry)); - comInterfaceEntry->IID = new Guid(IUTF8Marshalling._guid); - comInterfaceEntry->Vtable = (nint)S_Utf8VTable; - count = 1; - return comInterfaceEntry; - } - else if (obj is IUTF16Marshalling) - { - ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Utf16Implementation), sizeof(ComInterfaceEntry)); - comInterfaceEntry->IID = new Guid(IUTF16Marshalling._guid); - comInterfaceEntry->Vtable = (nint)S_Utf16VTable; - count = 1; - return comInterfaceEntry; - } - count = 0; - return null; - } - - protected override object? CreateObject(nint externalComObject, CreateObjectFlags flags) => throw new NotImplementedException(); - protected override void ReleaseObjects(IEnumerable objects) => throw new NotImplementedException(); - } - - class Utf8Implementation : IUTF8Marshalling - { - string _data = "Hello, World!"; - - string IUTF8Marshalling.GetString() - { - return _data; - } - void IUTF8Marshalling.SetString(string x) - { - _data = x; - } - - // Provides function pointers in the COM format to use in COM VTables - public static class ABI - { - [UnmanagedCallersOnly] - public static int GetStringUtf8(void* @this, byte** value) - { - try - { - string currValue = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).GetString(); - *value = Utf8StringMarshaller.ConvertToUnmanaged(currValue); - return 0; - } - catch (Exception e) - { - return e.HResult; - } - } - - [UnmanagedCallersOnly] - public static int SetStringUtf8(void* @this, byte* newValue) - { - try - { - string value = Utf8StringMarshaller.ConvertToManaged(newValue); - ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).SetString(value); - return 0; - } - catch (Exception e) - { - return e.HResult; - } - } - } - } - - class Utf16Implementation : IUTF16Marshalling - { - string _data = "Hello, World!"; - - string IUTF16Marshalling.GetString() - { - return _data; - } - void IUTF16Marshalling.SetString(string x) - { - _data = x; - } - - // Provides function pointers in the COM format to use in COM VTables - public static class ABI - { - [UnmanagedCallersOnly] - public static int GetStringUtf16(void* @this, ushort** value) - { - try - { - string currValue = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).GetString(); - *value = Utf16StringMarshaller.ConvertToUnmanaged(currValue); - return 0; - } - catch (Exception e) - { - return e.HResult; - } - } - - [UnmanagedCallersOnly] - public static int SetStringUtf16(void* @this, ushort* newValue) - { - try - { - string value = Utf16StringMarshaller.ConvertToManaged(newValue); - ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).SetString(value); - return 0; - } - catch (Exception e) - { - return e.HResult; - } - } - } - } - } + public unsafe class StringMarshalling + { + [UnmanagedCallersOnly(EntryPoint = "new_utf8_marshalling")] + public static void* CreateUtf8ComObject() + { + MyComWrapper cw = new(); + var myObject = new Utf8Implementation(); + nint ptr = cw.GetOrCreateComInterfaceForObject(myObject, CreateComInterfaceFlags.None); + + return (void*)ptr; + } + + [UnmanagedCallersOnly(EntryPoint = "new_utf16_marshalling")] + public static void* CreateUtf16ComObject() + { + MyComWrapper cw = new(); + var myObject = new Utf16Implementation(); + nint ptr = cw.GetOrCreateComInterfaceForObject(myObject, CreateComInterfaceFlags.None); + + return (void*)ptr; + } + + class MyComWrapper : ComWrappers + { + static void* _s_comInterface1VTable = null; + static void* _s_comInterface2VTable = null; + static void* S_Utf8VTable + { + get + { + if (_s_comInterface1VTable != null) + return _s_comInterface1VTable; + void** vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(GetAndSetInt), sizeof(void*) * 5); + GetIUnknownImpl(out var fpQueryInterface, out var fpAddReference, out var fpRelease); + vtable[0] = (void*)fpQueryInterface; + vtable[1] = (void*)fpAddReference; + vtable[2] = (void*)fpRelease; + vtable[3] = (delegate* unmanaged)&Utf8Implementation.ABI.GetStringUtf8; + vtable[4] = (delegate* unmanaged)&Utf8Implementation.ABI.SetStringUtf8; + _s_comInterface1VTable = vtable; + return _s_comInterface1VTable; + } + } + static void* S_Utf16VTable + { + get + { + if (_s_comInterface2VTable != null) + return _s_comInterface2VTable; + void** vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(GetAndSetInt), sizeof(void*) * 5); + GetIUnknownImpl(out var fpQueryInterface, out var fpAddReference, out var fpRelease); + vtable[0] = (void*)fpQueryInterface; + vtable[1] = (void*)fpAddReference; + vtable[2] = (void*)fpRelease; + vtable[3] = (delegate* unmanaged)&Utf16Implementation.ABI.GetStringUtf16; + vtable[4] = (delegate* unmanaged)&Utf16Implementation.ABI.SetStringUtf16; + _s_comInterface2VTable = vtable; + return _s_comInterface2VTable; + } + } + + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + if (obj is IUTF8Marshalling) + { + ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Utf8Implementation), sizeof(ComInterfaceEntry)); + comInterfaceEntry->IID = new Guid(IUTF8Marshalling.IID); + comInterfaceEntry->Vtable = (nint)S_Utf8VTable; + count = 1; + return comInterfaceEntry; + } + else if (obj is IUTF16Marshalling) + { + ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Utf16Implementation), sizeof(ComInterfaceEntry)); + comInterfaceEntry->IID = new Guid(IUTF16Marshalling.IID); + comInterfaceEntry->Vtable = (nint)S_Utf16VTable; + count = 1; + return comInterfaceEntry; + } + count = 0; + return null; + } + + protected override object? CreateObject(nint externalComObject, CreateObjectFlags flags) => throw new NotImplementedException(); + protected override void ReleaseObjects(IEnumerable objects) => throw new NotImplementedException(); + } + + class Utf8Implementation : IUTF8Marshalling + { + string _data = "Hello, World!"; + + string IUTF8Marshalling.GetString() + { + return _data; + } + void IUTF8Marshalling.SetString(string x) + { + _data = x; + } + + // Provides function pointers in the COM format to use in COM VTables + public static class ABI + { + [UnmanagedCallersOnly] + public static int GetStringUtf8(void* @this, byte** value) + { + try + { + string currValue = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).GetString(); + *value = Utf8StringMarshaller.ConvertToUnmanaged(currValue); + return 0; + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + public static int SetStringUtf8(void* @this, byte* newValue) + { + try + { + string value = Utf8StringMarshaller.ConvertToManaged(newValue); + ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).SetString(value); + return 0; + } + catch (Exception e) + { + return e.HResult; + } + } + } + } + + class Utf16Implementation : IUTF16Marshalling + { + string _data = "Hello, World!"; + + string IUTF16Marshalling.GetString() + { + return _data; + } + void IUTF16Marshalling.SetString(string x) + { + _data = x; + } + + // Provides function pointers in the COM format to use in COM VTables + public static class ABI + { + [UnmanagedCallersOnly] + public static int GetStringUtf16(void* @this, ushort** value) + { + try + { + string currValue = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).GetString(); + *value = Utf16StringMarshaller.ConvertToUnmanaged(currValue); + return 0; + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + public static int SetStringUtf16(void* @this, ushort* newValue) + { + try + { + string value = Utf16StringMarshaller.ConvertToManaged(newValue); + ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)@this).SetString(value); + return 0; + } + catch (Exception e) + { + return e.HResult; + } + } + } + } + } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshallingOverride.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshallingOverride.cs index df01a1045700b..8e9ca95295bd8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshallingOverride.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/StringMarshallingOverride.cs @@ -3,13 +3,9 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -using System.Text; -using System.Threading.Tasks; using SharedTypes.ComInterfaces; using static System.Runtime.InteropServices.ComWrappers; @@ -76,9 +72,9 @@ static void* S_DerivedVTable if (obj is IStringMarshallingOverrideDerived) { ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Implementation), sizeof(ComInterfaceEntry) * 2); - comInterfaceEntry[0].IID = new Guid(IStringMarshallingOverrideDerived._guid); + comInterfaceEntry[0].IID = new Guid(IStringMarshallingOverrideDerived.IID); comInterfaceEntry[0].Vtable = (nint)S_DerivedVTable; - comInterfaceEntry[1].IID = new Guid(IStringMarshallingOverride._guid); + comInterfaceEntry[1].IID = new Guid(IStringMarshallingOverride.IID); comInterfaceEntry[1].Vtable = (nint)S_VTable; count = 2; return comInterfaceEntry; @@ -86,7 +82,7 @@ static void* S_DerivedVTable if (obj is IStringMarshallingOverride) { ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Implementation), sizeof(ComInterfaceEntry)); - comInterfaceEntry->IID = new Guid(IStringMarshallingOverride._guid); + comInterfaceEntry->IID = new Guid(IStringMarshallingOverride.IID); comInterfaceEntry->Vtable = (nint)S_VTable; count = 1; return comInterfaceEntry; diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs new file mode 100644 index 0000000000000..abb8842f1e0a2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("F4963CBF-10AF-460B-8495-107782187705")] + internal partial interface IArrayOfStatelessElements + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessType[] param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessType[] param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType[] param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType[] param, int size); + void MethodContentsIn([MarshalUsing(CountElementName = nameof(size))][In] StatelessType[] param, int size); + void MethodContentsOut([MarshalUsing(CountElementName = nameof(size))][Out] StatelessType[] param, int size); + void MethodContentsInOut([MarshalUsing(CountElementName = nameof(size))][In, Out] StatelessType[] param, int size); + } + + [GeneratedComClass] + internal partial class ArrayOfStatelessElements : IArrayOfStatelessElements + { + public void Method(StatelessType[] param, int size) + { + } + public void MethodContentsIn(StatelessType[] param, int size) + { + // We should be able to modify the contents and the caller shouldn't see it + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + public void MethodContentsInOut(StatelessType[] param, int size) + { + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + public void MethodContentsOut(StatelessType[] param, int size) + { + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = i }; + } + } + public void MethodIn(in StatelessType[] param, int size) + { + // We should be able to modify the contents and the caller shouldn't see it + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + public void MethodOut(out StatelessType[] param, int size) + { + param = new StatelessType[size]; + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = i }; + } + } + public void MethodRef(ref StatelessType[] param, int size) + { + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs new file mode 100644 index 0000000000000..4e71832850fef --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("5A9D3ED6-CC17-4FB9-8F82-0070489B7213")] + internal partial interface IBool + { + [return: MarshalAs(UnmanagedType.I1)] + bool Get(); + void Set([MarshalAs(UnmanagedType.I1)] bool value); + } + + [GeneratedComClass] + internal partial class IBoolImpl : IBool + { + bool _data; + public bool Get() => _data; + public void Set(bool value) => _data = value; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs index d792b62d6d5a9..7a1c97a7b3730 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs @@ -7,7 +7,7 @@ namespace SharedTypes.ComInterfaces { - [Guid(_guid)] + [Guid(IID)] [GeneratedComInterface(StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Utf16StringMarshaller))] internal partial interface ICustomStringMarshallingUtf16 { @@ -15,6 +15,6 @@ internal partial interface ICustomStringMarshallingUtf16 public void SetString(string value); - public const string _guid = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; + public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs index fb1c6ebba8b3a..f6cd4ff8afc55 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs @@ -7,8 +7,8 @@ namespace SharedTypes.ComInterfaces { - [GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper)] - [Guid(_guid)] + [GeneratedComInterface] + [Guid(IID)] internal partial interface IDerived : IGetAndSetInt { void SetName([MarshalUsing(typeof(Utf16StringMarshaller))] string name); @@ -16,7 +16,7 @@ internal partial interface IDerived : IGetAndSetInt [return: MarshalUsing(typeof(Utf16StringMarshaller))] string GetName(); - internal new const string _guid = "7F0DB364-3C04-4487-9193-4BB05DC7B654"; + internal new const string IID = "7F0DB364-3C04-4487-9193-4BB05DC7B654"; } [GeneratedComClass] diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs index e586a04573c9f..86af99aa4ff72 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs @@ -8,9 +8,9 @@ namespace SharedTypes.ComInterfaces { [GeneratedComInterface] - [Guid(_guid)] + [Guid(IID)] internal partial interface IEmpty { - public const string _guid = "95D19F50-F2D8-4E61-884B-0A9162EA4646"; + public const string IID = "95D19F50-F2D8-4E61-884B-0A9162EA4646"; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs new file mode 100644 index 0000000000000..f63cbbb91ab1d --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-2D8F-48A8-B6FB-B44B5F1B9FB6")] + internal partial interface IFloat + { + float Get(); + void Set(float value); + } + + [GeneratedComClass] + internal partial class IFloatImpl : IFloat + { + float _data; + public float Get() => _data; + public void Set(float value) => _data = value; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs index 54e7fb896bf94..3f1b18f34e9be 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs @@ -8,14 +8,14 @@ namespace SharedTypes.ComInterfaces { [GeneratedComInterface] - [Guid(_guid)] + [Guid(IID)] internal partial interface IGetAndSetInt { int GetInt(); public void SetInt(int x); - public const string _guid = "2c3f9903-b586-46b1-881b-adfce9af47b1"; + public const string IID = "2c3f9903-b586-46b1-881b-adfce9af47b1"; } [GeneratedComClass] internal partial class GetAndSetInt : IGetAndSetInt diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs index b70de07d1596b..f2283708678df 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs @@ -8,12 +8,12 @@ namespace SharedTypes.ComInterfaces { [GeneratedComInterface] - [Guid(_guid)] + [Guid(IID)] internal partial interface IGetIntArray { [return: MarshalUsing(ConstantElementCount = 10)] int[] GetInts(); - public const string _guid = "7D802A0A-630A-4C8E-A21F-771CC9031FB9"; + public const string IID = "7D802A0A-630A-4C8E-A21F-771CC9031FB9"; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs new file mode 100644 index 0000000000000..e50344f3a268f --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("EE6D1F2A-3418-4317-A87C-35488F6546AB")] + internal partial interface IInt + { + public int Get(); + public void Set(int value); + public void SwapRef(ref int value); + public void GetOut(out int value); + public void SetIn(in int value); + } + + [GeneratedComClass] + internal partial class IIntImpl : IInt + { + int _data; + + public int Get() => _data; + + public void Set(int value) => _data = value; + + public void SetIn(in int value) + { + _data = value; + } + + public void GetOut(out int value) + { + value = _data; + } + + public void SwapRef(ref int value) + { + var tmp = _data; + _data = value; + value = tmp; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs new file mode 100644 index 0000000000000..fbc68dcf4dbca --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] + internal partial interface IIntArray + { + [return: MarshalUsing(CountElementName = nameof(size))] + int[] GetReturn(out int size); + int GetOut([MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out int[] array); + void SetContents([MarshalUsing(CountElementName = nameof(size))] int[] array, int size); + void FillAscending([Out][MarshalUsing(CountElementName = nameof(size))] int[] array, int size); + void Double([In, Out][MarshalUsing(CountElementName = nameof(size))] int[] array, int size); + void PassIn([MarshalUsing(CountElementName = nameof(size))] in int[] array, int size); + void SwapArray([MarshalUsing(CountElementName = nameof(size))] ref int[] array, int size); + } + + [GeneratedComClass] + internal partial class IIntArrayImpl : IIntArray + { + int[] _data; + public int[] GetReturn(out int size) + { + size = _data.Length; + return _data; + } + public int GetOut(out int[] array) + { + array = _data; + return array.Length; + } + public void SetContents(int[] array, int size) + { + _data = new int[size]; + array.CopyTo(_data, 0); + } + + public void FillAscending(int[] array, int size) + { + for (int i = 0; i < size; i++) + { + array[i] = i; + } + } + public void Double(int[] array, int size) + { + for (int i = 0; i < size; i++) + { + array[i] = array[i] * 2; + } + + } + + public void PassIn([MarshalUsing(CountElementName = "size")] in int[] array, int size) + { + _data = new int[size]; + array.CopyTo(_data, 0); + } + public void SwapArray([MarshalUsing(CountElementName = "size")] ref int[] array, int size) + { + var temp = _data; + _data = array; + array = temp; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs new file mode 100644 index 0000000000000..95c6a13335b44 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("A4857398-06FB-4A6E-81DB-35461BE999C5")] + internal partial interface IInterface + { + public IInt Get(); + public void SetInt(IInt value); + public void SwapRef(ref IInt value); + public void GetOut(out IInt value); + public void InInt(in IInt value); + } + + [GeneratedComClass] + internal partial class IInterfaceImpl : IInterface + { + IInt _data = new IIntImpl(); + + IInt IInterface.Get() => _data; + + void IInterface.InInt(in IInt value) + { + var i = value.Get(); + } + + void IInterface.GetOut(out IInt value) + { + value = _data; + } + + void IInterface.SwapRef(ref IInt value) + { + var tmp = _data; + _data = value; + value = tmp; + } + + void IInterface.SetInt(IInt value) + { + int x = value.Get(); + value.Set(x); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs new file mode 100644 index 0000000000000..c1153e377fc65 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] + internal partial interface IJaggedIntArray + { + [return: MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] + int[][] Get( + [MarshalUsing(CountElementName = nameof(length))] + out int[] widths, + out int length); + + int Get2( + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] + out int[][] array, + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] + out int[] widths); + + void Set( + [MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] + int[][] array, + [MarshalUsing(CountElementName = nameof(length))] + int[] widths, + int length); + } + + [GeneratedComClass] + internal partial class IJaggedIntArrayImpl : IJaggedIntArray + { + int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; + int[] _widths = new int[] { 3, 2, 4 }; + public int[][] Get(out int[] widths, out int length) + { + widths = _widths; + length = _data.Length; + return _data; + } + public int Get2(out int[][] array, out int[] widths) + { + array = _data; + widths = _widths; + return array.Length; + } + public void Set(int[][] array, int[] widths, int length) + { + _data = array; + _widths = widths; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs new file mode 100644 index 0000000000000..3a86b74a70b5c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(StringMarshalling = System.Runtime.InteropServices.StringMarshalling.Utf8)] + [Guid(IID)] + internal partial interface IRefStrings + { + public const string IID = "5146B7DB-0588-469B-B8E5-B38090A2FC15"; + void RefString(ref string value); + void InString(in string value); + void OutString(out string value); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs new file mode 100644 index 0000000000000..50a25b520939f --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Microsoft.Win32.SafeHandles; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + internal partial interface ISafeFileHandle + { + void Method(SafeFileHandle p); + void MethodIn(in SafeFileHandle p); + void MethodOut(out SafeFileHandle p); + void MethodRef(ref SafeFileHandle p); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs new file mode 100644 index 0000000000000..a585a5456b403 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulCallerAllocatedBufferMarshalling + { + void Method(StatefulCallerAllocatedBufferType param); + void MethodIn(in StatefulCallerAllocatedBufferType param); + void MethodOut(out StatefulCallerAllocatedBufferType param); + void MethodRef(ref StatefulCallerAllocatedBufferType param); + StatefulCallerAllocatedBufferType Return(); + [PreserveSig] + StatefulCallerAllocatedBufferType ReturnPreserveSig(); + } + + [NativeMarshalling(typeof(StatefulCallerAllocatedBufferTypeMarshaller))] + internal class StatefulCallerAllocatedBufferType + { + } + + [CustomMarshaller(typeof(StatefulCallerAllocatedBufferType), MarshalMode.Default, typeof(StatefulCallerAllocatedBufferTypeMarshaller))] + internal struct StatefulCallerAllocatedBufferTypeMarshaller + { + public static int BufferSize => 64; + + public void FromManaged(StatefulCallerAllocatedBufferType managed, Span buffer) + { + throw new NotImplementedException(); + } + + public nint ToUnmanaged() + { + throw new NotImplementedException(); + } + + public void FromUnmanaged(nint unmanaged) + { + throw new NotImplementedException(); + } + + public StatefulCallerAllocatedBufferType ToManaged() + { + throw new NotImplementedException(); + } + + public void Free() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs new file mode 100644 index 0000000000000..e4df4476cfb7c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + partial interface IStatefulCollectionBlittableElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatefulCollection p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection ReturnPreserveSig(int size); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs new file mode 100644 index 0000000000000..d31c70856b6eb --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + partial interface IStatefulCollectionStatelessElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatefulCollection p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatefulCollectionMarshaller<,>))] + internal class StatefulCollection + { + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatefulCollection<>), MarshalMode.Default, typeof(StatefulCollectionMarshaller<,>.Default))] + static unsafe class StatefulCollectionMarshaller where TUnmanagedElement : unmanaged + { + public ref struct Default + { + public byte* ToUnmanaged() => throw null; + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetUnmanagedValuesDestination() => throw null; + + public void FromUnmanaged(byte* value) => throw null; + public System.Span GetManagedValuesDestination(int numElements) => throw null; + public System.ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw null; + + public void Free() => throw null; + + public void FromManaged(StatefulCollection managed) => throw null; + + public StatefulCollection ToManaged() => throw null; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs new file mode 100644 index 0000000000000..70b9a4e78a0ac --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulFinallyMarshalling + { + void Method(StatefulFinallyType param); + void MethodIn(in StatefulFinallyType param); + void MethodOut(out StatefulFinallyType param); + void MethodRef(ref StatefulFinallyType param); + StatefulFinallyType Return(); + [PreserveSig] + StatefulFinallyType ReturnPreserveSig(); + } + + [NativeMarshalling(typeof(StatefulFinallyTypeMarshaller))] + internal class StatefulFinallyType + { + } + + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.Default, typeof(StatefulFinallyTypeMarshaller))] + internal struct StatefulFinallyTypeMarshaller + { + public void FromManaged(StatefulFinallyType managed) + { + throw new NotImplementedException(); + } + + public nint ToUnmanaged() + { + throw new NotImplementedException(); + } + + public void FromUnmanaged(nint unmanaged) + { + throw new NotImplementedException(); + } + + public StatefulFinallyType ToManagedFinally() + { + throw new NotImplementedException(); + } + + public void Free() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs new file mode 100644 index 0000000000000..f84da40e9e2e0 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulMarshalling + { + void Method(StatefulType param); + void MethodIn(in StatefulType param); + void MethodOut(out StatefulType param); + void MethodRef(ref StatefulType param); + StatefulType Return(); + [PreserveSig] + StatefulType ReturnPreserveSig(); + } + + [NativeMarshalling(typeof(StatefulTypeMarshaller))] + internal class StatefulType + { + } + + [CustomMarshaller(typeof(StatefulType), MarshalMode.Default, typeof(StatefulTypeMarshaller))] + internal struct StatefulTypeMarshaller + { + public void FromManaged(StatefulType managed) + { + throw new System.NotImplementedException(); + } + + public nint ToUnmanaged() + { + throw new System.NotImplementedException(); + } + + public void FromUnmanaged(nint unmanaged) + { + throw new System.NotImplementedException(); + } + + public StatefulType ToManaged() + { + throw new System.NotImplementedException(); + } + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs new file mode 100644 index 0000000000000..1b5438cbdd6f2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulPinnedMarshalling + { + void Method(StatefulPinnedType param); + void MethodIn(in StatefulPinnedType param); + void MethodOut(out StatefulPinnedType param); + void MethodRef(ref StatefulPinnedType param); + StatefulPinnedType Return(); + [PreserveSig] + StatefulPinnedType ReturnPreserveSig(); + } + + [NativeMarshalling(typeof(StatefulPinnedTypeMarshaller))] + internal class StatefulPinnedType + { + } + + [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.Default, typeof(StatefulPinnedTypeMarshaller))] + internal struct StatefulPinnedTypeMarshaller + { + + public static int BufferSize => 64; + public ref int GetPinnableReference() => throw new System.NotImplementedException(); + public void FromManaged(StatefulPinnedType managed, Span buffer) + { + throw new System.NotImplementedException(); + } + + public nint ToUnmanaged() + { + throw new System.NotImplementedException(); + } + + public void FromUnmanaged(nint unmanaged) + { + throw new System.NotImplementedException(); + } + + public StatefulPinnedType ToManaged() + { + throw new System.NotImplementedException(); + } + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs new file mode 100644 index 0000000000000..f5c3d4b7e2885 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessCallerAllocateBufferMarshalling + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessCallerAllocatedBufferType param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessCallerAllocatedBufferType param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessCallerAllocatedBufferType param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessCallerAllocatedBufferType param, int size); + StatelessCallerAllocatedBufferType Return(); + [PreserveSig] + StatelessCallerAllocatedBufferType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessCallerAllocatedBufferMarshalling : IStatelessCallerAllocateBufferMarshalling + { + public void Method([MarshalUsing(CountElementName = "size")] StatelessCallerAllocatedBufferType param, int size) { } + public void MethodIn([MarshalUsing(CountElementName = "size")] in StatelessCallerAllocatedBufferType param, int size) { } + public void MethodOut([MarshalUsing(CountElementName = "size")] out StatelessCallerAllocatedBufferType param, int size) { param = new StatelessCallerAllocatedBufferType { I = 42 }; } + public void MethodRef([MarshalUsing(CountElementName = "size")] ref StatelessCallerAllocatedBufferType param, int size) { param = new StatelessCallerAllocatedBufferType { I = 200 }; } + public StatelessCallerAllocatedBufferType Return() => throw new NotImplementedException(); + public StatelessCallerAllocatedBufferType ReturnPreserveSig() => throw new NotImplementedException(); + } + + [NativeMarshalling(typeof(StatelessCallerAllocatedBufferTypeMarshaller))] + internal class StatelessCallerAllocatedBufferType + { + public int I; + } + + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.Default, typeof(StatelessCallerAllocatedBufferTypeMarshaller))] + internal static class StatelessCallerAllocatedBufferTypeMarshaller + { + public static int FreeCount { get; private set; } + public static int BufferSize => 64; + public static nint ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed, Span buffer) => managed.I; + + public static StatelessCallerAllocatedBufferType ConvertToManaged(nint unmanaged) => new StatelessCallerAllocatedBufferType { I = (int)unmanaged }; + + public static void Free(nint unmanaged) => FreeCount++; + + public static nint ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed) => managed.I; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs new file mode 100644 index 0000000000000..684352435e626 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + partial interface IStatelessCollectionBlittableElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatelessCollection p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection ReturnPreserveSig(int size); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs new file mode 100644 index 0000000000000..ca6770fdf0440 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + partial interface IStatelessCollectionStatelessElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatelessCollection p, + int size); + + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, + in int size); + + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, + int size); + + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, + out int size); + + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection Return(int size); + + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatelessCollectionMarshaller<,>))] + internal class StatelessCollection + { + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.Default, typeof(StatelessCollectionMarshaller<,>.Default))] + internal static unsafe class StatelessCollectionMarshaller where TUnmanagedElement : unmanaged + { + internal static class Default + { + public static nint AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) + { + throw new System.NotImplementedException(); + } + + public static StatelessCollection AllocateContainerForManagedElements(nint unmanaged, int numElements) + { + throw new System.NotImplementedException(); + } + + public static System.ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) + { + throw new System.NotImplementedException(); + } + + public static System.Span GetUnmanagedValuesDestination(nint unmanaged, int numElements) + { + throw new System.NotImplementedException(); + } + + public static System.ReadOnlySpan GetUnmanagedValuesSource(nint unmanaged, int numElements) + { + throw new System.NotImplementedException(); + } + + public static System.Span GetManagedValuesDestination(StatelessCollection managed) + { + throw new System.NotImplementedException(); + } + + public static void Free(nint unmanaged) { } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs new file mode 100644 index 0000000000000..6b0c57a1f37fb --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A26-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessFinallyMarshalling + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessFinallyType param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessFinallyType param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessFinallyType param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessFinallyType param, int size); + StatelessFinallyType Return(); + [PreserveSig] + StatelessFinallyType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessFinallyMarshalling : IStatelessFinallyMarshalling + { + public void Method([MarshalUsing(CountElementName = "size")] StatelessFinallyType param, int size) { } + public void MethodIn([MarshalUsing(CountElementName = "size")] in StatelessFinallyType param, int size) { } + public void MethodOut([MarshalUsing(CountElementName = "size")] out StatelessFinallyType param, int size) { param = new StatelessFinallyType { I = 42 }; } + public void MethodRef([MarshalUsing(CountElementName = "size")] ref StatelessFinallyType param, int size) { param = new StatelessFinallyType { I = 200 }; } + public StatelessFinallyType Return() => throw new NotImplementedException(); + public StatelessFinallyType ReturnPreserveSig() => throw new NotImplementedException(); + } + + [NativeMarshalling(typeof(StatelessFinallyTypeMarshaller))] + internal class StatelessFinallyType + { + public int I; + } + + [CustomMarshaller(typeof(StatelessFinallyType), MarshalMode.Default, typeof(StatelessFinallyTypeMarshaller))] + internal static class StatelessFinallyTypeMarshaller + { + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(StatelessFinallyType managed) => managed.I; + + public static StatelessFinallyType ConvertToManagedFinally(nint unmanaged) => new StatelessFinallyType { I = (int)unmanaged }; + + public static void Free(nint unmanaged) => FreeCount++; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs new file mode 100644 index 0000000000000..c2c8d45e31ad5 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessMarshalling + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessType param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessType param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType param, int size); + StatelessType Return(); + [PreserveSig] + StatelessType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessMarshalling : IStatelessMarshalling + { + public void Method([MarshalUsing(CountElementName = "size")] StatelessType param, int size) { } + public void MethodIn([MarshalUsing(CountElementName = "size")] in StatelessType param, int size) { } + public void MethodOut([MarshalUsing(CountElementName = "size")] out StatelessType param, int size) { param = new StatelessType { I = 42 }; } + public void MethodRef([MarshalUsing(CountElementName = "size")] ref StatelessType param, int size) { param = new StatelessType { I = 200 }; } + public StatelessType Return() => throw new NotImplementedException(); + public StatelessType ReturnPreserveSig() => throw new NotImplementedException(); + } + + [NativeMarshalling(typeof(StatelessTypeMarshaller))] + internal class StatelessType + { + public int I; + } + + [CustomMarshaller(typeof(StatelessType), MarshalMode.Default, typeof(StatelessTypeMarshaller))] + internal static class StatelessTypeMarshaller + { + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; + + public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; + + public static void Free(nint unmanaged) => FreeCount++; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs new file mode 100644 index 0000000000000..4419fc52c077c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessPinnedMarshalling + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessPinnedType param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessPinnedType param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessPinnedType param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessPinnedType param, int size); + StatelessPinnedType Return(); + [PreserveSig] + StatelessPinnedType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessPinnedMarshalling : IStatelessPinnedMarshalling + { + public void Method([MarshalUsing(CountElementName = "size")] StatelessPinnedType param, int size) { } + public void MethodIn([MarshalUsing(CountElementName = "size")] in StatelessPinnedType param, int size) { } + public void MethodOut([MarshalUsing(CountElementName = "size")] out StatelessPinnedType param, int size) { param = new StatelessPinnedType { I = 42 }; } + public void MethodRef([MarshalUsing(CountElementName = "size")] ref StatelessPinnedType param, int size) { param = new StatelessPinnedType { I = 200 }; } + public StatelessPinnedType Return() => throw new NotImplementedException(); + public StatelessPinnedType ReturnPreserveSig() => throw new NotImplementedException(); + } + + [NativeMarshalling(typeof(StatelessPinnedTypeMarshaller))] + internal class StatelessPinnedType + { + public int I; + } + + internal struct StatelessPinnedStruct + { + public int I; + } + + [CustomMarshaller(typeof(StatelessPinnedType), MarshalMode.Default, typeof(StatelessPinnedTypeMarshaller))] + internal static class StatelessPinnedTypeMarshaller + { + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(StatelessPinnedType managed) => managed.I; + + public static StatelessPinnedType ConvertToManaged(nint unmanaged) => new StatelessPinnedType { I = (int)unmanaged }; + + static StatelessPinnedStruct _field; + public static ref StatelessPinnedStruct GetPinnableReference(StatelessPinnedType unmanaged) + { + _field = new StatelessPinnedStruct() { I = unmanaged.I }; + return ref _field; + } + + public static void Free(nint unmanaged) => FreeCount++; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs index cd7c8f620dc93..df956009fce8b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs @@ -2,20 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -using System.Text; -using System.Threading.Tasks; namespace SharedTypes.ComInterfaces { [GeneratedComInterface(StringMarshalling = System.Runtime.InteropServices.StringMarshalling.Utf8)] - [Guid(_guid)] + [Guid(IID)] internal partial interface IStringMarshallingOverride { - public const string _guid = "5146B7DB-0588-469B-B8E5-B38090A2FC15"; + public const string IID = "5146B7DB-0588-469B-B8E5-B38090A2FC15"; string StringMarshallingUtf8(string input); [return: MarshalAs(UnmanagedType.LPWStr)] diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs index 079db461a66bd..5022d42d13930 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs @@ -2,20 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices.Marshalling; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; +using System.Runtime.InteropServices.Marshalling; namespace SharedTypes.ComInterfaces { [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] - [Guid(_guid)] + [Guid(IID)] internal partial interface IStringMarshallingOverrideDerived : IStringMarshallingOverride { - public new const string _guid = "3AFFE3FD-D11E-4195-8250-0C73321977A0"; + public new const string IID = "3AFFE3FD-D11E-4195-8250-0C73321977A0"; string StringMarshallingUtf8_2(string input); [return: MarshalAs(UnmanagedType.LPWStr)] diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs index 2ef5534aa6b23..1c123065a2ec1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs @@ -7,7 +7,7 @@ namespace SharedTypes.ComInterfaces { - [Guid(_guid)] + [Guid(IID)] [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] internal partial interface IUTF16Marshalling { @@ -15,6 +15,6 @@ internal partial interface IUTF16Marshalling public void SetString(string value); - public const string _guid = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; + public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs index 2689425abd506..08dbbdd5e6892 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs @@ -7,7 +7,7 @@ namespace SharedTypes.ComInterfaces { - [Guid(_guid)] + [Guid(IID)] [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] internal partial interface IUTF8Marshalling { @@ -15,6 +15,6 @@ internal partial interface IUTF8Marshalling public void SetString(string value); - public const string _guid = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; + public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs new file mode 100644 index 0000000000000..1c6513607d905 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + [GeneratedComInterface] + [Guid("A4857395-06FB-4A6E-81DB-35461BE999C5")] + internal partial interface ICollectionMarshallingFails + { + [return: MarshalUsing(ConstantElementCount = 10)] + [return: MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + public int[] GetConstSize(); + + [return: MarshalUsing(CountElementName = nameof(size))] + [return: MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + public int[] Get(out int size); + + public void Set( + [MarshalUsing(CountElementName = nameof(size))] + [MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + int[] value, int size); + } + + [GeneratedComClass] + internal partial class ICollectionMarshallingFailsImpl : ICollectionMarshallingFails + { + int[] _data = new[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + public int[] Get(out int size) + { + size = _data.Length; + return _data; + } + + [return: MarshalUsing(ConstantElementCount = 10), MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + public int[] GetConstSize() => new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + public void Set(int[] value, int size) + { + _data = new int[size]; + value.CopyTo(_data, 0); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs new file mode 100644 index 0000000000000..4da8802dff407 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] + internal partial interface IJaggedIntArrayMarshallingFails + { + [return: MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + int[][] Get( + [MarshalUsing(CountElementName = nameof(length))] + out int[] widths, + out int length); + + int Get2( + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + out int[][] array, + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] + out int[] widths); + + [return: MarshalUsing(ConstantElementCount = 10), + MarshalUsing(ElementIndirectionDepth = 1, ConstantElementCount = 10), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + int[][] GetConstSize(); + + void Set( + [MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + int[][] array, + [MarshalUsing(CountElementName = nameof(length))] + int[] widths, + int length); + } + + [GeneratedComClass] + internal partial class IJaggedIntArrayMarshallingFailsImpl : IJaggedIntArrayMarshallingFails + { + int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; + int[] _widths = new int[] { 3, 2, 4 }; + public int[][] Get(out int[] widths, out int length) + { + widths = _widths; + length = _data.Length; + return _data; + } + public int Get2(out int[][] array, out int[] widths) + { + array = _data; + widths = _widths; + return array.Length; + } + + [return: MarshalUsing(ConstantElementCount = 10), MarshalUsing(ElementIndirectionDepth = 1, ConstantElementCount = 10), MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + public int[][] GetConstSize() + { + int[][] values = new int[10][]; + for (int i = 0; i < 10; i++) + { + values[i] = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + } + return values; + } + + public void Set(int[][] array, int[] widths, int length) + { + _data = array; + _widths = widths; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs index 6890cacc60c74..77afe03c85fdd 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs @@ -57,7 +57,7 @@ public static nint ConvertToUnmanaged(string managed) if (++_marshalledCount == ThrowOnNthMarshalledElement) { _marshalledCount = 0; - throw new ArgumentException("This marshaller throws on the Nth element marshalled where N = " + ThrowOnNthMarshalledElement); + throw new MarshallingFailureException("This marshaller throws on the Nth element marshalled where N = " + ThrowOnNthMarshalledElement); } return (nint)Utf8StringMarshaller.ConvertToUnmanaged(managed); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs new file mode 100644 index 0000000000000..b832266e409a4 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + internal class MarshallingFailureException : Exception + { + public MarshallingFailureException() : base() { } + public MarshallingFailureException(string message) : base(message) { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs new file mode 100644 index 0000000000000..0bfb7e052d74a --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + [CustomMarshaller(typeof(int), MarshalMode.ElementIn, typeof(ThrowOn4thElementMarshalled))] + [CustomMarshaller(typeof(int), MarshalMode.ElementOut, typeof(ThrowOn4thElementMarshalled))] + internal static class ThrowOn4thElementMarshalled + { + static int _marshalledCount = 0; + static int _unmarshalledCount = 0; + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(int managed) + { + if (_marshalledCount++ == 3) + { + _marshalledCount = 0; + throw new MarshallingFailureException("The element was the 4th element (with 0-based index 3)"); + } + return managed; + } + + public static int ConvertToManaged(nint unmanaged) + { + if (_unmarshalledCount++ == 3) + { + _unmarshalledCount = 0; + throw new MarshallingFailureException("The element was the 4th element (with 0-based index 3)"); + } + return (int)unmanaged; + } + public static void Free(nint unmanaged) => ++FreeCount; + } + +} From 751632edea2e2ec8ac50975eac02d705d2fdfe80 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:07:06 -0700 Subject: [PATCH 06/20] Enable tests --- .../tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index a820e032e798f..9d35c9ded0136 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -117,7 +117,6 @@ public void IInterface() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/89747")] public void ICollectionMarshallingFails() { var obj = CreateWrapper(); @@ -136,7 +135,6 @@ public void ICollectionMarshallingFails() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/89747")] public void IJaggedArrayMarshallingFails() { var obj = CreateWrapper(); From c562670331538b36068e8cf4e97bacf86d2387c8 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:39:44 -0700 Subject: [PATCH 07/20] Move cleanup stage filter method to CustomTypeMarshallingGenerator --- .../CustomTypeMarshallingGenerator.cs | 18 ++++++++++++++++-- .../Marshalling/MarshallerHelpers.cs | 11 ----------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs index bf993d5f40aee..8d7360b507438 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs @@ -97,9 +97,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return _nativeTypeMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); } break; - case StubCodeContext.Stage.CleanupCallerAllocated when MarshallerHelpers.ShouldCleanupInCurrentStage(info, context): + case StubCodeContext.Stage.CleanupCallerAllocated when GetCleanupStage(info, context) is StubCodeContext.Stage.CleanupCallerAllocated: return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); - case StubCodeContext.Stage.CleanupCalleeAllocated when MarshallerHelpers.ShouldCleanupInCurrentStage(info, context): + case StubCodeContext.Stage.CleanupCalleeAllocated when GetCleanupStage(info, context) is StubCodeContext.Stage.CleanupCalleeAllocated: return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); default: break; @@ -117,6 +117,20 @@ private bool ShouldGenerateByValueOutMarshalling(TypePositionInfo info, StubCode && !_isPinned; } + /// + /// Returns which stage cleanup should be performed for the parameter. + /// + private static StubCodeContext.Stage GetCleanupStage(TypePositionInfo info, StubCodeContext context) + { + if (context.Direction is MarshalDirection.UnmanagedToManaged) + return StubCodeContext.Stage.CleanupCallerAllocated; + + if (MarshallerHelpers.GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) + return StubCodeContext.Stage.CleanupCalleeAllocated; + + return StubCodeContext.Stage.CleanupCallerAllocated; + } + public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) { return _nativeTypeMarshaller.UsesNativeIdentifier(info, context); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 80fa7d867c2ec..86275796f208e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -394,16 +394,5 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo } throw new UnreachableException("An element is either a return value or passed by value or by ref."); } - - public static bool ShouldCleanupInCurrentStage(TypePositionInfo info, StubCodeContext context) - { - if (context.Direction is MarshalDirection.UnmanagedToManaged) - return context.CurrentStage is StubCodeContext.Stage.CleanupCallerAllocated; - - if (GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) - return context.CurrentStage is StubCodeContext.Stage.CleanupCalleeAllocated; - - return context.CurrentStage is StubCodeContext.Stage.CleanupCallerAllocated; - } } } From 51f0c543be5ef6767ba597010449cc5e468c25ab Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:03:19 -0700 Subject: [PATCH 08/20] Make marshaller types specific to each scenario --- .../Marshalling/ElementsMarshalling.cs | 5 +- .../IStatefulFinallyMarshalling.cs | 80 +++++++++++++--- .../ComInterfaces/IStatefulMarshalling.cs | 82 ++++++++++++---- .../IStatelessCollectionStatelessElement.cs | 93 +++++++++++++++---- .../ComInterfaces/IStatelessMarshalling.cs | 43 +++++++-- 5 files changed, 243 insertions(+), 60 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs index d457e37a7f2b2..55818927c3965 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs @@ -630,8 +630,7 @@ private static bool UsesLastIndexMarshalled(TypePositionInfo info, StubCodeConte { return false; } - bool usesLastIndexMarshalled = !shouldCleanupAllElements && !onlyUnmarshals; - return usesLastIndexMarshalled; + return true; } private static bool ShouldCleanUpAllElements(TypePositionInfo info, StubCodeContext context) @@ -640,7 +639,7 @@ private static bool ShouldCleanUpAllElements(TypePositionInfo info, StubCodeCont _ = context; // AdditionalTemporaryStateLivesAcrossStages implies that it is an outer collection // Out parameters means that the contents are created by the P/Invoke and assumed to have successfully created all elements - return !context.AdditionalTemporaryStateLivesAcrossStages || info.ByValueContentsMarshalKind == ByValueContentsMarshalKind.Out || info.RefKind == RefKind.Out; + return !context.AdditionalTemporaryStateLivesAcrossStages || info.ByValueContentsMarshalKind == ByValueContentsMarshalKind.Out || info.RefKind == RefKind.Out || info.IsNativeReturnPosition; } public override StatementSyntax GenerateSetupStatement(TypePositionInfo info, StubCodeContext context) diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs index 70b9a4e78a0ac..51e8991d4aa7f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs @@ -25,32 +25,82 @@ internal class StatefulFinallyType { } - [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.Default, typeof(StatefulFinallyTypeMarshaller))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] internal struct StatefulFinallyTypeMarshaller { - public void FromManaged(StatefulFinallyType managed) + internal struct Bidirectional { - throw new NotImplementedException(); - } + public void FromManaged(StatefulFinallyType managed) + { + throw new System.NotImplementedException(); + } - public nint ToUnmanaged() - { - throw new NotImplementedException(); - } + public nint ToUnmanaged() + { + throw new System.NotImplementedException(); + } - public void FromUnmanaged(nint unmanaged) - { - throw new NotImplementedException(); + public void FromUnmanaged(nint unmanaged) + { + throw new System.NotImplementedException(); + } + + public StatefulFinallyType ToManagedFinally() + { + throw new NotImplementedException(); + } + + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } } - public StatefulFinallyType ToManagedFinally() + internal struct ManagedToUnmanaged { - throw new NotImplementedException(); + public void FromManaged(StatefulFinallyType managed) + { + throw new System.NotImplementedException(); + } + + public nint ToUnmanaged() + { + throw new System.NotImplementedException(); + } + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } } - public void Free() + internal struct UnmanagedToManaged { - throw new NotImplementedException(); + public void FromUnmanaged(nint unmanaged) + { + throw new System.NotImplementedException(); + } + public StatefulFinallyType ToManagedFinally() + { + throw new NotImplementedException(); + } + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs index f84da40e9e2e0..a1df4de144823 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs @@ -24,34 +24,82 @@ internal class StatefulType { } - [CustomMarshaller(typeof(StatefulType), MarshalMode.Default, typeof(StatefulTypeMarshaller))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] internal struct StatefulTypeMarshaller { - public void FromManaged(StatefulType managed) + internal struct Bidirectional { - throw new System.NotImplementedException(); - } + public void FromManaged(StatefulType managed) + { + throw new System.NotImplementedException(); + } - public nint ToUnmanaged() - { - throw new System.NotImplementedException(); - } + public nint ToUnmanaged() + { + throw new System.NotImplementedException(); + } - public void FromUnmanaged(nint unmanaged) - { - throw new System.NotImplementedException(); + public void FromUnmanaged(nint unmanaged) + { + throw new System.NotImplementedException(); + } + + public StatefulType ToManaged() + { + throw new System.NotImplementedException(); + } + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } } - public StatefulType ToManaged() + internal struct ManagedToUnmanaged { - throw new System.NotImplementedException(); + public void FromManaged(StatefulType managed) + { + throw new System.NotImplementedException(); + } + + public nint ToUnmanaged() + { + throw new System.NotImplementedException(); + } + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } } - public void Free() + internal struct UnmanagedToManaged { - throw new System.NotImplementedException(); - } + public void FromUnmanaged(nint unmanaged) + { + throw new System.NotImplementedException(); + } - public void OnInvoked() { } + public StatefulType ToManaged() + { + throw new System.NotImplementedException(); + } + + public void Free() + { + throw new System.NotImplementedException(); + } + + public void OnInvoked() { } + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs index ca6770fdf0440..0465f391e3b42 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs @@ -1,13 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; namespace SharedTypes.ComInterfaces { [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] - partial interface IStatelessCollectionStatelessElement + internal partial interface IStatelessCollectionStatelessElement { void Method( [MarshalUsing(CountElementName = nameof(size))] StatelessCollection p, @@ -38,43 +39,103 @@ internal class StatelessCollection { } + internal struct NativeCollection + { + + } + [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.Default, typeof(StatelessCollectionMarshaller<,>.Default))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessCollectionMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessCollectionMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementIn, typeof(StatelessCollectionMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessCollectionMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessCollectionMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementOut, typeof(StatelessCollectionMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] internal static unsafe class StatelessCollectionMarshaller where TUnmanagedElement : unmanaged { - internal static class Default + internal static class Bidirectional + { + public static NativeCollection AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) + { + throw new NotImplementedException(); + } + + public static StatelessCollection AllocateContainerForManagedElements(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(StatelessCollection managed) + { + throw new NotImplementedException(); + } + + public static void Free(NativeCollection unmanaged) { } + } + + internal static class ManagedToUnmanaged { - public static nint AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) + public static NativeCollection AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } - public static StatelessCollection AllocateContainerForManagedElements(nint unmanaged, int numElements) + public static ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } - public static System.ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) + public static Span GetUnmanagedValuesDestination(NativeCollection unmanaged, int numElements) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } - public static System.Span GetUnmanagedValuesDestination(nint unmanaged, int numElements) + public static void Free(NativeCollection unmanaged) => throw new NotImplementedException(); + + } + + internal static class UnmanagedToManaged + { + public static StatelessCollection AllocateContainerForManagedElements(NativeCollection unmanaged, int numElements) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } - public static System.ReadOnlySpan GetUnmanagedValuesSource(nint unmanaged, int numElements) + public static ReadOnlySpan GetUnmanagedValuesSource(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + // Should be removed: https://github.com/dotnet/runtime/issues/89885 + public static Span GetUnmanagedValuesDestination(NativeCollection unmanaged, int numElements) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } - public static System.Span GetManagedValuesDestination(StatelessCollection managed) + public static Span GetManagedValuesDestination(StatelessCollection managed) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } - public static void Free(nint unmanaged) { } + public static void Free(NativeCollection unmanaged) => throw new NotImplementedException(); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs index c2c8d45e31ad5..325064ad8cbf9 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs @@ -23,10 +23,10 @@ internal partial interface IStatelessMarshalling [GeneratedComClass] internal partial class StatelessMarshalling : IStatelessMarshalling { - public void Method([MarshalUsing(CountElementName = "size")] StatelessType param, int size) { } - public void MethodIn([MarshalUsing(CountElementName = "size")] in StatelessType param, int size) { } - public void MethodOut([MarshalUsing(CountElementName = "size")] out StatelessType param, int size) { param = new StatelessType { I = 42 }; } - public void MethodRef([MarshalUsing(CountElementName = "size")] ref StatelessType param, int size) { param = new StatelessType { I = 200 }; } + public void Method(StatelessType param, int size) { } + public void MethodIn(in StatelessType param, int size) { } + public void MethodOut(out StatelessType param, int size) { param = new StatelessType { I = 42 }; } + public void MethodRef(ref StatelessType param, int size) { param = new StatelessType { I = 200 }; } public StatelessType Return() => throw new NotImplementedException(); public StatelessType ReturnPreserveSig() => throw new NotImplementedException(); } @@ -37,14 +37,39 @@ internal class StatelessType public int I; } - [CustomMarshaller(typeof(StatelessType), MarshalMode.Default, typeof(StatelessTypeMarshaller))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementIn, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementRef, typeof(Bidirectional))] internal static class StatelessTypeMarshaller { - public static int FreeCount { get; private set; } - public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; + internal static class Bidirectional + { + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; - public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; + public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; - public static void Free(nint unmanaged) => FreeCount++; + public static void Free(nint unmanaged) => FreeCount++; + } + + internal static class ManagedToUnmanaged + { + public static int FreeCount { get; private set; } + public static void Free(nint unmanaged) => FreeCount++; + public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; + } + + internal static class UnmanagedToManaged + { + public static int FreeCount { get; private set; } + public static void Free(nint unmanaged) => FreeCount++; + public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; + } } } From 26283fe1cd3709d5d42cd70c610f2ca7a2d842ae Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:47:32 -0700 Subject: [PATCH 09/20] Expect System.Exception from ThrowForHR on non-windows --- .../RcwAroundCcwTests.cs | 46 ++++++------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index 9d35c9ded0136..92c232357b849 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -119,31 +119,34 @@ public void IInterface() [Fact] public void ICollectionMarshallingFails() { + Type hrExceptionType = PlatformDetection.IsWindows ? typeof(MarshallingFailureException) : typeof(Exception); + var obj = CreateWrapper(); Assert.Throws(() => - _ = obj.GetConstSize() + obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, 10) ); - Assert.Throws(() => - _ = obj.Get(out _) + Assert.Throws(hrExceptionType, () => + _ = obj.GetConstSize() ); - Assert.Throws(() => - obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, 10) + Assert.Throws(hrExceptionType, () => + _ = obj.Get(out _) ); } [Fact] public void IJaggedArrayMarshallingFails() { + Type hrExceptionType = PlatformDetection.IsWindows ? typeof(MarshallingFailureException) : typeof(Exception); var obj = CreateWrapper(); - Assert.Throws(() => + Assert.Throws(hrExceptionType, () => _ = obj.GetConstSize() ); - Assert.Throws(() => + Assert.Throws(hrExceptionType, () => _ = obj.Get(out _, out _) ); var array = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5, }, new int[] { 6, 7, 8, 9 } }; @@ -157,6 +160,7 @@ public void IJaggedArrayMarshallingFails() [Fact] public void IStringArrayMarshallingFails() { + Type hrExceptionType = PlatformDetection.IsWindows ? typeof(MarshallingFailureException) : typeof(Exception); var obj = CreateWrapper(); var strings = IStringArrayMarshallingFailsImpl.StartingStrings; @@ -178,35 +182,11 @@ public void IStringArrayMarshallingFails() { obj.ByValueInOutParam(strings); }); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows))] - public void IStringArrayMarshallingFailsOutWindows() - { - var obj = CreateWrapper(); - var strings = IStringArrayMarshallingFailsImpl.StartingStrings; - // This will fail in the native side and throw for HR on the managed to unmanaged stub. In Windows environments, this is will unwrap the exception. - Assert.Throws(() => - { - obj.OutParam(out strings); - }); - Assert.Throws(() => - { - _ = obj.ReturnValue(); - }); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows))] - public void IStringArrayMarshallingFailsOutNonWindows() - { - var obj = CreateWrapper(); - var strings = IStringArrayMarshallingFailsImpl.StartingStrings; - // This will fail in the native side and throw for HR on the managed to unmanaged stub. In non-Windows environments, this is a plain Exception. - Assert.Throws(() => + Assert.Throws(hrExceptionType, () => { obj.OutParam(out strings); }); - Assert.Throws(() => + Assert.Throws(hrExceptionType, () => { _ = obj.ReturnValue(); }); From 584567cdebcf42166902905c6660be8834970c82 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 3 Aug 2023 09:33:46 -0700 Subject: [PATCH 10/20] Expect System.Exception from ThrowForHR on NativeAOT --- .../ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index b167b14040721..1e0939e6c440e 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -21,6 +21,8 @@ public partial class RcwAroundCcwTests return ifaceObject; } + static bool SystemFindsComCalleeException() => PlatformDetection.IsWindows && PlatformDetection.IsNotNativeAot; + [Fact] public void IInt() { @@ -145,7 +147,7 @@ public void IStatelessFinallyMarshalling() [Fact] public void ICollectionMarshallingFails() { - Type hrExceptionType = PlatformDetection.IsWindows ? typeof(MarshallingFailureException) : typeof(Exception); + Type hrExceptionType = SystemFindsComCalleeException() ? typeof(MarshallingFailureException) : typeof(Exception); var obj = CreateWrapper(); @@ -165,7 +167,7 @@ public void ICollectionMarshallingFails() [Fact] public void IJaggedArrayMarshallingFails() { - Type hrExceptionType = PlatformDetection.IsWindows ? typeof(MarshallingFailureException) : typeof(Exception); + Type hrExceptionType = SystemFindsComCalleeException() ? typeof(MarshallingFailureException) : typeof(Exception); var obj = CreateWrapper(); Assert.Throws(hrExceptionType, () => @@ -186,7 +188,7 @@ public void IJaggedArrayMarshallingFails() [Fact] public void IStringArrayMarshallingFails() { - Type hrExceptionType = PlatformDetection.IsWindows ? typeof(MarshallingFailureException) : typeof(Exception); + Type hrExceptionType = SystemFindsComCalleeException() ? typeof(MarshallingFailureException) : typeof(Exception); var obj = CreateWrapper(); var strings = IStringArrayMarshallingFailsImpl.StartingStrings; From 028b5732bd9a196387ef305ceb477e36f0a616ff Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 3 Aug 2023 12:23:32 -0700 Subject: [PATCH 11/20] wip --- .../CustomTypeMarshallingGenerator.cs | 22 +---- .../Marshalling/ElementsMarshalling.cs | 5 +- .../ICustomTypeMarshallingStrategy.cs | 4 +- .../Marshalling/MarshallerHelpers.cs | 15 +++ .../StatefulMarshallingStrategy.cs | 77 +++++++++++++-- .../StatelessMarshallingStrategy.cs | 98 +++++++++++++++++-- ...nagedToManagedOwnershipTrackingStrategy.cs | 23 ++++- .../RcwAroundCcwTests.cs | 9 ++ .../IArrayOfStatelessElements.cs | 14 ++- 9 files changed, 226 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs index 8d7360b507438..0622a7608ded0 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CustomTypeMarshallingGenerator.cs @@ -97,10 +97,10 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return _nativeTypeMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); } break; - case StubCodeContext.Stage.CleanupCallerAllocated when GetCleanupStage(info, context) is StubCodeContext.Stage.CleanupCallerAllocated: - return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); - case StubCodeContext.Stage.CleanupCalleeAllocated when GetCleanupStage(info, context) is StubCodeContext.Stage.CleanupCalleeAllocated: - return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); + case StubCodeContext.Stage.CleanupCallerAllocated: + return _nativeTypeMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(info, context); + case StubCodeContext.Stage.CleanupCalleeAllocated: + return _nativeTypeMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(info, context); default: break; } @@ -117,20 +117,6 @@ private bool ShouldGenerateByValueOutMarshalling(TypePositionInfo info, StubCode && !_isPinned; } - /// - /// Returns which stage cleanup should be performed for the parameter. - /// - private static StubCodeContext.Stage GetCleanupStage(TypePositionInfo info, StubCodeContext context) - { - if (context.Direction is MarshalDirection.UnmanagedToManaged) - return StubCodeContext.Stage.CleanupCallerAllocated; - - if (MarshallerHelpers.GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) - return StubCodeContext.Stage.CleanupCalleeAllocated; - - return StubCodeContext.Stage.CleanupCallerAllocated; - } - public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) { return _nativeTypeMarshaller.UsesNativeIdentifier(info, context); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs index c132fa2c75d66..57027da94e312 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs @@ -440,8 +440,9 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo indexConstraintName, _elementInfo, _elementMarshaller, - StubCodeContext.Stage.CleanupCallerAllocated, - StubCodeContext.Stage.CleanupCalleeAllocated); + context.CurrentStage); + //StubCodeContext.Stage.CleanupCallerAllocated, + //StubCodeContext.Stage.CleanupCalleeAllocated); if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) { diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs index f9da5d32b3977..53223656b7ddf 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs @@ -13,7 +13,9 @@ internal interface ICustomTypeMarshallingStrategy { ManagedTypeInfo AsNativeType(TypePositionInfo info); - IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context); + IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context); + + IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context); IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 77cd004720dea..30ce2240e636c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -404,5 +404,20 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo } throw new UnreachableException("An element is either a return value or passed by value or by ref."); } + + /// + /// Returns which stage cleanup should be performed for the parameter. + /// + public static StubCodeContext.Stage GetCleanupStage(TypePositionInfo info, StubCodeContext context) + { + if (context.Direction is MarshalDirection.UnmanagedToManaged) + return StubCodeContext.Stage.CleanupCallerAllocated; + + if (GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) + return StubCodeContext.Stage.CleanupCalleeAllocated; + + return StubCodeContext.Stage.CleanupCallerAllocated; + } + } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs index edb226fd74cac..22c1c33c9f226 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs @@ -30,8 +30,28 @@ public ManagedTypeInfo AsNativeType(TypePositionInfo info) public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true; - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCallerAllocated) + yield break; + + if (!_shape.HasFlag(MarshallerShape.Free)) + yield break; + + // .Free(); + yield return ExpressionStatement( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(context.GetAdditionalIdentifier(info, MarshallerIdentifier)), + IdentifierName(ShapeMemberNames.Free)), + ArgumentList())); + } + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCalleeAllocated) + yield break; + if (!_shape.HasFlag(MarshallerShape.Free)) yield break; @@ -213,9 +233,14 @@ public ManagedTypeInfo AsNativeType(TypePositionInfo info) return _innerMarshaller.AsNativeType(info); } - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { - return _innerMarshaller.GenerateCleanupStatements(info, context); + return _innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(info, context); + } + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + return _innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(info, context); } public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context) @@ -371,12 +396,26 @@ public StatefulLinearCollectionMarshalling( } public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { + // We don't have anything to cleanup specifically related to this value, just the elements. We let the element marshaller decide whether to cleanup in callee or caller cleanup stage if (!_cleanupElements) - { yield break; + + StatementSyntax elementCleanup = _elementsMarshalling.GenerateElementCleanupStatement(info, context); + + if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement)) + { + yield return elementCleanup; } + } + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + // We don't have anything to cleanup specifically related to this value, just the elements. We let the element marshaller decide whether to cleanup in callee or caller cleanup stage + if (!_cleanupElements) + yield break; StatementSyntax elementCleanup = _elementsMarshalling.GenerateElementCleanupStatement(info, context); @@ -385,6 +424,7 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i yield return elementCleanup; } } + public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context) @@ -504,13 +544,36 @@ public StatefulFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller) public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + foreach (var statement in _innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(info, context)) + { + yield return statement; + } + + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCallerAllocated) + yield break; + + string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context); + // .Free(); + yield return ExpressionStatement( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(marshaller), + IdentifierName(ShapeMemberNames.Free)), + ArgumentList())); + } + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { - foreach (var statement in _innerMarshaller.GenerateCleanupStatements(info, context)) + foreach (var statement in _innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(info, context)) { yield return statement; } + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCalleeAllocated) + yield break; + string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context); // .Free(); yield return ExpressionStatement( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs index 6303abe2bfbc8..5028fae35d3f5 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs @@ -33,7 +33,9 @@ public ManagedTypeInfo AsNativeType(TypePositionInfo info) public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true; - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty(); + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty(); + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty(); public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { @@ -159,7 +161,8 @@ public StatelessCallerAllocatedBufferMarshalling(ICustomTypeMarshallingStrategy } public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context); + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(info, context); + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(info, context); public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context) @@ -266,9 +269,30 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller, public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCallerAllocated) + yield break; + + foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(info, context)) + { + yield return statement; + } + // .Free(); + yield return ExpressionStatement( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + _marshallerType, + IdentifierName(ShapeMemberNames.Free)), + ArgumentList(SingletonSeparatedList( + Argument(IdentifierName(context.GetIdentifiers(info).native)))))); + } + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { - foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context)) + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCalleeAllocated) + yield break; + + foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(info, context)) { yield return statement; } @@ -316,8 +340,30 @@ public ManagedTypeInfo AsNativeType(TypePositionInfo info) return _unmanagedType; } - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCallerAllocated) + yield break; + + if (MarshallerHelpers.GetMarshalDirection(info, context) == MarshalDirection.ManagedToUnmanaged) + { + yield return EmptyStatement(); + yield break; + } + + string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); + yield return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(numElementsIdentifier), + _numElementsExpression)); + } + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCalleeAllocated) + yield break; + if (MarshallerHelpers.GetMarshalDirection(info, context) == MarshalDirection.ManagedToUnmanaged) { yield return EmptyStatement(); @@ -554,12 +600,13 @@ public StatelessLinearCollectionMarshalling( public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _unmanagedType; - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { if (!_cleanupElementsAndSpace) { yield break; } + StatementSyntax elementCleanup = _elementsMarshalling.GenerateElementCleanupStatement(info, context); if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement)) @@ -577,9 +624,44 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i yield return elementCleanup; } - foreach (var statement in _spaceMarshallingStrategy.GenerateCleanupStatements(info, context)) + if (MarshallerHelpers.GetCleanupStage(info, context) is StubCodeContext.Stage.CleanupCallerAllocated) { - yield return statement; + foreach (var statement in _spaceMarshallingStrategy.GenerateCleanupCallerAllocatedResourcesStatements(info, context)) + { + yield return statement; + } + } + } + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + if (!_cleanupElementsAndSpace) + { + yield break; + } + StatementSyntax elementCleanup = _elementsMarshalling.GenerateElementCleanupStatement(info, context); + + if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement)) + { + // If we don't have the numElements variable still available from unmarshal or marshal stage, we need to reassign that again + if (!context.AdditionalTemporaryStateLivesAcrossStages) + { + string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); + yield return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(numElementsIdentifier), + _numElementsExpression)); + } + yield return elementCleanup; + } + + if (MarshallerHelpers.GetCleanupStage(info, context) is StubCodeContext.Stage.CleanupCallerAllocated) + { + foreach (var statement in _spaceMarshallingStrategy.GenerateCleanupCalleeAllocatedResourcesStatements(info, context)) + { + yield return statement; + } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs index c6b5c7685b027..ba43659536d52 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -25,7 +24,8 @@ public UnmanagedToManagedOwnershipTrackingStrategy(ICustomTypeMarshallingStrateg public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context); + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(info, context); + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(info, context); public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context) @@ -91,15 +91,30 @@ public CleanupOwnedOriginalValueMarshalling(ICustomTypeMarshallingStrategy inner public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) + public IEnumerable GenerateCleanupCallerAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCallerAllocated) + yield break; // if () // { // // } yield return IfStatement( IdentifierName(context.GetAdditionalIdentifier(info, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)), - Block(_innerMarshaller.GenerateCleanupStatements(info, new OwnedValueCodeContext(context)))); + Block(_innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(info, new OwnedValueCodeContext(context)))); + } + + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) + { + if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCalleeAllocated) + yield break; + // if () + // { + // + // } + yield return IfStatement( + IdentifierName(context.GetAdditionalIdentifier(info, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)), + Block(_innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(info, new OwnedValueCodeContext(context)))); } public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index 1e0939e6c440e..0a7c66317d977 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -91,6 +91,15 @@ public void IIntArray_Failing() Assert.True(data is [2, 4, 6]); } + [Fact] + public void IArrayOfStatelessElementsThrows() + { + var obj = CreateWrapper(); + var data = new StatelessType[10]; + obj.MethodContentsOut(data, 10) + + } + [Fact] public void IJaggedIntArray() { diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs index abb8842f1e0a2..101869ef2bf97 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs @@ -16,7 +16,7 @@ internal partial interface IArrayOfStatelessElements void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType[] param, int size); void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType[] param, int size); void MethodContentsIn([MarshalUsing(CountElementName = nameof(size))][In] StatelessType[] param, int size); - void MethodContentsOut([MarshalUsing(CountElementName = nameof(size))][Out] StatelessType[] param, int size); + void MethodContentsOut([MarshalUsing(CountElementName = nameof(size))][Out] StatelessType[] paramya, int size); void MethodContentsInOut([MarshalUsing(CountElementName = nameof(size))][In, Out] StatelessType[] param, int size); } @@ -72,4 +72,16 @@ public void MethodRef(ref StatelessType[] param, int size) } } } + + [GeneratedComClass] + internal partial class ArrayOfStatelessElementsThrows : IArrayOfStatelessElements + { + public void Method(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodContentsIn(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodContentsInOut(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodContentsOut(StatelessType[] paramya, int size) => throw new ManagedComMethodFailureException(); + public void MethodIn(in StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodOut(out StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodRef(ref StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + } } From 73369c1f1566729429037ca5cf6e9099e744f5b5 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:56:36 -0700 Subject: [PATCH 12/20] Cleanup ContentsOut guarded --- .../ManagedToNativeVTableMethodGenerator.cs | 4 ++-- .../ManagedHResultExceptionMarshallerFactory.cs | 4 ++-- .../TypePositionInfo.cs | 7 ++++--- .../ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs | 9 +++++++-- .../ComInterfaces/IStatefulFinallyMarshalling.cs | 3 ++- .../ComInterfaces/ManagedComMethodFailureException.cs | 11 +++++++++++ 6 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs index f6439d4221142..dd18f01bb7c88 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs @@ -171,6 +171,8 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray().NestFixedStatements(fixedBlock)); + tryStatements.AddRange(statements.NotifyForSuccessfulInvoke); + // = true; if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupCalleeAllocated.IsEmpty)) { @@ -179,8 +181,6 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray Generate(TypePositionInfo info, StubCodeCont { Debug.Assert(info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo); - if (context.CurrentStage != StubCodeContext.Stage.Unmarshal) + if (context.CurrentStage != StubCodeContext.Stage.NotifyForSuccessfulInvoke) { yield break; } @@ -83,7 +83,7 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont { Debug.Assert(info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo); - if (context.CurrentStage != StubCodeContext.Stage.Unmarshal) + if (context.CurrentStage != StubCodeContext.Stage.NotifyForSuccessfulInvoke) { yield break; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs index bb40116731362..4118cbf7badaa 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs @@ -45,9 +45,10 @@ public enum ByValueContentsMarshalKind /// public sealed record TypePositionInfo(ManagedTypeInfo ManagedType, MarshallingInfo MarshallingAttributeInfo) { - public const int UnsetIndex = int.MinValue; - public const int ReturnIndex = UnsetIndex + 1; - public const int ExceptionIndex = UnsetIndex + 2; + public const int UnsetIndex = ExceptionIndex + 1; + public const int ReturnIndex = ExceptionIndex + 2; + // Make ExceptionIndex the min so that it is always the first to be dealt with in each stage + public const int ExceptionIndex = int.MinValue; public static bool IsSpecialIndex(int index) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index 0a7c66317d977..780c34b621f68 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -96,8 +96,13 @@ public void IArrayOfStatelessElementsThrows() { var obj = CreateWrapper(); var data = new StatelessType[10]; - obj.MethodContentsOut(data, 10) - + var oldFreeCount = StatelessTypeMarshaller.UnmanagedToManaged.FreeCount; + try + { + obj.MethodContentsOut(data, 10); + } + catch (Exception) { } + Assert.Equal(oldFreeCount, StatelessTypeMarshaller.UnmanagedToManaged.FreeCount); } [Fact] diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs index 3e0f8e3bb70d1..3c9951c03fafe 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs @@ -19,6 +19,7 @@ internal partial interface IStatefulFinallyMarshalling [PreserveSig] StatefulFinallyType ReturnPreserveSig(); } + [GeneratedComClass] internal partial class StatefulFinallyMarshalling : IStatefulFinallyMarshalling { @@ -74,7 +75,7 @@ public void FromManaged(StatefulFinallyType managed) managed_i = managed.i; } - public nint ToUnmanaged() + public StatefulFinallyNative ToUnmanaged() { return new StatefulFinallyNative() { i = this.managed_i }; } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs new file mode 100644 index 0000000000000..de5aea6228a14 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace SharedTypes.ComInterfaces +{ + internal class ManagedComMethodFailureException : Exception + { + } +} From e6c251be1b5bf9db59eb586f1eeab43352d10ad3 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:47:00 -0700 Subject: [PATCH 13/20] Don't cleanup [Out] elements in unmanaged to managed stub --- .../Marshalling/ElementsMarshalling.cs | 3 +- .../RcwAroundCcwTests.cs | 47 ++++++++++++++++++- .../ComInterfaces/IStatelessMarshalling.cs | 1 + 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs index a4f92553873d2..8adf2012c5095 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs @@ -545,8 +545,7 @@ public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalState new FreeAlwaysOwnedOriginalValueGenerator(_elementMarshaller), StubCodeContext.Stage.Marshal, StubCodeContext.Stage.PinnedMarshal, - StubCodeContext.Stage.CleanupCallerAllocated, - StubCodeContext.Stage.CleanupCalleeAllocated)); + StubCodeContext.Stage.CleanupCallerAllocated)); } private static List GenerateElementStages( diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs index f111394fc5846..c4142457a1a56 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -91,18 +91,61 @@ public void IIntArray_Failing() Assert.True(data is [2, 4, 6]); } + [Fact] + public void IArrayOfStatelessElements() + { + var obj = CreateWrapper(); + var data = new StatelessType[10]; + + // ByValueContentsOut should only free the returned values + var oldFreeCount = StatelessTypeMarshaller.AllFreeCount; + obj.MethodContentsOut(data, data.Length); + Assert.Equal(oldFreeCount + 10, StatelessTypeMarshaller.AllFreeCount); + + // ByValueContentsOut should only free the elements after the call + oldFreeCount = StatelessTypeMarshaller.AllFreeCount; + obj.MethodContentsIn(data, data.Length); + Assert.Equal(oldFreeCount + 10, StatelessTypeMarshaller.AllFreeCount); + + // ByValueContentsInOut should free elements in both directions + oldFreeCount = StatelessTypeMarshaller.AllFreeCount; + obj.MethodContentsInOut(data, data.Length); + Assert.Equal(oldFreeCount + 20, StatelessTypeMarshaller.AllFreeCount); + } + [Fact] public void IArrayOfStatelessElementsThrows() { var obj = CreateWrapper(); var data = new StatelessType[10]; - var oldFreeCount = StatelessTypeMarshaller.UnmanagedToManaged.FreeCount; + var oldFreeCount = StatelessTypeMarshaller.AllFreeCount; try { obj.MethodContentsOut(data, 10); } catch (Exception) { } - Assert.Equal(oldFreeCount, StatelessTypeMarshaller.UnmanagedToManaged.FreeCount); + Assert.Equal(oldFreeCount, StatelessTypeMarshaller.AllFreeCount); + + for (int i = 0; i < 10; i++) + { + data[i] = new StatelessType() { I = i }; + } + + oldFreeCount = StatelessTypeMarshaller.AllFreeCount; + try + { + obj.MethodContentsIn(data, 10); + } + catch (Exception) { } + Assert.Equal(oldFreeCount + 10, StatelessTypeMarshaller.AllFreeCount); + + oldFreeCount = StatelessTypeMarshaller.AllFreeCount; + try + { + obj.MethodContentsInOut(data, 10); + } + catch (Exception) { } + Assert.Equal(oldFreeCount + 10, StatelessTypeMarshaller.AllFreeCount); } [Fact] diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs index 325064ad8cbf9..3593d5ff11570 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs @@ -48,6 +48,7 @@ internal class StatelessType [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementRef, typeof(Bidirectional))] internal static class StatelessTypeMarshaller { + public static int AllFreeCount => Bidirectional.FreeCount + UnmanagedToManaged.FreeCount + ManagedToUnmanaged.FreeCount; internal static class Bidirectional { public static int FreeCount { get; private set; } From aba85f5207d4976df38e1c2022ffcce7d95d5f63 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:16:43 -0700 Subject: [PATCH 14/20] Fix some ByValueOut Behavior --- .../Marshalling/ElementsMarshalling.cs | 19 ++++++++++++++----- .../StatelessMarshallingStrategy.cs | 4 ++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs index 8adf2012c5095..843549befce39 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; @@ -441,8 +442,6 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo _elementInfo, _elementMarshaller, context.CurrentStage); - //StubCodeContext.Stage.CleanupCallerAllocated, - //StubCodeContext.Stage.CleanupCalleeAllocated); if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) { @@ -533,6 +532,18 @@ public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalState .WithInitializer(EqualsValueClause( CollectionSource.GetManagedValuesDestination(info, context)))))); + StubCodeContext.Stage[] stagesToGenerate; + + // Until we separate CalleeAllocated cleanup and CallerAllocated cleanup in unmanaged to managed, we'll need this hack + if (context.Direction is MarshalDirection.UnmanagedToManaged && info.ByValueContentsMarshalKind is ByValueContentsMarshalKind.Out) + { + stagesToGenerate = new[] { StubCodeContext.Stage.Marshal, StubCodeContext.Stage.PinnedMarshal }; + } + else + { + stagesToGenerate = new[] { StubCodeContext.Stage.Marshal, StubCodeContext.Stage.PinnedMarshal, StubCodeContext.Stage.CleanupCallerAllocated, StubCodeContext.Stage.CleanupCalleeAllocated }; + } + return Block( setNumElements, unmanagedValuesSource, @@ -543,9 +554,7 @@ public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalState IdentifierName(numElementsIdentifier), _elementInfo, new FreeAlwaysOwnedOriginalValueGenerator(_elementMarshaller), - StubCodeContext.Stage.Marshal, - StubCodeContext.Stage.PinnedMarshal, - StubCodeContext.Stage.CleanupCallerAllocated)); + stagesToGenerate)); } private static List GenerateElementStages( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs index 5028fae35d3f5..889ff7b9eda91 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs @@ -728,6 +728,10 @@ public IEnumerable GenerateUnmarshalStatements(TypePositionInfo // If the parameter is marshalled by-value [Out], then we don't marshal the contents of the collection. // We do clear the span, so that if the invoke target doesn't fill it, we aren't left with undefined content. yield return _elementsMarshalling.GenerateClearManagedValuesDestination(info, context); + foreach (var statement in _spaceMarshallingStrategy.GenerateUnmarshalStatements(info, context)) + { + yield return statement; + } yield break; } From 51bc04c13cc2e1cbc8be4056ff995189f56b60b9 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:19:53 -0700 Subject: [PATCH 15/20] Cleanup some changes from debugging --- .../Marshalling/ElementsMarshalling.cs | 2 -- .../Marshalling/MarshallerHelpers.cs | 14 -------------- .../TypePositionInfo.cs | 7 +++---- .../ComInterfaces/IArrayOfStatelessElements.cs | 4 ++-- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs index a4f92553873d2..3deb6c8f035dc 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ElementsMarshalling.cs @@ -441,8 +441,6 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo _elementInfo, _elementMarshaller, context.CurrentStage); - //StubCodeContext.Stage.CleanupCallerAllocated, - //StubCodeContext.Stage.CleanupCalleeAllocated); if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) { diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 3203ca4956455..7508c135f6417 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -405,20 +405,6 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo throw new UnreachableException("An element is either a return value or passed by value or by ref."); } - /// - /// Returns which stage cleanup should be performed for the parameter. - /// - public static StubCodeContext.Stage GetCleanupStage(TypePositionInfo info, StubCodeContext context) - { - if (context.Direction is MarshalDirection.UnmanagedToManaged) - return StubCodeContext.Stage.CleanupCallerAllocated; - - if (GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) - return StubCodeContext.Stage.CleanupCalleeAllocated; - - return StubCodeContext.Stage.CleanupCallerAllocated; - } - /// Ensure that the count of a collection is available at call time if the parameter is not an out parameter. /// It only looks at an indirection level of 0 (the size of the outer array), so there are some holes in /// analysis if the parameter is a multidimensional array, but that case seems very unlikely to be hit. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs index f8bb23636b4e4..76697b0e6dd6a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs @@ -45,10 +45,9 @@ public enum ByValueContentsMarshalKind /// public sealed record TypePositionInfo(ManagedTypeInfo ManagedType, MarshallingInfo MarshallingAttributeInfo) { - public const int UnsetIndex = ExceptionIndex + 1; - public const int ReturnIndex = ExceptionIndex + 2; - // Make ExceptionIndex the min so that it is always the first to be dealt with in each stage - public const int ExceptionIndex = int.MinValue; + public const int UnsetIndex = int.MinValue; + public const int ReturnIndex = UnsetIndex + 1; + public const int ExceptionIndex = UnsetIndex + 2; public static bool IsSpecialIndex(int index) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs index 101869ef2bf97..658606d84ee3c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs @@ -16,7 +16,7 @@ internal partial interface IArrayOfStatelessElements void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType[] param, int size); void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType[] param, int size); void MethodContentsIn([MarshalUsing(CountElementName = nameof(size))][In] StatelessType[] param, int size); - void MethodContentsOut([MarshalUsing(CountElementName = nameof(size))][Out] StatelessType[] paramya, int size); + void MethodContentsOut([MarshalUsing(CountElementName = nameof(size))][Out] StatelessType[] param, int size); void MethodContentsInOut([MarshalUsing(CountElementName = nameof(size))][In, Out] StatelessType[] param, int size); } @@ -79,7 +79,7 @@ internal partial class ArrayOfStatelessElementsThrows : IArrayOfStatelessElement public void Method(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); public void MethodContentsIn(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); public void MethodContentsInOut(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - public void MethodContentsOut(StatelessType[] paramya, int size) => throw new ManagedComMethodFailureException(); + public void MethodContentsOut(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); public void MethodIn(in StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); public void MethodOut(out StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); public void MethodRef(ref StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); From 2f836f6c1b2e1994c9e93f99c5f701fdc1b7cf7a Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:33:46 -0700 Subject: [PATCH 16/20] Too many commits --- .../Marshalling/MarshallerHelpers.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 7508c135f6417..3203ca4956455 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -405,6 +405,20 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo throw new UnreachableException("An element is either a return value or passed by value or by ref."); } + /// + /// Returns which stage cleanup should be performed for the parameter. + /// + public static StubCodeContext.Stage GetCleanupStage(TypePositionInfo info, StubCodeContext context) + { + if (context.Direction is MarshalDirection.UnmanagedToManaged) + return StubCodeContext.Stage.CleanupCallerAllocated; + + if (GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) + return StubCodeContext.Stage.CleanupCalleeAllocated; + + return StubCodeContext.Stage.CleanupCallerAllocated; + } + /// Ensure that the count of a collection is available at call time if the parameter is not an out parameter. /// It only looks at an indirection level of 0 (the size of the outer array), so there are some holes in /// analysis if the parameter is a multidimensional array, but that case seems very unlikely to be hit. From 464902412da2a98003698ee2d7cbf62d29b1d3e4 Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Mon, 7 Aug 2023 15:56:09 +0000 Subject: [PATCH 17/20] PR feedback --- .../Marshalling/MarshallerHelpers.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 3203ca4956455..a14f4343a5d90 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -419,6 +419,7 @@ public static StubCodeContext.Stage GetCleanupStage(TypePositionInfo info, StubC return StubCodeContext.Stage.CleanupCallerAllocated; } + /// /// Ensure that the count of a collection is available at call time if the parameter is not an out parameter. /// It only looks at an indirection level of 0 (the size of the outer array), so there are some holes in /// analysis if the parameter is a multidimensional array, but that case seems very unlikely to be hit. @@ -430,10 +431,10 @@ public static void ValidateCountInfoAvailableAtCall(MarshalDirection stubDirecti if (stubDirection is MarshalDirection.ManagedToUnmanaged) return; - if (info.MarshallingAttributeInfo is NativeLinearCollectionMarshallingInfo collectionMarshallingInfo - && collectionMarshallingInfo.ElementCountInfo is CountElementCountInfo countInfo - && !(info.RefKind is RefKind.Out - || info.ManagedIndex is TypePositionInfo.ReturnIndex)) + if (!(info.RefKind is RefKind.Out + || info.ManagedIndex is TypePositionInfo.ReturnIndex) + && info.MarshallingAttributeInfo is NativeLinearCollectionMarshallingInfo collectionMarshallingInfo + && collectionMarshallingInfo.ElementCountInfo is CountElementCountInfo countInfo) { if (countInfo.ElementInfo.IsByRef && countInfo.ElementInfo.RefKind is RefKind.Out) { From 5f3368eb5c28665267deed1ad63d12a3b43441bb Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Mon, 7 Aug 2023 23:15:45 +0000 Subject: [PATCH 18/20] Empty to rerun CI From aebc8877988f1c19ead925cf66f371444e1d2231 Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Tue, 8 Aug 2023 01:45:48 +0000 Subject: [PATCH 19/20] PR Feedback --- .../LibraryImportGenerator/Pipeline.md | 21 ++++++++++++------- .../Marshalling/MarshallerHelpers.cs | 14 +++++++++---- .../StatelessMarshallingStrategy.cs | 7 +------ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/docs/design/libraries/LibraryImportGenerator/Pipeline.md b/docs/design/libraries/LibraryImportGenerator/Pipeline.md index 9533241c9ad29..bd38953951751 100644 --- a/docs/design/libraries/LibraryImportGenerator/Pipeline.md +++ b/docs/design/libraries/LibraryImportGenerator/Pipeline.md @@ -91,8 +91,12 @@ The stub code generator itself will handle some initial setup and variable decla - Call `Generate` on the marshalling generator for every parameter 1. `GuaranteedUnmarshal`: conversion of native to managed data even when an exception is thrown - Call `Generate` on the marshalling generator for every parameter. -1. `Cleanup`: free any allocated resources + - If this stage has any statements, put them in an if statement where the condition represents whether the call succeeded +1. `CleanupCallerAllocated`: free any resources allocated by the caller - Call `Generate` on the marshalling generator for every parameter +1. `CleanupCalleeAllocated`: if the native method succeeded, free any resources allocated by the callee (`out` parameters and return values) + - Call `Generate` on the marshalling generator for every parameter + - If this stage has any statements, put them in an if statement where the condition represents whether the call succeeded Generated P/Invoke structure (if no code is generated for `GuaranteedUnmarshal` and `Cleanup`, the `try-finally` is omitted): ```C# @@ -113,7 +117,8 @@ try finally { << GuaranteedUnmarshal >> - << Cleanup >> + << CleanupCalleeAllocated >> + << CleanupCallerAllocated >> } ``` @@ -138,12 +143,12 @@ Support for these features is indicated in code by the `abstract` `SingleFrameSp The various scenarios mentioned above have different levels of support for these specialized features: -| Scenarios | Pinning and Stack allocation across the native context | Storing additional temporary state in locals | -|------|-----|-----| -| P/Invoke | supported | supported | -| Reverse P/Invoke | unsupported | supported | -| User-defined structure content marshalling | unsupported | unsupported | -| non-blittable array marshalling | unsupported | unuspported | +| Scenarios | Pinning and Stack allocation across the native context | Storing additional temporary state in locals | +|--------------------------------------------|--------------------------------------------------------|----------------------------------------------| +| P/Invoke | supported | supported | +| Reverse P/Invoke | unsupported | supported | +| User-defined structure content marshalling | unsupported | unsupported | +| non-blittable array marshalling | unsupported | unuspported | To help enable developers to use the full model described in the [Struct Marshalling design](./StructMarshalling.md), we declare that in contexts where `AdditionalTemporaryStateLivesAcrossStages` is false, developers can still assume that state declared in the `Setup` phase is valid in any phase, but any side effects in code emitted in a phase other than `Setup` will not be guaranteed to be visible in other phases. This enables developers to still use the identifiers declared in the `Setup` phase in their other phases, but they'll need to take care to design their generators to handle these rules. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 3203ca4956455..40f2a22763873 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -410,13 +410,19 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo /// public static StubCodeContext.Stage GetCleanupStage(TypePositionInfo info, StubCodeContext context) { + // Unmanaged to managed doesn't properly handle lifetimes right now and will default to the original behavior. + // Failures will only occur when marshalling fails, and would only cause leaks, not double frees. + // See https://github.com/dotnet/runtime/issues/89483 for more details if (context.Direction is MarshalDirection.UnmanagedToManaged) return StubCodeContext.Stage.CleanupCallerAllocated; - if (GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged) - return StubCodeContext.Stage.CleanupCalleeAllocated; - - return StubCodeContext.Stage.CleanupCallerAllocated; + return GetMarshalDirection(info, context) switch + { + MarshalDirection.UnmanagedToManaged => StubCodeContext.Stage.CleanupCalleeAllocated, + MarshalDirection.ManagedToUnmanaged => StubCodeContext.Stage.CleanupCallerAllocated, + MarshalDirection.Bidirectional => StubCodeContext.Stage.CleanupCallerAllocated, + _ => throw new UnreachableException() + }; } /// Ensure that the count of a collection is available at call time if the parameter is not an out parameter. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs index 5028fae35d3f5..a30a96eef97ff 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs @@ -287,6 +287,7 @@ public IEnumerable GenerateCleanupCallerAllocatedResourcesState ArgumentList(SingletonSeparatedList( Argument(IdentifierName(context.GetIdentifiers(info).native)))))); } + public IEnumerable GenerateCleanupCalleeAllocatedResourcesStatements(TypePositionInfo info, StubCodeContext context) { if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCalleeAllocated) @@ -345,12 +346,6 @@ public IEnumerable GenerateCleanupCallerAllocatedResourcesState if (MarshallerHelpers.GetCleanupStage(info, context) is not StubCodeContext.Stage.CleanupCallerAllocated) yield break; - if (MarshallerHelpers.GetMarshalDirection(info, context) == MarshalDirection.ManagedToUnmanaged) - { - yield return EmptyStatement(); - yield break; - } - string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); yield return ExpressionStatement( AssignmentExpression( From dce6751ba1cea105c7b1a8fcd2f6d001e8661792 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 8 Aug 2023 10:28:06 -0700 Subject: [PATCH 20/20] Add comments with the C# expression above the builders --- .../Marshalling/StatelessMarshallingStrategy.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs index a30a96eef97ff..e8074124e7b99 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs @@ -347,6 +347,7 @@ public IEnumerable GenerateCleanupCallerAllocatedResourcesState yield break; string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); + // = ; yield return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, @@ -366,6 +367,7 @@ public IEnumerable GenerateCleanupCalleeAllocatedResourcesState } string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); + // = ; yield return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, @@ -438,6 +440,7 @@ public IEnumerable GenerateMarshalStatements(TypePositionInfo i public IEnumerable GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty(); public IEnumerable GenerateSetupStatements(TypePositionInfo info, StubCodeContext context) { + // int ; string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); yield return LocalDeclarationStatement( VariableDeclaration( @@ -609,6 +612,7 @@ public IEnumerable GenerateCleanupCallerAllocatedResourcesState // If we don't have the numElements variable still available from unmarshal or marshal stage, we need to reassign that again if (!context.AdditionalTemporaryStateLivesAcrossStages) { + // = ; string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); yield return ExpressionStatement( AssignmentExpression( @@ -641,6 +645,7 @@ public IEnumerable GenerateCleanupCalleeAllocatedResourcesState // If we don't have the numElements variable still available from unmarshal or marshal stage, we need to reassign that again if (!context.AdditionalTemporaryStateLivesAcrossStages) { + // = ; string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context); yield return ExpressionStatement( AssignmentExpression(