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 1/9] 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 9221bef508f3b9..5a948c271fa494 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<StatementSyntax>();
             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<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 2c1d49fd3dd12e..0d2b17dace2298 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<StatementSyntax>();
             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<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 5acdc3072a225b..6c656c19d045d1 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<FunctionPointerUnm
                         BracketedArgumentList(SingletonSeparatedList(
                             Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(index)))))),
                     callConv));
-            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);
 
             if (_setLastError)
@@ -143,7 +143,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray<FunctionPointerUnm
                     initializeToDefault: false));
             }
 
-            if (!statements.GuaranteedUnmarshal.IsEmpty)
+            if (!(statements.GuaranteedUnmarshal.IsEmpty && statements.CleanupOut.IsEmpty))
             {
                 setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true));
             }
@@ -172,7 +172,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray<FunctionPointerUnm
             tryStatements.Add(statements.Pin.CastArray<FixedStatementSyntax>().NestFixedStatements(fixedBlock));
 
             // <invokeSucceeded> = 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<FunctionPointerUnm
 
             List<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 6d255912aedaab..76e77843e50799 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<CatchClauseSyntax> 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 34d09a70a66e6f..d55b100b664ffb 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<StatementSyntax>();
@@ -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));
             // <invokeSucceeded> = 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<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 e8e3de05ffff27..8a3033a1a8fea8 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<StatementSyntax> Unmarshal { get; init; }
         public ImmutableArray<StatementSyntax> NotifyForSuccessfulInvoke { get; init; }
         public ImmutableArray<StatementSyntax> GuaranteedUnmarshal { get; init; }
-        public ImmutableArray<StatementSyntax> Cleanup { get; init; }
+        public ImmutableArray<StatementSyntax> CleanupIn { get; init; }
+        public ImmutableArray<StatementSyntax> CleanupOut { get; init; }
 
         public ImmutableArray<CatchClauseSyntax> 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<StatementSyntax> GenerateStatementsForStubContext(
             ImmutableArray<StatementSyntax>.Builder statementsToUpdate = ImmutableArray.CreateBuilder<StatementSyntax>();
             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 7873781e475df4..f2bb0ca0eb709c 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<StatementSyntax> 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 d457e37a7f2b23..03e8679db83e70 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<StatementSyntax> 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 86275796f208ee..7ae2a4d7bb3f31 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 4e0b3bcf40f922..2f9a277a93f2a9 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<StatementSyntax> 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 5b9d0866f743cd..927c8b1785c578 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<StatementSyntax> GenerateSetupStatements(TypePositionInfo inf
 
     /// <summary>
     /// Marshalling strategy that uses the tracking variables introduced by <see cref="UnmanagedToManagedOwnershipTrackingStrategy"/> to cleanup the original value if the original value is owned
-    /// in the <see cref="StubCodeContext.Stage.Cleanup"/> stage.
+    /// in the <see cref="StubCodeContext.Stage.CleanupIn"/> stage.
     /// </summary>
     internal sealed class CleanupOwnedOriginalValueMarshalling : ICustomTypeMarshallingStrategy
     {
@@ -119,7 +119,7 @@ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo i
 
     /// <summary>
     /// Marshalling strategy to cache the initial value of a given <see cref="TypePositionInfo"/> 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 <see cref="StubCodeContext.Stage.Cleanup"/> stage, so additional ownership tracking is extraneous.
+    /// Useful in scenarios where the value is always owned in all code-paths that reach the <see cref="StubCodeContext.Stage.CleanupIn"/> stage, so additional ownership tracking is extraneous.
     /// </summary>
     internal sealed class FreeAlwaysOwnedOriginalValueGenerator : IMarshallingGenerator
     {
@@ -138,7 +138,7 @@ public IEnumerable<StatementSyntax> 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 35e07f1f7dc477..fb7cc299f083c9 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,
 
             /// <summary>
-            /// Perform any cleanup required
+            /// Perform any cleanup required on caller allocated resources
             /// </summary>
-            Cleanup,
+            CleanupIn,
+
+            /// <summary>
+            /// Perform any cleanup required on callee allocated resources
+            /// </summary>
+            CleanupOut,
 
             /// <summary>
             /// 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 cca4aa23ea2a9e..c2368fd5b229b9 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 2/9] 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 5a948c271fa494..0a18241ef7c767 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<StatementSyntax>();
             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<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 0d2b17dace2298..1f415b8338008e 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<StatementSyntax>();
             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<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 6c656c19d045d1..f6439d42211428 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<FunctionPointerUnm
                         BracketedArgumentList(SingletonSeparatedList(
                             Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(index)))))),
                     callConv));
