diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs index 5acdc3072a225b..ec05c0d3dcb077 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -220,6 +221,9 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray IsFunctionPointer: false); public IEnumerable Generate(TypePositionInfo info, StubCodeContext context) { - if (context.CurrentStage != StubCodeContext.Stage.Unmarshal) + switch (context.CurrentStage) { - yield break; + case StubCodeContext.Stage.Unmarshal: + var (managed, native) = context.GetIdentifiers(info); + // = ComWrappers.ComInterfaceDispatch.GetInstance<>(); + yield return ExpressionStatement( + AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + IdentifierName(managed), + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseName(TypeNames.System_Runtime_InteropServices_ComWrappers_ComInterfaceDispatch), + GenericName( + Identifier("GetInstance"), + TypeArgumentList(SingletonSeparatedList(info.ManagedType.Syntax)))), + ArgumentList( + SingletonSeparatedList( + Argument( + IdentifierName(native))))))); + yield break; + case StubCodeContext.Stage.AssignOut: + Debug.Assert(MarshallerHelpers.MarshalsOut(info, context)); + yield return this.GeneratePointerAssignOut(info, context); + yield break; + default: + yield break; } - - var (managed, native) = context.GetIdentifiers(info); - - // = ComWrappers.ComInterfaceDispatch.GetInstance<>(); - yield return ExpressionStatement( - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName(managed), - InvocationExpression( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - ParseName(TypeNames.System_Runtime_InteropServices_ComWrappers_ComInterfaceDispatch), - GenericName( - Identifier("GetInstance"), - TypeArgumentList(SingletonSeparatedList(info.ManagedType.Syntax)))), - ArgumentList( - SingletonSeparatedList( - Argument( - IdentifierName(native))))))); } public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => SignatureBehavior.NativeType; diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs index 6d255912aedaab..16a325c6eb39bd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -14,7 +13,6 @@ namespace Microsoft.Interop internal sealed class UnmanagedToManagedStubGenerator { private const string ReturnIdentifier = "__retVal"; - private const string InvokeSucceededIdentifier = "__invokeSucceeded"; private readonly BoundGenerators _marshallers; @@ -60,41 +58,28 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke) || !statements.ManagedExceptionCatchClauses.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables); - if (!statements.GuaranteedUnmarshal.IsEmpty) - { - setupStatements.Add(MarshallerHelpers.Declare(PredefinedType(Token(SyntaxKind.BoolKeyword)), InvokeSucceededIdentifier, initializeToDefault: true)); - } - - setupStatements.AddRange(declarations.Initializations); setupStatements.AddRange(declarations.Variables); + setupStatements.AddRange(declarations.Initializations); setupStatements.AddRange(statements.Setup); List tryStatements = new(); tryStatements.AddRange(statements.Unmarshal); + tryStatements.AddRange(statements.GuaranteedUnmarshal); tryStatements.Add(statements.InvokeStatement); - if (!statements.GuaranteedUnmarshal.IsEmpty) - { - tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName(InvokeSucceededIdentifier), - LiteralExpression(SyntaxKind.TrueLiteralExpression)))); - } - tryStatements.AddRange(statements.NotifyForSuccessfulInvoke); - tryStatements.AddRange(statements.PinnedMarshal); tryStatements.AddRange(statements.Marshal); + tryStatements.AddRange(statements.PinnedMarshal); + + tryStatements.AddRange(statements.AssignOut); + tryStatements.AddRange(statements.Cleanup); List allStatements = setupStatements; List finallyStatements = new(); - if (!statements.GuaranteedUnmarshal.IsEmpty) - { - finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal))); - } SyntaxList catchClauses = List(statements.ManagedExceptionCatchClauses); - finallyStatements.AddRange(statements.Cleanup); if (finallyStatements.Count > 0) { allStatements.Add( @@ -110,6 +95,7 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke) allStatements.AddRange(tryStatements); } + // Return if (!_marshallers.IsUnmanagedVoidReturn) allStatements.Add(ReturnStatement(IdentifierName(_context.GetIdentifiers(_marshallers.NativeReturnMarshaller.TypeInfo).native))); diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs index 34d09a70a66e6f..c843c797376eed 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -183,6 +184,9 @@ public BlockSyntax GeneratePInvokeBody(string dllImportName) allStatements.Add(MarshallerHelpers.CreateSetLastPInvokeErrorStatement(LastErrorIdentifier)); } + // ManagedToUnmanaged shouldn't need AssignOut stage + Debug.Assert(statements.AssignOut.IsEmpty); + // Return if (!_marshallers.IsManagedVoidReturn) allStatements.Add(ReturnStatement(IdentifierName(_context.GetIdentifiers(_marshallers.ManagedReturnMarshaller.TypeInfo).managed))); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BooleanMarshallingInfoProvider.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BooleanMarshallingInfoProvider.cs index 6a7b78acf1dd56..da0ea28c1aba15 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BooleanMarshallingInfoProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BooleanMarshallingInfoProvider.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Text; using Microsoft.CodeAnalysis; namespace Microsoft.Interop 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..d0fdf2883a4a81 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 @@ -22,7 +22,7 @@ public struct GeneratedStatements public ImmutableArray NotifyForSuccessfulInvoke { get; init; } public ImmutableArray GuaranteedUnmarshal { get; init; } public ImmutableArray Cleanup { get; init; } - + public ImmutableArray AssignOut { get; init; } public ImmutableArray ManagedExceptionCatchClauses { get; init; } public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeContext context) @@ -39,7 +39,8 @@ public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeCo 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 }), - ManagedExceptionCatchClauses = GenerateCatchClauseForManagedException(marshallers, context) + ManagedExceptionCatchClauses = GenerateCatchClauseForManagedException(marshallers, context), + AssignOut = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.AssignOut }), }; } public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeContext context, ExpressionSyntax expressionToInvoke) @@ -71,7 +72,26 @@ private static ImmutableArray GenerateStatementsForStubContext( ImmutableArray.Builder statementsToUpdate = ImmutableArray.CreateBuilder(); foreach (BoundGenerator marshaller in marshallers.SignatureMarshallers) { - statementsToUpdate.AddRange(marshaller.Generator.Generate(marshaller.TypeInfo, context)); + StubCodeContext localContext = context; + // Modify the context if we need to marshal out + switch (context.CurrentStage) + { + case StubCodeContext.Stage.Marshal: + case StubCodeContext.Stage.PinnedMarshal: + if (MarshallerHelpers.MarshalsOut(marshaller.TypeInfo, context)) + localContext = new MarshalOutContext(context); + break; + case StubCodeContext.Stage.AssignOut: + if (!MarshallerHelpers.MarshalsOut(marshaller.TypeInfo, context)) + continue; + localContext = new AssignOutContext(context, marshaller.Generator.AsParameter(marshaller.TypeInfo, context).Identifier.ToString()); + break; + case StubCodeContext.Stage.CleanupFailure: + if (!MarshallerHelpers.MarshalsOut(marshaller.TypeInfo, context)) + continue; + break; + } + statementsToUpdate.AddRange(marshaller.Generator.Generate(marshaller.TypeInfo, localContext)); } if (statementsToUpdate.Count > 0) @@ -150,7 +170,10 @@ private static ImmutableArray GenerateCatchClauseForManagedEx { if (!marshallers.HasManagedExceptionMarshaller) { - return ImmutableArray.Empty; + ImmutableArray.Create(CatchClause( + CatchDeclaration(ParseTypeName(TypeNames.System_Exception)), + filter: null, + Block(List(GenerateStatementsForStubContext(marshallers, new MarshalOutContext(context with { CurrentStage = StubCodeContext.Stage.CleanupFailure })))))); } ImmutableArray.Builder catchClauseBuilder = ImmutableArray.CreateBuilder(); @@ -164,6 +187,15 @@ private static ImmutableArray GenerateCatchClauseForManagedEx catchClauseBuilder.AddRange( managedExceptionMarshaller.Generator.Generate( managedExceptionMarshaller.TypeInfo, context with { CurrentStage = StubCodeContext.Stage.PinnedMarshal })); + catchClauseBuilder.AddRange(GenerateStatementsForStubContext(marshallers, new MarshalOutContext(context with { CurrentStage = StubCodeContext.Stage.CleanupFailure }))); + if (!marshallers.IsUnmanagedVoidReturn) + { + catchClauseBuilder.Add(ReturnStatement(IdentifierName(context.GetIdentifiers(marshallers.NativeReturnMarshaller.TypeInfo).native))); + } + else + { + catchClauseBuilder.Add(ReturnStatement()); + } return ImmutableArray.Create( CatchClause( CatchDeclaration(ParseTypeName(TypeNames.System_Exception), Identifier(managed)), @@ -183,8 +215,10 @@ private static SyntaxTriviaList GenerateStageTrivia(StubCodeContext.Stage stage) 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.CleanupFailure => "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.", + StubCodeContext.Stage.AssignOut => "Assign to parameters", _ => throw new ArgumentOutOfRangeException(nameof(stage)) }; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AssignOutContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AssignOutContext.cs new file mode 100644 index 00000000000000..e9b75d53fb5087 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AssignOutContext.cs @@ -0,0 +1,35 @@ +// 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.Diagnostics; + +namespace Microsoft.Interop +{ + public sealed record AssignOutContext : StubCodeContext + { + internal StubCodeContext InnerContext { get; init; } + public string ParameterIdentifier { get; init; } + + internal AssignOutContext(StubCodeContext inner, string parameterIdentifier) + { + InnerContext = inner; + Debug.Assert(inner.CurrentStage == Stage.AssignOut); + CurrentStage = Stage.AssignOut; + Direction = inner.Direction; + ParentContext = inner.ParentContext; + ParameterIdentifier = parameterIdentifier; + } + + public override (TargetFramework framework, Version version) GetTargetFramework() => InnerContext.GetTargetFramework(); + + public override bool SingleFrameSpansNativeContext => InnerContext.SingleFrameSpansNativeContext; + + public override bool AdditionalTemporaryStateLivesAcrossStages => InnerContext.AdditionalTemporaryStateLivesAcrossStages; + + public override (string managed, string native) GetIdentifiers(TypePositionInfo info) + => (InnerContext.GetIdentifiers(info).managed, InnerContext.GetAdditionalIdentifier(info, "out")); + + public override string GetAdditionalIdentifier(TypePositionInfo info, string name) => InnerContext.GetAdditionalIdentifier(info, name); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs index 73b8cd8eb901a9..e503fbdaaa2dec 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs @@ -278,20 +278,10 @@ private ResolvedGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo info FreeStrategy freeStrategy = GetFreeStrategy(info, context); - if (freeStrategy == FreeStrategy.FreeOriginal) - { - marshallingStrategy = new UnmanagedToManagedOwnershipTrackingStrategy(marshallingStrategy); - } - - if (freeStrategy != FreeStrategy.NoFree && marshallerData.Shape.HasFlag(MarshallerShape.Free)) + if ((freeStrategy != FreeStrategy.NoFree) && marshallerData.Shape.HasFlag(MarshallerShape.Free)) { marshallingStrategy = new StatelessFreeMarshalling(marshallingStrategy, marshallerData.MarshallerType.Syntax); } - - if (freeStrategy == FreeStrategy.FreeOriginal) - { - marshallingStrategy = new CleanupOwnedOriginalValueMarshalling(marshallingStrategy); - } } IMarshallingGenerator marshallingGenerator = new CustomTypeMarshallingGenerator(marshallingStrategy, ByValueMarshalKindSupportDescriptor.Default, marshallerData.Shape.HasFlag(MarshallerShape.StatelessPinnableReference)); @@ -373,17 +363,7 @@ private ResolvedGenerator CreateNativeCollectionMarshaller( IElementsMarshallingCollectionSource collectionSource = new StatefulLinearCollectionSource(); ElementsMarshalling elementsMarshalling = CreateElementsMarshalling(marshallerData, elementInfo, elementMarshaller, unmanagedElementType, collectionSource); - if (freeStrategy == FreeStrategy.FreeOriginal) - { - marshallingStrategy = new UnmanagedToManagedOwnershipTrackingStrategy(marshallingStrategy); - } - - marshallingStrategy = new StatefulLinearCollectionMarshalling(marshallingStrategy, marshallerData.Shape, numElementsExpression, elementsMarshalling, freeStrategy != FreeStrategy.NoFree); - - if (freeStrategy == FreeStrategy.FreeOriginal) - { - marshallingStrategy = new CleanupOwnedOriginalValueMarshalling(marshallingStrategy); - } + marshallingStrategy = new StatefulLinearCollectionMarshalling(marshallingStrategy, marshallerData.Shape, numElementsExpression, elementsMarshalling, nativeType.Syntax, freeStrategy is not FreeStrategy.NoFree); if (marshallerData.Shape.HasFlag(MarshallerShape.Free)) { @@ -397,14 +377,10 @@ private ResolvedGenerator CreateNativeCollectionMarshaller( var freeStrategy = GetFreeStrategy(info, context); IElementsMarshallingCollectionSource collectionSource = new StatelessLinearCollectionSource(marshallerTypeSyntax); - if (freeStrategy == FreeStrategy.FreeOriginal) - { - marshallingStrategy = new UnmanagedToManagedOwnershipTrackingStrategy(marshallingStrategy); - } ElementsMarshalling elementsMarshalling = CreateElementsMarshalling(marshallerData, elementInfo, elementMarshaller, unmanagedElementType, collectionSource); - marshallingStrategy = new StatelessLinearCollectionMarshalling(marshallingStrategy, elementsMarshalling, nativeType, marshallerData.Shape, numElementsExpression, freeStrategy != FreeStrategy.NoFree); + marshallingStrategy = new StatelessLinearCollectionMarshalling(marshallingStrategy, elementsMarshalling, nativeType, marshallerData.Shape, numElementsExpression, freeStrategy is not FreeStrategy.NoFree); if (marshallerData.Shape.HasFlag(MarshallerShape.CallerAllocatedBuffer)) { @@ -415,15 +391,10 @@ private ResolvedGenerator CreateNativeCollectionMarshaller( marshallingStrategy = new StatelessCallerAllocatedBufferMarshalling(marshallingStrategy, marshallerTypeSyntax, bufferElementTypeSyntax, isLinearCollectionMarshalling: true); } - if (freeStrategy != FreeStrategy.NoFree && marshallerData.Shape.HasFlag(MarshallerShape.Free)) + if ((freeStrategy != FreeStrategy.NoFree) && marshallerData.Shape.HasFlag(MarshallerShape.Free)) { marshallingStrategy = new StatelessFreeMarshalling(marshallingStrategy, marshallerTypeSyntax); } - - if (freeStrategy == FreeStrategy.FreeOriginal) - { - marshallingStrategy = new CleanupOwnedOriginalValueMarshalling(marshallingStrategy); - } } ByValueMarshalKindSupportDescriptor byValueMarshalKindSupport; @@ -467,7 +438,11 @@ private enum FreeStrategy /// /// Do not free the unmanaged value, we don't own it. /// - NoFree + NoFree, + /// + /// Free the unmanaged value if the unmanaged to managed stub throws before returning. + /// + FreeNativeOnFailure } private static FreeStrategy GetFreeStrategy(TypePositionInfo info, StubCodeContext context) @@ -492,6 +467,13 @@ private static FreeStrategy GetFreeStrategy(TypePositionInfo info, StubCodeConte return FreeStrategy.FreeOriginal; } + // If we marshal a value out to a native callee, we should free the marshalled value if we throw before returning + // ByValueContents Out we should not free the native identifier itself since we don't allocate a new one, only new elements + if (MarshallerHelpers.MarshalsOut(info, context) && !info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + return FreeStrategy.FreeNativeOnFailure; + } + // In an unmanaged-to-managed stub, we don't take ownership of the value when it isn't passed by 'ref'. return FreeStrategy.NoFree; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs index ef2542dd87bc8c..e5ed8343c4cc4a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs @@ -91,6 +91,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont IdentifierName(nativeIdentifier))); } break; + case StubCodeContext.Stage.AssignOut: + yield return this.GeneratePointerAssignOut(info, context); + break; default: break; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs index a86df409e84a70..01b7bb1de87785 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs @@ -92,6 +92,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(comparand))))); } break; + case StubCodeContext.Stage.AssignOut: + yield return this.GeneratePointerAssignOut(info, context); + break; default: break; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs index ecf0380219021d..d881cfa3f94343 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs @@ -120,6 +120,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont IdentifierName(nativeIdentifier)))); } + break; + case StubCodeContext.Stage.AssignOut: + this.GeneratePointerAssignOut(info, context); break; default: break; 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..548431f6fe3786 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 @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.Interop @@ -97,8 +99,13 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont return _nativeTypeMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); } break; - case StubCodeContext.Stage.Cleanup: + case StubCodeContext.Stage.Cleanup + when !(context.Direction is MarshalDirection.UnmanagedToManaged && (RefKind.Out == info.RefKind || info.ByValueContentsMarshalKind is ByValueContentsMarshalKind.Out)): + case StubCodeContext.Stage.CleanupFailure: return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); + case StubCodeContext.Stage.AssignOut: + Debug.Assert(MarshallerHelpers.MarshalsOut(info, context)); + return _nativeTypeMarshaller.GenerateAssignOutStatements(info, (AssignOutContext)context); default: break; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/DelegateMarshaller.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/DelegateMarshaller.cs index 3f8631a3d4ab2a..6696436780422a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/DelegateMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/DelegateMarshaller.cs @@ -94,6 +94,9 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont ArgumentList(SingletonSeparatedList(Argument(IdentifierName(managedIdentifier)))))); } break; + case StubCodeContext.Stage.AssignOut: + this.GeneratePointerAssignOut(info, context); + break; 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..129ef98d06847d 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -55,6 +56,62 @@ public StatementSyntax GenerateClearManagedValuesDestination(TypePositionInfo in public abstract StatementSyntax GenerateUnmarshalStatement(TypePositionInfo info, StubCodeContext context); public abstract StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context); + public abstract TypeSyntax UnmanagedElementType { get; } + public StatementSyntax GenerateElementsAssignOutStatement(TypePositionInfo info, AssignOutContext context) + { + ExpressionSyntax source = IdentifierName(context.GetIdentifiers(info).native);//CollectionSource.GetUnmanagedValuesDestination(info, context.InnerContext); + ExpressionSyntax destination;// = CollectionSource.GetUnmanagedValuesDestination(info, context); + destination = GetUnmanagedValuesDestinationFromUnmanagedValuesSource(info, context.InnerContext); + + // .CopyTo(); + return ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + source, + IdentifierName("CopyTo"))) + .AddArgumentListArguments( + Argument(destination))); + } + + /// + /// Returns syntax for an invocation that will create a destination for unmanaged values from the unmanaged values source. + /// Necessary to marshal ByValue [Out] in unmanaged to managed stubs. + /// MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in __GetUnmanagedValuesSource__.GetPinnableReference(), __numElements__)) + /// + public InvocationExpressionSyntax GetUnmanagedValuesDestinationFromUnmanagedValuesSource(TypePositionInfo info, StubCodeContext context) + { + return InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), + IdentifierName("CreateSpan"))) + .WithArgumentList( + ArgumentList( + SeparatedList( + new[] + { + Argument( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe), + IdentifierName("AsRef")), + ArgumentList(SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + CollectionSource.GetUnmanagedValuesSource(info, context), + IdentifierName("GetPinnableReference")), + ArgumentList())) + .WithRefKindKeyword( + Token(SyntaxKind.InKeyword)))))) + .WithRefKindKeyword( + Token(SyntaxKind.RefKeyword)), + Argument( + IdentifierName(MarshallerHelpers.GetNumElementsIdentifier(info, context))) + }))); + } } #pragma warning disable SA1400 // Access modifier should be declared https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3659 @@ -98,6 +155,8 @@ internal sealed class BlittableElementsMarshalling : ElementsMarshalling private readonly TypeSyntax _managedElementType; private readonly TypeSyntax _unmanagedElementType; + public override TypeSyntax UnmanagedElementType => _unmanagedElementType; + public BlittableElementsMarshalling(TypeSyntax managedElementType, TypeSyntax unmanagedElementType, IElementsMarshallingCollectionSource collectionSource) : base(collectionSource) { @@ -107,40 +166,49 @@ public BlittableElementsMarshalling(TypeSyntax managedElementType, TypeSyntax un public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalStatement(TypePositionInfo info, StubCodeContext context) { - // MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(), .Length) - ExpressionSyntax destination = CastToManagedIfNecessary( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), - IdentifierName("CreateSpan")), - ArgumentList( - SeparatedList(new[] - { - Argument( - InvocationExpression( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), - IdentifierName("GetReference")), - ArgumentList(SingletonSeparatedList( - Argument(CollectionSource.GetUnmanagedValuesSource(info, context)))))) - .WithRefKindKeyword( - Token(SyntaxKind.RefKeyword)), - Argument( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - CollectionSource.GetUnmanagedValuesSource(info, context), - IdentifierName("Length"))) - })))); - - // .CopyTo(); - return ExpressionStatement( + ExpressionSyntax destination; + List statements = new(); + var numElementsAssignment = CollectionSource.GetNumElementsAssignmentFromManagedValuesSource(info, context); + statements.Add(numElementsAssignment); + if (MarshallerHelpers.MarshalsOut(info, context)) + { + // #pragma warning disable CS9081 + // = stackalloc [numElementsExpression] + // #pragma warning restore CS9081 + statements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + IdentifierName(context.GetIdentifiers(info).native), + StackAllocArrayCreationExpression( + ArrayType(_unmanagedElementType, + SingletonList(ArrayRankSpecifier(SingletonSeparatedList( + IdentifierName(MarshallerHelpers.GetNumElementsIdentifier(info, context))))))))) + .WithLeadingTrivia(Trivia( + PragmaWarningDirectiveTrivia( + Token(SyntaxKind.DisableKeyword), + SingletonSeparatedList(IdentifierName("CS9081")), + isActive: true))) + .WithTrailingTrivia(Trivia( + PragmaWarningDirectiveTrivia( + Token(SyntaxKind.RestoreKeyword), + SingletonSeparatedList(IdentifierName("CS9081")), + isActive: true)))); + destination = IdentifierName(context.GetIdentifiers(info).native); + } + else + { + // MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(), .Length) + destination = CastToManagedIfNecessary(GetUnmanagedValuesDestinationFromUnmanagedValuesSource(info, context)); + } + statements.Add(ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, CollectionSource.GetManagedValuesDestination(info, context), IdentifierName("CopyTo"))) .AddArgumentListArguments( - Argument(destination))); + Argument(destination)))); + + // .CopyTo(); + return Block(statements); } public override StatementSyntax GenerateMarshalStatement(TypePositionInfo info, StubCodeContext context) @@ -260,6 +328,8 @@ public NonBlittableElementsMarshalling( _elementInfo = elementInfo; } + public override TypeSyntax UnmanagedElementType => _unmanagedElementType; + public override StatementSyntax GenerateMarshalStatement(TypePositionInfo info, StubCodeContext context) { string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); @@ -268,6 +338,7 @@ public override StatementSyntax GenerateMarshalStatement(TypePositionInfo info, // ReadOnlySpan = // Span = // .Clear() + // = Unsafe.AsPointer(ref .GetPinnableReference()) // << marshal contents >> var statements = new List() { @@ -440,7 +511,7 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo indexConstraintName, _elementInfo, _elementMarshaller, - StubCodeContext.Stage.Cleanup); + context.CurrentStage); if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) { @@ -453,6 +524,13 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo } return EmptyStatement(); } + ExpressionSyntax spanInit = context.Direction == MarshalDirection.ManagedToUnmanaged + ? CollectionSource.GetUnmanagedValuesDestination(info, context) + : CollectionSource.GetUnmanagedValuesSource(info, context); + if (context.CurrentStage is StubCodeContext.Stage.CleanupFailure && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + spanInit = IdentifierName(context.GetIdentifiers(info).native); + } return Block( LocalDeclarationStatement(VariableDeclaration( @@ -462,10 +540,7 @@ public override StatementSyntax GenerateElementCleanupStatement(TypePositionInfo SingletonSeparatedList( VariableDeclarator( Identifier(nativeSpanIdentifier)) - .WithInitializer(EqualsValueClause( - context.Direction == MarshalDirection.ManagedToUnmanaged - ? CollectionSource.GetUnmanagedValuesDestination(info, context) - : CollectionSource.GetUnmanagedValuesSource(info, context)))))), + .WithInitializer(EqualsValueClause(spanInit))))), contentsCleanupStatements); } @@ -481,6 +556,51 @@ public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalState var setNumElements = CollectionSource.GetNumElementsAssignmentFromManagedValuesDestination(info, context); + if (MarshallerHelpers.MarshalsOut(info, context)) + { + // #pragma warning disable CS9081 + // = stackalloc [numElementsExpression] + // #pragma warning restore CS9081 + var outInit = ExpressionStatement(AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(context.GetIdentifiers(info).native), + StackAllocArrayCreationExpression( + ArrayType(_unmanagedElementType, + SingletonList(ArrayRankSpecifier(SingletonSeparatedList( + IdentifierName(MarshallerHelpers.GetNumElementsIdentifier(info, context))))))))); + var nativeSpanDeclaration = LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList( + SingletonSeparatedList(_unmanagedElementType)) + ), + SingletonSeparatedList(VariableDeclarator(nativeSpanIdentifier) + .WithInitializer(EqualsValueClause( + IdentifierName(context.GetIdentifiers(info).native)))))); + + LocalDeclarationStatementSyntax managedSpanDeclaration = LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList(SingletonSeparatedList(_elementInfo.ManagedType.Syntax))), + SingletonSeparatedList( + VariableDeclarator( + Identifier(managedSpanIdentifier)) + .WithInitializer(EqualsValueClause( + CollectionSource.GetManagedValuesDestination(info, context)))))); + return Block( + setNumElements, + outInit, + nativeSpanDeclaration, + managedSpanDeclaration, + GenerateContentsMarshallingStatement( + info, + context, + IdentifierName(numElementsIdentifier), + _elementInfo, + new FreeAlwaysOwnedOriginalValueGenerator(_elementMarshaller), + StubCodeContext.Stage.Marshal, + StubCodeContext.Stage.PinnedMarshal)); + } // Span = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in .GetPinnableReference(), )); LocalDeclarationStatementSyntax unmanagedValuesSource = LocalDeclarationStatement(VariableDeclaration( GenericName( @@ -488,37 +608,7 @@ public override StatementSyntax GenerateUnmanagedToManagedByValueOutMarshalState TypeArgumentList( SingletonSeparatedList(_unmanagedElementType)) ), - SingletonSeparatedList(VariableDeclarator(nativeSpanIdentifier).WithInitializer(EqualsValueClause( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), - IdentifierName("CreateSpan"))) - .WithArgumentList( - ArgumentList( - SeparatedList( - new[] - { - Argument( - InvocationExpression( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe), - IdentifierName("AsRef")), - ArgumentList(SingletonSeparatedList( - Argument( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - CollectionSource.GetUnmanagedValuesSource(info, context), - IdentifierName("GetPinnableReference")), - ArgumentList())) - .WithRefKindKeyword( - Token(SyntaxKind.InKeyword)))))) - .WithRefKindKeyword( - Token(SyntaxKind.RefKeyword)), - Argument( - IdentifierName(numElementsIdentifier)) - })))))))); + SingletonSeparatedList(VariableDeclarator(nativeSpanIdentifier).WithInitializer(EqualsValueClause(GetUnmanagedValuesDestinationFromUnmanagedValuesSource(info, context)))))); // Span = LocalDeclarationStatementSyntax managedValuesDestination = LocalDeclarationStatement(VariableDeclaration( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs index f9da5d32b39778..dd210092be180e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomTypeMarshallingStrategy.cs @@ -13,6 +13,8 @@ internal interface ICustomTypeMarshallingStrategy { ManagedTypeInfo AsNativeType(TypePositionInfo info); + IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context); + IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context); IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalOutContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalOutContext.cs new file mode 100644 index 00000000000000..ab23ff2d3bd224 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalOutContext.cs @@ -0,0 +1,35 @@ +// 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 Microsoft.Interop +{ + /// + /// A that uses the "out" postfixed local variable as the native identifier. + /// Meant to be used during the Marshal and PinnedMarshal stages for pairs of (, ) that return true from + /// + internal sealed record MarshalOutContext : StubCodeContext + { + internal StubCodeContext InnerContext { get; init; } + + internal MarshalOutContext(StubCodeContext inner) + { + InnerContext = inner; + CurrentStage = inner.CurrentStage; + Direction = inner.Direction; + ParentContext = inner.ParentContext; + } + + public override (TargetFramework framework, Version version) GetTargetFramework() => InnerContext.GetTargetFramework(); + + public override bool SingleFrameSpansNativeContext => InnerContext.SingleFrameSpansNativeContext; + + public override bool AdditionalTemporaryStateLivesAcrossStages => InnerContext.AdditionalTemporaryStateLivesAcrossStages; + + public override (string managed, string native) GetIdentifiers(TypePositionInfo info) + => (InnerContext.GetIdentifiers(info).managed, InnerContext.GetAdditionalIdentifier(info, "out")); + + public override string GetAdditionalIdentifier(TypePositionInfo info, string name) => InnerContext.GetAdditionalIdentifier(info, name); + } +} 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..0eab01d092f26a 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 @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -394,5 +395,49 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo } throw new UnreachableException("An element is either a return value or passed by value or by ref."); } + + /// + /// Returns whether the parameter must be marshalled back to a native callee. + /// In COM, this means the parameter should be marshalled to a local variable in the Marshal and PinnedMarshal stages + /// and copied to the parameter value in the AssignOut stage. + /// + public static bool MarshalsOut(TypePositionInfo info, StubCodeContext context) + => context.Direction is MarshalDirection.UnmanagedToManaged // UnmanagedToManaged is the only direction with a native callee + && context.AdditionalTemporaryStateLivesAcrossStages // if AdditionTempStateLivesAcrossStages is false, it is an element of an array + && ((info.IsByRef && info.RefKind is RefKind.Out or RefKind.Ref) // out and ref need to be marshalled back to the callee + || info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out) // [Out] needs it's contents marshalled back to native + || (info.IsManagedReturnPosition && !info.IsNativeReturnPosition)); // Managed returns on non-PreserveSig methods are 'out' + + /// + /// Generates the following statements + /// _ = ; + /// + public static StatementSyntax CreateDiscardStatement(string identifier) + => ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("_"), IdentifierName(identifier))); + + /// + /// Generates the statement + /// * = ; + /// + public static ExpressionStatementSyntax GenerateAssignmentToPointerValue(string pointerIdentifier, string valueIdentifier) + { + return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + PrefixUnaryExpression(SyntaxKind.PointerIndirectionExpression, IdentifierName(pointerIdentifier)), + IdentifierName(valueIdentifier))); + } + + /// + /// Returns the default AssignOut statement for pointer parameters. + /// Throws if is false. + /// + /// + public static ImmutableArray GenerateDefaultAssignOutStatement(TypePositionInfo info, AssignOutContext context) + { + if (!MarshalsOut(info, context)) + throw new ArgumentException("Cannot make an AssignOut statement for a parameter that is not marshalled out"); + return ImmutableArray.Create(GenerateAssignmentToPointerValue(context.ParameterIdentifier, context.GetIdentifiers(info).native)); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs index 2c11351959b038..a673d14e73f4cd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs @@ -155,7 +155,7 @@ private static bool TryRehydrateMarshalAsAttribute(TypePositionInfo info, out At CustomTypeMarshallerData defaultMarshallerData = collectionMarshalling.Marshallers.GetModeOrDefault(MarshalMode.Default); if ((defaultMarshallerData.MarshallerType.FullTypeName.StartsWith($"{TypeNames.System_Runtime_InteropServices_ArrayMarshaller}<") || defaultMarshallerData.MarshallerType.FullTypeName.StartsWith($"{TypeNames.System_Runtime_InteropServices_PointerArrayMarshaller}<")) - && defaultMarshallerData.CollectionElementMarshallingInfo is NoMarshallingInfo or MarshalAsInfo { UnmanagedType: not UnmanagedType.CustomMarshaler }) + && defaultMarshallerData.CollectionElementMarshallingInfo is NoMarshallingInfo or MarshalAsInfo { UnmanagedType: not UnmanagedType.CustomMarshaler }) { countInfo = collectionMarshalling.ElementCountInfo; elementMarshallingInfo = defaultMarshallerData.CollectionElementMarshallingInfo; @@ -256,10 +256,24 @@ public static ArgumentSyntax AsManagedArgument(this IMarshallingGenerator genera return Argument(IdentifierName(managedIdentifier)); } - public static ExpressionSyntax GenerateNativeByRefInitialization(this IMarshallingGenerator generator, TypePositionInfo info, StubCodeContext context) + /// + /// Generates *__p_native_param + /// + public static ExpressionSyntax GenerateParameterValueDereference(this IMarshallingGenerator generator, TypePositionInfo info, StubCodeContext context) + { + string paramIdentifier = generator.AsParameter(info, context).Identifier.Text; + return PrefixUnaryExpression(SyntaxKind.PointerIndirectionExpression, IdentifierName(paramIdentifier)); + } + + /// + /// If the parameter should marshal to a local and assign out, generates a statement to copy the pointed to value to the parameter. + /// Throws if the arguments are not meant to be marshalled out to a native caller. + /// + public static ExpressionStatementSyntax GeneratePointerAssignOut(this IMarshallingGenerator generator, TypePositionInfo info, StubCodeContext context) { - string paramIdentifier = context.GetAdditionalIdentifier(info, ParameterIdentifierSuffix); - return RefExpression(PrefixUnaryExpression(SyntaxKind.PointerIndirectionExpression, IdentifierName(paramIdentifier))); + if (!MarshallerHelpers.MarshalsOut(info, context)) + throw new ArgumentException(""); + return MarshallerHelpers.GenerateAssignmentToPointerValue(generator.AsParameter(info, context).Identifier.ToString(), context.GetIdentifiers(info).native); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs index 03a906f8881597..c1227216c59f33 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatefulMarshallingStrategy.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -190,6 +191,12 @@ public static string GetMarshallerIdentifier(TypePositionInfo info, StubCodeCont { return context.GetAdditionalIdentifier(info, MarshallerIdentifier); } + + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) + { + Debug.Assert(MarshallerHelpers.MarshalsOut(info, context)); + return MarshallerHelpers.GenerateDefaultAssignOutStatement(info, context); + } } /// @@ -284,6 +291,8 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); public IEnumerable GenerateNotifyForSuccessfulInvokeStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateNotifyForSuccessfulInvokeStatements(info, context); + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) + => _innerMarshaller.GenerateAssignOutStatements(info, context); } internal sealed class StatefulLinearCollectionSource : IElementsMarshallingCollectionSource @@ -355,12 +364,14 @@ internal sealed class StatefulLinearCollectionMarshalling : ICustomTypeMarshalli private readonly ExpressionSyntax _numElementsExpression; private readonly ElementsMarshalling _elementsMarshalling; private readonly bool _cleanupElements; + private readonly TypeSyntax _unmanagedCollectionType; public StatefulLinearCollectionMarshalling( ICustomTypeMarshallingStrategy innerMarshaller, MarshallerShape shape, ExpressionSyntax numElementsExpression, ElementsMarshalling elementsMarshalling, + TypeSyntax unmanagedCollectionType, bool cleanupElements) { _innerMarshaller = innerMarshaller; @@ -368,12 +379,13 @@ public StatefulLinearCollectionMarshalling( _numElementsExpression = numElementsExpression; _elementsMarshalling = elementsMarshalling; _cleanupElements = cleanupElements; + _unmanagedCollectionType = unmanagedCollectionType; } public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - if (!_cleanupElements) + if (!(_cleanupElements || MarshallerHelpers.MarshalsOut(info, context))) { yield break; } @@ -444,6 +456,16 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf { InstanceIdentifier = numElementsIdentifier }, context); + + if (MarshallerHelpers.MarshalsOut(info, context) + && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + yield return MarshallerHelpers.Declare( + ScopedType(GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList(SingletonSeparatedList(_elementsMarshalling.UnmanagedElementType)))), + context.GetAdditionalIdentifier(info, "out"), true); + } } public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) @@ -488,6 +510,19 @@ public IEnumerable GenerateUnmarshalStatements(TypePositionInfo public IEnumerable GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateUnmarshalCaptureStatements(info, context); public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true; + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) + { + Debug.Assert(MarshallerHelpers.MarshalsOut(info, context)); + if (info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + yield return _elementsMarshalling.GenerateElementsAssignOutStatement(info, context); + } + else + { + foreach (var s in _innerMarshaller.GenerateAssignOutStatements(info, context)) + yield return s; + } + } } /// @@ -534,5 +569,6 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i public IEnumerable GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateUnmarshalCaptureStatements(info, context); public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.UsesNativeIdentifier(info, context); + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) => _innerMarshaller.GenerateAssignOutStatements(info, context); } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs index 24cfcca03df60b..aa97d7193731cb 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StatelessMarshallingStrategy.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -138,6 +139,12 @@ public IEnumerable GenerateNotifyForSuccessfulInvokeStatements( { return Array.Empty(); } + + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) + { + Debug.Assert(MarshallerHelpers.MarshalsOut(info, context)); + return MarshallerHelpers.GenerateDefaultAssignOutStatement(info, context); + } } /// @@ -251,6 +258,7 @@ IEnumerable GenerateCallerAllocatedBufferMarshalStatements() public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.UsesNativeIdentifier(info, context); public IEnumerable GenerateNotifyForSuccessfulInvokeStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateNotifyForSuccessfulInvokeStatements(info, context); + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) => _innerMarshaller.GenerateAssignOutStatements(info, context); } internal sealed class StatelessFreeMarshalling : ICustomTypeMarshallingStrategy @@ -265,6 +273,7 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller, } public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) => _innerMarshaller.GenerateAssignOutStatements(info, context); public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { @@ -316,6 +325,12 @@ public ManagedTypeInfo AsNativeType(TypePositionInfo info) return _unmanagedType; } + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) + { + Debug.Assert(MarshallerHelpers.MarshalsOut(info, context)); + return MarshallerHelpers.GenerateDefaultAssignOutStatement(info, context); + } + public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { if (MarshallerHelpers.GetMarshalDirection(info, context) == MarshalDirection.ManagedToUnmanaged) @@ -553,10 +568,23 @@ public StatelessLinearCollectionMarshalling( } public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _unmanagedType; + public IEnumerable GenerateAssignOutStatements(TypePositionInfo info, AssignOutContext context) + { + Debug.Assert(MarshallerHelpers.MarshalsOut(info, context)); + if (info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + yield return _elementsMarshalling.GenerateElementsAssignOutStatement(info, context); + } + else + { + foreach (var s in _spaceMarshallingStrategy.GenerateAssignOutStatements(info, context)) + yield return s; + } + } public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - if (!_cleanupElementsAndSpace) + if (!(_cleanupElementsAndSpace || MarshallerHelpers.MarshalsOut(info, context))) { yield break; } @@ -577,9 +605,12 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i yield return elementCleanup; } - foreach (var statement in _spaceMarshallingStrategy.GenerateCleanupStatements(info, context)) + if (!_cleanupElementsAndSpace) { - yield return statement; + foreach (var statement in _spaceMarshallingStrategy.GenerateCleanupStatements(info, context)) + { + yield return statement; + } } } @@ -627,6 +658,15 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf { yield return elementsSetup; } + if (MarshallerHelpers.MarshalsOut(info, context) + && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + yield return MarshallerHelpers.Declare( + ScopedType(GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList(SingletonSeparatedList(_elementsMarshalling.UnmanagedElementType)))), + context.GetAdditionalIdentifier(info, "out"), true); + } } public IEnumerable GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty(); @@ -646,6 +686,10 @@ public IEnumerable GenerateUnmarshalStatements(TypePositionInfo // If the parameter is marshalled by-value [Out], then we don't marshal the contents of the collection. // We do clear the span, so that if the invoke target doesn't fill it, we aren't left with undefined content. yield return _elementsMarshalling.GenerateClearManagedValuesDestination(info, context); + foreach (var statement in _spaceMarshallingStrategy.GenerateUnmarshalStatements(info, context)) + { + yield return statement; + } yield break; } 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..44d6dfe5b820bc 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/UnmanagedToManagedOwnershipTrackingStrategy.cs @@ -3,120 +3,11 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Microsoft.Interop { - /// - /// Marshalling strategy that introduces a variable to hold the initial value of the provided and a variable to track if the original value has been replaced. - /// - /// - internal sealed class UnmanagedToManagedOwnershipTrackingStrategy : ICustomTypeMarshallingStrategy - { - private readonly ICustomTypeMarshallingStrategy _innerMarshaller; - - public UnmanagedToManagedOwnershipTrackingStrategy(ICustomTypeMarshallingStrategy innerMarshaller) - { - _innerMarshaller = innerMarshaller; - } - - public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context); - - public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); - public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context) - { - foreach (StatementSyntax statement in _innerMarshaller.GenerateMarshalStatements(info, context)) - { - yield return statement; - } - - // Now that we've set the new value to pass to the caller on the identifier, we need to make sure that we free the old one. - // The caller will not see the old one any more, so it won't be able to free it. - - // = true; - yield return ExpressionStatement( - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName(context.GetAdditionalIdentifier(info, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)), - LiteralExpression(SyntaxKind.TrueLiteralExpression))); - } - - public IEnumerable GenerateNotifyForSuccessfulInvokeStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateNotifyForSuccessfulInvokeStatements(info, context); - public IEnumerable GeneratePinnedMarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GeneratePinnedMarshalStatements(info, context); - - public IEnumerable GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GeneratePinStatements(info, context); - public IEnumerable GenerateSetupStatements(TypePositionInfo info, StubCodeContext context) - { - foreach (StatementSyntax statement in _innerMarshaller.GenerateSetupStatements(info, context)) - { - yield return statement; - } - - // bool = false; - yield return LocalDeclarationStatement( - VariableDeclaration( - PredefinedType(Token(SyntaxKind.BoolKeyword)), - SingletonSeparatedList( - VariableDeclarator( - Identifier(context.GetAdditionalIdentifier(info, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)), - null, - EqualsValueClause( - LiteralExpression(SyntaxKind.FalseLiteralExpression)))))); - - yield return OwnershipTrackingHelpers.DeclareOriginalValueIdentifier(info, context, AsNativeType(info)); - } - - public IEnumerable GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateUnmarshalCaptureStatements(info, context); - - public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateUnmarshalStatements(info, context); - public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.UsesNativeIdentifier(info, context); - } - - /// - /// Marshalling strategy that uses the tracking variables introduced by to cleanup the original value if the original value is owned - /// in the stage. - /// - internal sealed class CleanupOwnedOriginalValueMarshalling : ICustomTypeMarshallingStrategy - { - private readonly ICustomTypeMarshallingStrategy _innerMarshaller; - - public CleanupOwnedOriginalValueMarshalling(ICustomTypeMarshallingStrategy innerMarshaller) - { - _innerMarshaller = innerMarshaller; - } - - public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info); - - public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) - { - // if () - // { - // - // } - yield return IfStatement( - IdentifierName(context.GetAdditionalIdentifier(info, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)), - Block(_innerMarshaller.GenerateCleanupStatements(info, new OwnedValueCodeContext(context)))); - } - - public IEnumerable GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context); - public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateMarshalStatements(info, context); - - public IEnumerable GenerateNotifyForSuccessfulInvokeStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateNotifyForSuccessfulInvokeStatements(info, context); - public IEnumerable GeneratePinnedMarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GeneratePinnedMarshalStatements(info, context); - - public IEnumerable GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GeneratePinStatements(info, context); - public IEnumerable GenerateSetupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateSetupStatements(info, context); - - public IEnumerable GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateUnmarshalCaptureStatements(info, context); - - public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateUnmarshalStatements(info, context); - public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.UsesNativeIdentifier(info, context); - } - /// /// Marshalling strategy to cache the initial value of a given in a local variable and cleanup that value in the cleanup stage. /// Useful in scenarios where the value is always owned in all code-paths that reach the stage, so additional ownership tracking is extraneous. 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..f3315f8ced94e7 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 @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; namespace Microsoft.Interop { @@ -72,7 +71,18 @@ public enum Stage /// Convert native data to managed data even in the case of an exception during /// the non-cleanup phases. /// - GuaranteedUnmarshal + GuaranteedUnmarshal, + + /// + /// In scenarios where necessary (unmanaged to managed stubs), assign marshalled values out to the parameters. + /// Separating this from ensures parameters remain unmodified when the method returns a failing return value. + /// + AssignOut, + + /// + /// In scenarios where the managed callee throws before returning, this stage cleans up any resources that the method allocated but cannot transfer to the caller. + /// + CleanupFailure } /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs index c791ec1f268a0a..0a90a3144e9435 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Microsoft.Interop { @@ -53,22 +52,23 @@ public static VariableDeclarations GenerateDeclarationsForManagedToUnmanaged(Bou static void AppendVariableDeclarations(ImmutableArray.Builder statementsToUpdate, BoundGenerator marshaller, StubCodeContext context, bool initializeToDefault) { - (string managed, string native) = context.GetIdentifiers(marshaller.TypeInfo); + TypePositionInfo info = marshaller.TypeInfo; + (string managed, string native) = context.GetIdentifiers(info); // Declare variable for return value - if (marshaller.TypeInfo.IsManagedReturnPosition || marshaller.TypeInfo.IsNativeReturnPosition) + if (info.IsManagedReturnPosition || info.IsNativeReturnPosition) { statementsToUpdate.Add(MarshallerHelpers.Declare( - marshaller.TypeInfo.ManagedType.Syntax, + info.ManagedType.Syntax, managed, initializeToDefault)); } // Declare variable with native type for parameter or return value - if (marshaller.Generator.UsesNativeIdentifier(marshaller.TypeInfo, context)) + if (marshaller.Generator.UsesNativeIdentifier(info, context)) { statementsToUpdate.Add(MarshallerHelpers.Declare( - marshaller.Generator.AsNativeType(marshaller.TypeInfo).Syntax, + marshaller.Generator.AsNativeType(info).Syntax, native, initializeToDefault)); } @@ -83,7 +83,10 @@ public static VariableDeclarations GenerateDeclarationsForUnmanagedToManaged(Bou foreach (BoundGenerator marshaller in marshallers.NativeParameterMarshallers) { TypePositionInfo info = marshaller.TypeInfo; - if (info.IsNativeReturnPosition || info.IsManagedReturnPosition) + if (info.IsNativeReturnPosition) + continue; + + if (info.IsManagedReturnPosition) continue; // Declare variables for parameters @@ -110,63 +113,66 @@ public static VariableDeclarations GenerateDeclarationsForUnmanagedToManaged(Bou static void AppendVariableDeclarations(ImmutableArray.Builder statementsToUpdate, BoundGenerator marshaller, StubCodeContext context, bool initializeToDefault) { - (string managed, string native) = context.GetIdentifiers(marshaller.TypeInfo); + var info = marshaller.TypeInfo; + (string managed, string native) = context.GetIdentifiers(info); // Declare variable for return value - if (marshaller.TypeInfo.IsNativeReturnPosition) + if (info.IsNativeReturnPosition) { - bool nativeReturnUsesNativeIdentifier = marshaller.Generator.UsesNativeIdentifier(marshaller.TypeInfo, context); + bool nativeReturnUsesNativeIdentifier = marshaller.Generator.UsesNativeIdentifier(info, context); // Always initialize the return value. statementsToUpdate.Add(MarshallerHelpers.Declare( - marshaller.TypeInfo.ManagedType.Syntax, + info.ManagedType.Syntax, managed, initializeToDefault || !nativeReturnUsesNativeIdentifier)); if (nativeReturnUsesNativeIdentifier) { statementsToUpdate.Add(MarshallerHelpers.Declare( - marshaller.Generator.AsNativeType(marshaller.TypeInfo).Syntax, + marshaller.Generator.AsNativeType(info).Syntax, native, initializeToDefault: true)); } } else { - ValueBoundaryBehavior boundaryBehavior = marshaller.Generator.GetValueBoundaryBehavior(marshaller.TypeInfo, context); + ValueBoundaryBehavior boundaryBehavior = marshaller.Generator.GetValueBoundaryBehavior(info, context); // Declare variable with native type for parameter // if the marshaller uses the native identifier and the signature uses a different identifier // than the native identifier. - if (marshaller.Generator.UsesNativeIdentifier(marshaller.TypeInfo, context) + if (marshaller.Generator.UsesNativeIdentifier(info, context) && boundaryBehavior is not (ValueBoundaryBehavior.NativeIdentifier or ValueBoundaryBehavior.CastNativeIdentifier)) { - TypeSyntax localType = marshaller.Generator.AsNativeType(marshaller.TypeInfo).Syntax; - if (boundaryBehavior != ValueBoundaryBehavior.AddressOfNativeIdentifier) + TypeSyntax localType = marshaller.Generator.AsNativeType(info).Syntax; + if (MarshallerHelpers.MarshalsOut(info, context) + && !info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) { + string outlocal = context.GetAdditionalIdentifier(info, "out"); + // __param_native_out; statementsToUpdate.Add(MarshallerHelpers.Declare( localType, - native, - false)); + outlocal, + true)); } - else + + if (boundaryBehavior is ValueBoundaryBehavior.AddressOfNativeIdentifier + && MarshallerHelpers.GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional) { - // To simplify propogating back the value to the "byref" parameter, - // we'll just declare the native identifier as a ref to its type. - // The rest of the code we generate will work as expected, and we don't need - // to manually propogate back the updated values after the call. + // If we need to unmarshal, initialize the native in value statementsToUpdate.Add(MarshallerHelpers.Declare( - RefType(localType), + localType, native, - marshaller.Generator.GenerateNativeByRefInitialization(marshaller.TypeInfo, context))); + marshaller.Generator.GenerateParameterValueDereference(info, context))); } } if (boundaryBehavior != ValueBoundaryBehavior.ManagedIdentifier) { statementsToUpdate.Add(MarshallerHelpers.Declare( - marshaller.TypeInfo.ManagedType.Syntax, + info.ManagedType.Syntax, managed, initializeToDefault)); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ComInterfaceGenerator.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ComInterfaceGenerator.Tests.csproj index b0262f1f0ca548..4f37f6ff80b02d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ComInterfaceGenerator.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ComInterfaceGenerator.Tests.csproj @@ -7,6 +7,7 @@ true true + \src\GeneratedCode\ $(OutputPath)\Generated $(InteropCompilerGeneratedFilesOutputPath)\ComInterfaceGenerator.Tests true 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..76d8004c789cd4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/RcwAroundCcwTests.cs @@ -2,12 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using SharedTypes.ComInterfaces; using SharedTypes.ComInterfaces.MarshallingFails; using Xunit; -using static ComInterfaceGenerator.Tests.ComInterfaces; namespace ComInterfaceGenerator.Tests { @@ -23,17 +23,26 @@ public partial class RcwAroundCcwTests } [Fact] - public void IGetAndSetInt() - { - var obj = CreateWrapper(); - obj.SetInt(1); - Assert.Equal(1, obj.GetInt()); + public void IInt() + { + var obj = CreateWrapper(); + obj.Set(1); + Assert.Equal(1, obj.Get()); + var local = 4; + obj.SwapRef(ref local); + Assert.Equal(1, local); + Assert.Equal(4, obj.Get()); + local = 2; + obj.SetIn(in local); + local = 0; + obj.GetOut(out local); + Assert.Equal(2, local); } [Fact] public void IDerived() { - var obj = CreateWrapper(); + IDerived obj = CreateWrapper(); obj.SetInt(1); Assert.Equal(1, obj.GetInt()); obj.SetName("A"); @@ -63,10 +72,13 @@ public void IIntArray() var obj = CreateWrapper(); int[] data = new int[] { 1, 2, 3 }; int length = data.Length; - obj.Set(data, length); - Assert.Equal(data, obj.Get(out int _)); - obj.Get2(out var value); + obj.SetContents(data, length); + Assert.Equal(data, obj.GetReturn(out int _)); + obj.GetOut(out var value); Assert.Equal(data, value); + obj.SwapArray(ref data, data.Length); + obj.PassIn(in data, data.Length); + obj.Double(data, data.Length); } [Fact] @@ -89,8 +101,11 @@ public void IInterface() { var iint = CreateWrapper(); var obj = CreateWrapper(); - obj.Set(iint); + obj.SetInt(iint); _ = obj.Get(); + obj.SwapRef(ref iint); + obj.InInt(in iint); + obj.GetOut(out var _); } [Fact] @@ -99,14 +114,13 @@ public void ICollectionMarshallingFails() var obj = CreateWrapper(); Assert.Throws(() => - _ = obj.Get() + _ = obj.Get(out _) ); Assert.Throws(() => - obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }) + obj.Set(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, 10) ); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/88111")] [Fact] public void IJaggedArrayMarshallingFails() { @@ -116,8 +130,8 @@ public void IJaggedArrayMarshallingFails() _ = 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 }; + var length = 3; Assert.Throws(() => obj.Set(array, widths, length) ); @@ -147,11 +161,10 @@ public void IStringArrayMarshallingFails() { obj.OutParam(out strings); }); - // https://github.com/dotnet/runtime/issues/87845 - //Assert.Throws(() => - //{ - // obj.ByValueOutParam(strings); - //}); + Assert.Throws(() => + { + obj.ByValueOutParam(strings); + }); Assert.Throws(() => { obj.ByValueInOutParam(strings); @@ -161,260 +174,60 @@ public void IStringArrayMarshallingFails() _ = obj.ReturnValue(); }); } - } - - public static partial class ComInterfaces - { - [GeneratedComInterface] - [Guid("EE6D1F2A-3418-4317-A87C-35488F6546AB")] - internal partial interface IInt - { - 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 + [Fact] + public void IArrayOfStatelessElements() { - bool _data; - public bool Get() => _data; - public void Set(bool value) => _data = value; - } + var obj = CreateWrapper(); + var originalData = GetStartingValue(); + var data = GetStartingValue(); + var refData = GetStartingValue().Select(a => new StatelessType() { I = a.I * 2 }).ToArray(); + var outData = GetStartingValue().Reverse().ToArray(); - [GeneratedComInterface] - [Guid("9FA4A8A9-2D8F-48A8-B6FB-B44B5F1B9FB6")] - internal partial interface IFloat - { - float Get(); - void Set(float value); - } + obj.Method(data, data.Length); + AssertEqual(originalData, data); - [GeneratedComClass] - internal partial class IFloatImpl : IFloat - { - float _data; - public float Get() => _data; - public void Set(float value) => _data = value; - } + obj.MethodIn(in data, data.Length); + AssertEqual(originalData, data); - [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); - } + obj.MethodContentsIn(data, data.Length); + AssertEqual(originalData, data); - [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); + obj.MethodOut(out data, data.Length); + AssertEqual(outData, data); - int Get2( - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - out int[][] array, - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] - out int[] widths); + data = GetStartingValue(); + obj.MethodContentsOut(data, data.Length); + AssertEqual(outData, data); - void Set( - [MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - int[][] array, - [MarshalUsing(CountElementName = nameof(length))] - int[] widths, - int length); - } + data = GetStartingValue(); + obj.MethodRef(ref data, data.Length); + AssertEqual(refData, data); - [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; - } - } + data = GetStartingValue(); + obj.MethodContentsInOut(data, data.Length); + AssertEqual(refData, data); - [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) + static void AssertEqual(StatelessType[] param1, StatelessType[] param2) { - if (_marshalledCount++ == 3) + Assert.Equal(param1.Length, param2.Length); + for (int i = 0; i < param1.Length; i++) { - _marshalledCount = 0; - throw new ArgumentException("The element was the 4th element (with 0-based index 3)"); + Assert.Equal(param1[i].I, param2[i].I); } - return managed; } - public static int ConvertToManaged(nint unmanaged) + static StatelessType[] GetStartingValue() { - if (_unmarshalledCount++ == 3) + return new StatelessType[] { - _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[] { 1, 2, 3 }; - 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); - } - - [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; - } - public void Set(int[][] array, int[] widths, int length) - { - _data = array; - _widths = widths; - } - } - - [GeneratedComInterface] - [Guid("A4857398-06FB-4A6E-81DB-35461BE999C5")] - internal partial interface IInterface - { - public IInt Get(); - public void Set(IInt value); - } - - [GeneratedComClass] - public partial class IInterfaceImpl : IInterface - { - IInt _data = new IIntImpl(); - IInt IInterface.Get() => _data; - void IInterface.Set(IInt value) - { - int x = value.Get(); - value.Set(x); - _data = value; + new StatelessType() {I = 5}, + new StatelessType() {I = 4}, + new StatelessType() {I = 3}, + new StatelessType() {I = 2}, + new StatelessType() {I = 1}, + new StatelessType() {I = 0} + }; } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/UnmanagedToManagedCustomMarshallingTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/UnmanagedToManagedCustomMarshallingTests.cs index 54e96f3b268f8f..33943867854284 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/UnmanagedToManagedCustomMarshallingTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/UnmanagedToManagedCustomMarshallingTests.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -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; -using System.Threading.Tasks; using SharedTypes; using Xunit; using static ComInterfaceGenerator.Tests.UnmanagedToManagedCustomMarshallingTests; @@ -416,6 +413,8 @@ public struct Ref private TUnmanaged* _originalUnmanaged; private TUnmanaged* _unmanaged; + private TUnmanaged* Unmanaged => _unmanaged == null ? _unmanaged = (TUnmanaged*)Marshal.AllocCoTaskMem(sizeof(TUnmanaged) * _managed.Length) : _unmanaged; + private TManaged[] _managed; public void FromUnmanaged(TUnmanaged* unmanaged) @@ -451,7 +450,7 @@ public void FromManaged(TManaged[] managed) public TUnmanaged* ToUnmanaged() { - return _unmanaged = (TUnmanaged*)Marshal.AllocCoTaskMem(sizeof(TUnmanaged) * _managed.Length); + return Unmanaged; } public ReadOnlySpan GetManagedValuesSource() @@ -461,7 +460,7 @@ public ReadOnlySpan GetManagedValuesSource() public Span GetUnmanagedValuesDestination() { - return new(_unmanaged, _managed.Length); + return new(Unmanaged, _managed.Length); } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs index 4fbe5cabbcd803..ea91fa0b7454ec 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -780,11 +779,12 @@ public static IEnumerable ByValueMarshalAttributeOnPinnedMarshalledTyp .WithLocation(1) .WithLocation(2) .WithArguments(SR.InOutAttributes, paramName, SR.PinnedMarshallingIsInOutByDefault); - yield return new object[] { - ID(), - codeSnippets.ByValueMarshallingOfType(inAttribute + outAttribute + constElementCount, "int[]", paramNameWithLocation), - new DiagnosticResult[] { inOutAttributeIsDefaultDiagnostic } - }; + // https://github.com/dotnet/runtime/issues/89265 + //yield return new object[] { + // ID(), + // codeSnippets.ByValueMarshallingOfType(inAttribute + outAttribute + constElementCount, "int[]", paramNameWithLocation), + // new DiagnosticResult[] { inOutAttributeIsDefaultDiagnostic } + //}; yield return new object[] { ID(), codeSnippets.ByValueMarshallingOfType(inAttribute + outAttribute + constElementCount, "char[]", paramNameWithLocation, (StringMarshalling.Utf16, null)), diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs index 3e51f406db3da6..ec5ac6f6518595 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs @@ -208,16 +208,6 @@ public void MultiDimensionalOutArray_EnsureAllCleaned() { var arr = GetMultiDimensionalArray(10, 10); var widths = new int[10] { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; - //foreach (var throwOn in new int[] { 0, 1, 45, 99 }) - //{ - // EnforceAllElementsCleanedUpBoolStruct.ThrowOnNthUnmarshalledElement(throwOn); - // EnforceAllElementsCleanedUpBoolStruct.ExpectedCleanupNumber = 100; - // Assert.Throws(() => - // { - // NativeExportsNE.MarshallingFails.NegateBoolsOut2D(arr, arr.Length, widths, out BoolStruct[][] boolsOut); - // }); - // EnforceAllElementsCleanedUpBoolStruct.AssertAllHaveBeenCleaned(); - //} // Run without throwing - this is okay only because the native code doesn't actually use the array, it creates a whole new one EnforceAllElementsCleanedUpBoolStruct.ThrowOnNthUnmarshalledElement(-1); EnforceAllElementsCleanedUpBoolStruct.ExpectedCleanupNumber = 100; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/LibraryImportGenerator.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/LibraryImportGenerator.Tests.csproj index 9b91d1800dc395..38b9bda2a469b8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/LibraryImportGenerator.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/LibraryImportGenerator.Tests.csproj @@ -7,13 +7,13 @@ true true + \src\GeneratedCode\ $(OutputPath)\Generated $(InteropCompilerGeneratedFilesOutputPath)\LibraryImportGenerator.Tests - + 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..67b227c51b943f 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 @@ -53,7 +53,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..06c52cfa5000ab 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 @@ -81,7 +81,7 @@ static void* S_Utf16VTable if (obj is IUTF8Marshalling) { ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Utf8Implementation), sizeof(ComInterfaceEntry)); - comInterfaceEntry->IID = new Guid(IUTF8Marshalling._guid); + comInterfaceEntry->IID = new Guid(IUTF8Marshalling.IID); comInterfaceEntry->Vtable = (nint)S_Utf8VTable; count = 1; return comInterfaceEntry; @@ -89,7 +89,7 @@ static void* S_Utf16VTable else if (obj is IUTF16Marshalling) { ComInterfaceEntry* comInterfaceEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Utf16Implementation), sizeof(ComInterfaceEntry)); - comInterfaceEntry->IID = new Guid(IUTF16Marshalling._guid); + comInterfaceEntry->IID = new Guid(IUTF16Marshalling.IID); comInterfaceEntry->Vtable = (nint)S_Utf16VTable; count = 1; return comInterfaceEntry; 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..8d5c14fa96c2c2 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 @@ -76,9 +76,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 +86,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/NativeExports/NativeExports.csproj b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj index 7c5b7e09bf1006..2a7d9b149f94ab 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj @@ -18,6 +18,13 @@ '$(TargetOS)' == 'tvossimulator'">true + + + + + + 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 54669fa6c380ec..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 @@ -2,22 +2,21 @@ // 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] - [Guid(_guid)] + [Guid(IID)] 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"; + 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 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 p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection ReturnPreserveSig(int size); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs new file mode 100644 index 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 p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatefulCollectionMarshaller<,>))] + internal class StatefulCollection + { + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatefulCollection<>), MarshalMode.Default, typeof(StatefulCollectionMarshaller<,>.Default))] + static unsafe class StatefulCollectionMarshaller where TUnmanagedElement : unmanaged + { + public ref struct Default + { + public byte* ToUnmanaged() => throw null; + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetUnmanagedValuesDestination() => throw null; + + public void FromUnmanaged(byte* value) => throw null; + public System.Span GetManagedValuesDestination(int numElements) => throw null; + public System.ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw null; + + public void Free() => throw null; + + public void FromManaged(StatefulCollection managed) => throw null; + + public StatefulCollection ToManaged() => throw null; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs new file mode 100644 index 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 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 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 p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection ReturnPreserveSig(int size); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs new file mode 100644 index 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 p, + int size); + + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, + in int size); + + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, + int size); + + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, + out int size); + + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection Return(int size); + + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatelessCollectionMarshaller<,>))] + internal class StatelessCollection + { + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.Default, typeof(StatelessCollectionMarshaller<,>.Default))] + internal static unsafe class StatelessCollectionMarshaller where TUnmanagedElement : unmanaged + { + internal static class Default + { + public static nint AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) + { + throw new System.NotImplementedException(); + } + + public static StatelessCollection AllocateContainerForManagedElements(nint unmanaged, int numElements) + { + throw new System.NotImplementedException(); + } + + public static System.ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) + { + throw new System.NotImplementedException(); + } + + public static System.Span GetUnmanagedValuesDestination(nint unmanaged, int numElements) + { + throw new System.NotImplementedException(); + } + + public static System.ReadOnlySpan GetUnmanagedValuesSource(nint unmanaged, int numElements) + { + throw new System.NotImplementedException(); + } + + public static System.Span GetManagedValuesDestination(StatelessCollection managed) + { + throw new System.NotImplementedException(); + } + + public static void Free(nint unmanaged) { } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs new file mode 100644 index 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..1cb2f338216567 --- /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 paramyBoi, 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..6e01be3d7e7da3 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs @@ -0,0 +1,38 @@ +// 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(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; + } + 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..a0d8e964b8f935 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs @@ -0,0 +1,63 @@ +// 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); + + 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; + } + 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/ThrowOn4thElementMarshalled.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs new file mode 100644 index 00000000000000..99e01d95278e43 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs @@ -0,0 +1,38 @@ +// 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.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 ArgumentException("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 ArgumentException("The element was the 4th element (with 0-based index 3)"); + } + return (int)unmanaged; + } + public static void Free(nint unmanaged) => ++FreeCount; + } + +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/SharedTypes.csproj b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/SharedTypes.csproj index c68eadac7ca4b4..06fadd8571d0bd 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/SharedTypes.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/SharedTypes.csproj @@ -9,6 +9,13 @@ + + + + + +