From b9f3597a8caa1277b5375ea1682e58345238a478 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Apr 2022 14:18:20 -0700 Subject: [PATCH] Add some basic marshalling tests to give some validation that we set up the marshalling generator correctly (E2E-style validation) --- .../VtableIndexStubGenerator.cs | 7 +- .../BoundGenerators.cs | 10 ++- .../CodeSnippets.cs | 85 +++++++++++++++++++ .../Compiles.cs | 24 ++++++ 4 files changed, 121 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs index f71d1432ee6ac..6a5c8f0d91b7c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs @@ -103,16 +103,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ) .WithTrackingName(StepNames.CalculateStubInformation); - IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray)> generateSingleStub = generateStubInformation + IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray)> generateManagedToNativeStub = generateStubInformation + .Where(data => data.VtableIndexData.Direction.HasFlag(CustomTypeMarshallerDirection.In)) .Select( static (data, ct) => GenerateManagedToNativeStub(data) ) .WithComparer(Comparers.GeneratedSyntax) .WithTrackingName(StepNames.GenerateManagedToNativeStub); - context.RegisterDiagnostics(generateSingleStub.SelectMany((stubInfo, ct) => stubInfo.Item2)); + context.RegisterDiagnostics(generateManagedToNativeStub.SelectMany((stubInfo, ct) => stubInfo.Item2)); - context.RegisterConcatenatedSyntaxOutputs(generateSingleStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs"); + context.RegisterConcatenatedSyntaxOutputs(generateManagedToNativeStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs"); IncrementalValuesProvider generateNativeInterface = generateStubInformation .Select(static (context, ct) => context.ContainingSyntaxContext) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs index 90ca43a64abbd..b1f95c1903a1c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs @@ -101,10 +101,16 @@ static IEnumerable GetInfoDependencies(TypePositionInfo info) static int GetInfoIndex(TypePositionInfo info) { + // A TypePositionInfo needs to have either a managed or native index. + // We use negative values of the native index to distinguish them from the managed index. if (info.ManagedIndex == TypePositionInfo.UnsetIndex) { - // A TypePositionInfo needs to have either a managed or native index. - // We use negative values of the native index to distinguish them from the managed index. + if (info.NativeIndex == 0) + { + // If we don't have a managed index and the native index is zero, use ReturnIndex + 1 as our + // index to avoid conflict with managed parameter 0. + return TypePositionInfo.ReturnIndex + 1; + } return -info.NativeIndex; } return info.ManagedIndex; diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs index 78a5d550260d7..af21ae18bd081 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs @@ -85,5 +85,90 @@ sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativ } "; + public static string BasicParametersAndModifiers(string typeName) => $@" +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly:DisableRuntimeMarshalling] + +readonly record struct NoCasting {{}} +partial interface INativeAPI +{{ + public static readonly NoCasting TypeKey = default; + [VirtualMethodIndex(0)] + {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); +}} + +// Try using the generated native interface +sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native +{{ + public VirtualMethodTableInfo GetFunctionPointerForIndex(NoCasting typeKey) => throw null; +}} +"; + + public static string BasicParametersAndModifiers() => BasicParametersAndModifiers(typeof(T).FullName!); + + public const string CustomTypeMarshallingTestsTypeName = "S"; + + public static string SimpleCustomTypeMarshallingDeclaration = $@" +[NativeMarshalling(typeof(Marshaller))] +struct {CustomTypeMarshallingTestsTypeName} {{}} + +[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}))] +struct Marshaller +{{ + public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}} + + public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null; +}} +"; + + public static string TwoStageCustomTypeMarshallingDeclaration = $@" +[NativeMarshalling(typeof(Marshaller))] +struct {CustomTypeMarshallingTestsTypeName} {{}} + +[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Marshaller +{{ + public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}} + + public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null; + + public int ToNativeValue() => throw null; + + public void FromNativeValue(int i) => throw null; +}} +"; + + public static string OptionalCallerAllocatedBufferMarshallingDeclaration = $@" +[NativeMarshalling(typeof(Marshaller))] +struct {CustomTypeMarshallingTestsTypeName} +{{ +}} + +[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)] +struct Marshaller +{{ + public Marshaller({CustomTypeMarshallingTestsTypeName} s, System.Span b) {{}} + public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}} + + public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null; +}} +"; + + public static string UnmanagedResourcesCustomTypeMarshallingDeclaration = $@" +[NativeMarshalling(typeof(Marshaller))] +struct {CustomTypeMarshallingTestsTypeName} {{}} + +[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] +struct Marshaller +{{ + public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}} + + public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null; + + public void FreeNative() {{}} +}} +"; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs index ad1933c03563d..b395fae11037d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -16,6 +17,29 @@ public static IEnumerable VTableIndexCodeSnippetsToCompile() yield return new[] { CodeSnippets.SpecifiedMethodIndexNoExplicitParameters }; yield return new[] { CodeSnippets.SpecifiedMethodIndexNoExplicitParametersNoImplicitThis }; yield return new[] { CodeSnippets.SpecifiedMethodIndexNoExplicitParametersCallConvWithCallingConventions }; + + // Basic marshalling validation + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + + // Attributed marshalling model validation + yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.SimpleCustomTypeMarshallingDeclaration }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.TwoStageCustomTypeMarshallingDeclaration }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.OptionalCallerAllocatedBufferMarshallingDeclaration }; + yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.UnmanagedResourcesCustomTypeMarshallingDeclaration }; + + // SafeHandles + yield return new[] { CodeSnippets.BasicParametersAndModifiers("Microsoft.Win32.SafeHandles.SafeFileHandle") }; } [Theory]