-            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);
 
             if (_setLastError)
@@ -143,7 +143,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray<FunctionPointerUnm
                     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));
             }
@@ -172,7 +172,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray<FunctionPointerUnm
             tryStatements.Add(statements.Pin.CastArray<FixedStatementSyntax>().NestFixedStatements(fixedBlock));
 
             // <invokeSucceeded> = 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<FunctionPointerUnm
 
             List<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 76e77843e50799..21887ac354d9a6 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<CatchClauseSyntax> 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 d55b100b664ffb..939a623ab7dba8 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<StatementSyntax>();
@@ -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));
             // <invokeSucceeded> = 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<StatementSyntax> allStatements = setupStatements;
             List<StatementSyntax> finallyStatements = new List<StatementSyntax>();
-            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 8a3033a1a8fea8..691309dde5487f 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<StatementSyntax> Unmarshal { get; init; }
         public ImmutableArray<StatementSyntax> NotifyForSuccessfulInvoke { get; init; }
         public ImmutableArray<StatementSyntax> GuaranteedUnmarshal { get; init; }
-        public ImmutableArray<StatementSyntax> CleanupIn { get; init; }
-        public ImmutableArray<StatementSyntax> CleanupOut { get; init; }
+        public ImmutableArray<StatementSyntax> CleanupCallerAllocated { get; init; }
+        public ImmutableArray<StatementSyntax> CleanupCalleeAllocated { get; init; }
 
         public ImmutableArray<CatchClauseSyntax> 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<StatementSyntax> GenerateStatementsForStubContext(
             ImmutableArray<StatementSyntax>.Builder statementsToUpdate = ImmutableArray.CreateBuilder<StatementSyntax>();
             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 f2bb0ca0eb709c..18b5bcce0813d9 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<StatementSyntax> 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 03e8679db83e70..c132fa2c75d661 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<StatementSyntax> 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 7ae2a4d7bb3f31..235013c72c9561 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 2f9a277a93f2a9..9f4700ec00f936 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<StatementSyntax> 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 927c8b1785c578..c6b5c7685b027b 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<StatementSyntax> GenerateSetupStatements(TypePositionInfo inf
 
     /// <summary>
     /// Marshalling strategy that uses the tracking variables introduced by <see cref="UnmanagedToManagedOwnershipTrackingStrategy"/> to cleanup the original value if the original value is owned
-    /// in the <see cref="StubCodeContext.Stage.CleanupIn"/> stage.
+    /// in the <see cref="StubCodeContext.Stage.CleanupCallerAllocated"/> stage.
     /// </summary>
     internal sealed class CleanupOwnedOriginalValueMarshalling : ICustomTypeMarshallingStrategy
     {
@@ -119,7 +119,7 @@ public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo i
 
     /// <summary>
     /// Marshalling strategy to cache the initial value of a given <see cref="TypePositionInfo"/> 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 <see cref="StubCodeContext.Stage.CleanupIn"/> stage, so additional ownership tracking is extraneous.
+    /// Useful in scenarios where the value is always owned in all code-paths that reach the <see cref="StubCodeContext.Stage.CleanupCallerAllocated"/> stage, so additional ownership tracking is extraneous.
     /// </summary>
     internal sealed class FreeAlwaysOwnedOriginalValueGenerator : IMarshallingGenerator
     {
@@ -138,7 +138,7 @@ public IEnumerable<StatementSyntax> 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 fb7cc299f083c9..50af716eb3a51f 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
             /// <summary>
             /// Perform any cleanup required on caller allocated resources
             /// </summary>
-            CleanupIn,
+            CleanupCallerAllocated,
 
             /// <summary>
             /// Perform any cleanup required on callee allocated resources
             /// </summary>
-            CleanupOut,
+            CleanupCalleeAllocated,
 
             /// <summary>
             /// 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 3/9] 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 18b5bcce0813d9..bf993d5f40aee8 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<StatementSyntax> 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 235013c72c9561..80fa7d867c2ec4 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 4/9] 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 691309dde5487f..d611eb59cd3b40 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 54669fa6c380ec..fb1c6ebba8b3a2 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 5/9] 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 c2368fd5b229b9..a820e032e798ff 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<GetAndSetInt, IGetAndSetInt>();
-            obj.SetInt(1);
-            Assert.Equal(1, obj.GetInt());
+        public void IInt()
+        {
+            var obj = CreateWrapper<IIntImpl, IInt>();
+            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<Derived, IDerived>();
+            IDerived obj = CreateWrapper<Derived, IDerived>();
             obj.SetInt(1);
             Assert.Equal(1, obj.GetInt());
             obj.SetName("A");
@@ -63,10 +71,22 @@ public void IIntArray()
             var obj = CreateWrapper<IIntArrayImpl, IIntArray>();
             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<IIntArrayImpl, IIntArray>();
+            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<IIntImpl, IInt>();
             var obj = CreateWrapper<IInterfaceImpl, IInterface>();
-            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<ICollectionMarshallingFailsImpl, ICollectionMarshallingFails>();
 
-            Assert.Throws<ArgumentException>(() =>
-                _ = obj.Get()
+            Assert.Throws<MarshallingFailureException>(() =>
+                _ = obj.GetConstSize()
             );
-            Assert.Throws<ArgumentException>(() =>
-                obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 })
+
+            Assert.Throws<MarshallingFailureException>(() =>
+                _ = obj.Get(out _)
+            );
+
+            Assert.Throws<MarshallingFailureException>(() =>
+                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<IJaggedIntArrayMarshallingFailsImpl, IJaggedIntArrayMarshallingFails>();
 
-            Assert.Throws<ArgumentException>(() =>
+            Assert.Throws<MarshallingFailureException>(() =>
+                _ = obj.GetConstSize()
+            );
+
+            Assert.Throws<MarshallingFailureException>(() =>
                 _ = 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<ArgumentException>(() =>
+            var length = 3;
+            Assert.Throws<MarshallingFailureException>(() =>
                 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<ArgumentException>(() =>
+            Assert.Throws<MarshallingFailureException>(() =>
             {
                 obj.Param(strings);
             });
-            Assert.Throws<ArgumentException>(() =>
+            Assert.Throws<MarshallingFailureException>(() =>
             {
                 obj.RefParam(ref strings);
             });
-            Assert.Throws<ArgumentException>(() =>
+            Assert.Throws<MarshallingFailureException>(() =>
             {
                 obj.InParam(in strings);
             });
-            Assert.Throws<ArgumentException>(() =>
-            {
-                obj.OutParam(out strings);
-            });
-            // https://github.com/dotnet/runtime/issues/87845
-            //Assert.Throws<ArgumentException>(() =>
-            //{
-            //    obj.ByValueOutParam(strings);
-            //});
-            Assert.Throws<ArgumentException>(() =>
+            Assert.Throws<MarshallingFailureException>(() =>
             {
                 obj.ByValueInOutParam(strings);
             });
-            Assert.Throws<ArgumentException>(() =>
-            {
-                _ = 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<IStringArrayMarshallingFailsImpl, IStringArrayMarshallingFails>();
+            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<MarshallingFailureException>(() =>
             {
-                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<MarshallingFailureException>(() =>
             {
-                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<IStringArrayMarshallingFailsImpl, IStringArrayMarshallingFails>();
+            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<Exception>(() =>
             {
-                array = _data;
-                widths = _widths;
-                return array.Length;
-            }
-            public void Set(int[][] array, int[] widths, int length)
+                obj.OutParam(out strings);
+            });
+            Assert.Throws<Exception>(() =>
             {
-                _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<IStringArrayMarshallingFailsImpl, IStringArrayMarshallingFails>();
 
-        [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<MarshallingFailureException>(() =>
             {
-                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 c8f2292a6985c1..ac900cd202d2cc 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 66faa8c577e7c2..c9a149e12798c7 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 9059f53c4cc8ae..75c7db67fd137f 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<void*, byte**, int>)&Utf8Implementation.ABI.GetStringUtf8;
-					vtable[4] = (delegate* unmanaged<void*, byte*, int>)&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<void*, ushort**, int>)&Utf16Implementation.ABI.GetStringUtf16;
-					vtable[4] = (delegate* unmanaged<void*, ushort*, int>)&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<IUTF8Marshalling>((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<IUTF8Marshalling>((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<IUTF16Marshalling>((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<IUTF16Marshalling>((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<void*, byte**, int>)&Utf8Implementation.ABI.GetStringUtf8;
+                    vtable[4] = (delegate* unmanaged<void*, byte*, int>)&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<void*, ushort**, int>)&Utf16Implementation.ABI.GetStringUtf16;
+                    vtable[4] = (delegate* unmanaged<void*, ushort*, int>)&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<IUTF8Marshalling>((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<IUTF8Marshalling>((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<IUTF16Marshalling>((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<IUTF16Marshalling>((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 df01a1045700be..8e9ca95295bd81 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 00000000000000..abb8842f1e0a2f
--- /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 00000000000000..4e71832850fefc
--- /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 d792b62d6d5a97..7a1c97a7b37302 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 fb1c6ebba8b3a2..f6cd4ff8afc55b 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 e586a04573c9f1..86af99aa4ff726 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 00000000000000..f63cbbb91ab1de
--- /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 54e7fb896bf94f..3f1b18f34e9beb 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 b70de07d1596b5..f2283708678df1 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 00000000000000..e50344f3a268fd
--- /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 00000000000000..fbc68dcf4dbca0
--- /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 00000000000000..95c6a13335b44d
--- /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 00000000000000..c1153e377fc656
--- /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 00000000000000..3a86b74a70b5c2
--- /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 00000000000000..50a25b520939fb
--- /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 00000000000000..a585a5456b4039
--- /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<byte> 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 00000000000000..e4df4476cfb7cc
--- /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<int> p,
+            int size);
+        void MethodIn(
+            [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection<int> pIn,
+            in int size);
+        void MethodRef(
+            [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection<int> pRef,
+            int size);
+        void MethodOut(
+            [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection<int> pOut,
+            out int size);
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatefulCollection<int> Return(int size);
+        [PreserveSig]
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatefulCollection<int> 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 00000000000000..d31c70856b6ebc
--- /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<StatelessType> p,
+            int size);
+        void MethodIn(
+            [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection<StatelessType> pIn,
+            in int size);
+        void MethodRef(
+            [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection<StatelessType> pRef,
+            int size);
+        void MethodOut(
+            [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection<StatelessType> pOut,
+            out int size);
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatefulCollection<StatelessType> Return(int size);
+        [PreserveSig]
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatefulCollection<StatelessType> ReturnPreserveSig(int size);
+    }
+
+    [NativeMarshalling(typeof(StatefulCollectionMarshaller<,>))]
+    internal class StatefulCollection<T>
+    {
+    }
+
+    [ContiguousCollectionMarshaller]
+    [CustomMarshaller(typeof(StatefulCollection<>), MarshalMode.Default, typeof(StatefulCollectionMarshaller<,>.Default))]
+    static unsafe class StatefulCollectionMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
+    {
+        public ref struct Default
+        {
+            public byte* ToUnmanaged() => throw null;
+            public System.ReadOnlySpan<T> GetManagedValuesSource() => throw null;
+            public System.Span<TUnmanagedElement> GetUnmanagedValuesDestination() => throw null;
+
+            public void FromUnmanaged(byte* value) => throw null;
+            public System.Span<T> GetManagedValuesDestination(int numElements) => throw null;
+            public System.ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements) => throw null;
+
+            public void Free() => throw null;
+
+            public void FromManaged(StatefulCollection<T> managed) => throw null;
+
+            public StatefulCollection<T> 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 00000000000000..70b9a4e78a0ac7
--- /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 00000000000000..f84da40e9e2e00
--- /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 00000000000000..1b5438cbdd6f25
--- /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<byte> 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 00000000000000..f5c3d4b7e2885c
--- /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<byte> 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 00000000000000..684352435e6269
--- /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<int> p,
+            int size);
+        void MethodIn(
+            [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection<int> pIn,
+            in int size);
+        void MethodRef(
+            [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection<int> pRef,
+            int size);
+        void MethodOut(
+            [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection<int> pOut,
+            out int size);
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatelessCollection<int> Return(int size);
+        [PreserveSig]
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatelessCollection<int> 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 00000000000000..ca6770fdf04401
--- /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<StatelessType> p,
+            int size);
+
+        void MethodIn(
+            [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection<StatelessType> pIn,
+            in int size);
+
+        void MethodRef(
+            [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection<StatelessType> pRef,
+            int size);
+
+        void MethodOut(
+            [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection<StatelessType> pOut,
+            out int size);
+
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatelessCollection<StatelessType> Return(int size);
+
+        [PreserveSig]
+        [return: MarshalUsing(CountElementName = nameof(size))]
+        StatelessCollection<StatelessType> ReturnPreserveSig(int size);
+    }
+
+    [NativeMarshalling(typeof(StatelessCollectionMarshaller<,>))]
+    internal class StatelessCollection<T>
+    {
+    }
+
+    [ContiguousCollectionMarshaller]
+    [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.Default, typeof(StatelessCollectionMarshaller<,>.Default))]
+    internal static unsafe class StatelessCollectionMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
+    {
+        internal static class Default
+        {
+            public static nint AllocateContainerForUnmanagedElements(StatelessCollection<T> managed, out int numElements)
+            {
+                throw new System.NotImplementedException();
+            }
+
+            public static StatelessCollection<T> AllocateContainerForManagedElements(nint unmanaged, int numElements)
+            {
+                throw new System.NotImplementedException();
+            }
+
+            public static System.ReadOnlySpan<T> GetManagedValuesSource(StatelessCollection<T> managed)
+            {
+                throw new System.NotImplementedException();
+            }
+
+            public static System.Span<TUnmanagedElement> GetUnmanagedValuesDestination(nint unmanaged, int numElements)
+            {
+                throw new System.NotImplementedException();
+            }
+
+            public static System.ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(nint unmanaged, int numElements)
+            {
+                throw new System.NotImplementedException();
+            }
+
+            public static System.Span<T> GetManagedValuesDestination(StatelessCollection<T> 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 00000000000000..6b0c57a1f37fba
--- /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 00000000000000..c2c8d45e31ad52
--- /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 00000000000000..4419fc52c077cf
--- /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 cd7c8f620dc935..df956009fce8be 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 079db461a66bd8..5022d42d13930f 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 2ef5534aa6b23d..1c123065a2ec1c 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 2689425abd5065..08dbbdd5e68924 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 00000000000000..1c6513607d9056
--- /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 00000000000000..4da8802dff4079
--- /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 6890cacc60c740..77afe03c85fddb 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 00000000000000..b832266e409a46
--- /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 00000000000000..0bfb7e052d74a3
--- /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 6/9] 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 a820e032e798ff..9d35c9ded01364 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<ICollectionMarshallingFailsImpl, ICollectionMarshallingFails>();
@@ -136,7 +135,6 @@ public void ICollectionMarshallingFails()
         }
 
         [Fact]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/89747")]
         public void IJaggedArrayMarshallingFails()
         {
             var obj = CreateWrapper<IJaggedIntArrayMarshallingFailsImpl, IJaggedIntArrayMarshallingFails>();

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 7/9] 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 bf993d5f40aee8..8d7360b507438e 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<StatementSyntax> 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;
         }
 
+        /// <summary>
+        /// Returns which stage cleanup should be performed for the parameter.
+        /// </summary>
+        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 80fa7d867c2ec4..86275796f208ee 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 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 8/9] 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 9d35c9ded01364..92c232357b849e 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<ICollectionMarshallingFailsImpl, ICollectionMarshallingFails>();
 
             Assert.Throws<MarshallingFailureException>(() =>
-                _ = obj.GetConstSize()
+                obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, 10)
             );
 
-            Assert.Throws<MarshallingFailureException>(() =>
-                _ = obj.Get(out _)
+            Assert.Throws(hrExceptionType, () =>
+                _ = obj.GetConstSize()
             );
 
-            Assert.Throws<MarshallingFailureException>(() =>
-                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<IJaggedIntArrayMarshallingFailsImpl, IJaggedIntArrayMarshallingFails>();
 
-            Assert.Throws<MarshallingFailureException>(() =>
+            Assert.Throws(hrExceptionType, () =>
                 _ = obj.GetConstSize()
             );
 
-            Assert.Throws<MarshallingFailureException>(() =>
+            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<IStringArrayMarshallingFailsImpl, IStringArrayMarshallingFails>();
 
             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<IStringArrayMarshallingFailsImpl, IStringArrayMarshallingFails>();
-            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<MarshallingFailureException>(() =>
-            {
-                obj.OutParam(out strings);
-            });
-            Assert.Throws<MarshallingFailureException>(() =>
-            {
-                _ = obj.ReturnValue();
-            });
-        }
-
-        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows))]
-        public void IStringArrayMarshallingFailsOutNonWindows()
-        {
-            var obj = CreateWrapper<IStringArrayMarshallingFailsImpl, IStringArrayMarshallingFails>();
-            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<Exception>(() =>
+            Assert.Throws(hrExceptionType, () =>
             {
                 obj.OutParam(out strings);
             });
-            Assert.Throws<Exception>(() =>
+            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 9/9] 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 b167b140407217..1e0939e6c440e1 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<ICollectionMarshallingFailsImpl, ICollectionMarshallingFails>();
 
@@ -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<IJaggedIntArrayMarshallingFailsImpl, IJaggedIntArrayMarshallingFails>();
 
             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<IStringArrayMarshallingFailsImpl, IStringArrayMarshallingFails>();
 
             var strings = IStringArrayMarshallingFailsImpl.StartingStrings;
