Skip to content

Commit b9f3597

Browse files
committed
Add some basic marshalling tests to give some validation that we set up the marshalling generator correctly (E2E-style validation)
1 parent 6d7327b commit b9f3597

File tree

4 files changed

+121
-5
lines changed

4 files changed

+121
-5
lines changed

src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
103103
)
104104
.WithTrackingName(StepNames.CalculateStubInformation);
105105

106-
IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray<Diagnostic>)> generateSingleStub = generateStubInformation
106+
IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray<Diagnostic>)> generateManagedToNativeStub = generateStubInformation
107+
.Where(data => data.VtableIndexData.Direction.HasFlag(CustomTypeMarshallerDirection.In))
107108
.Select(
108109
static (data, ct) => GenerateManagedToNativeStub(data)
109110
)
110111
.WithComparer(Comparers.GeneratedSyntax)
111112
.WithTrackingName(StepNames.GenerateManagedToNativeStub);
112113

113-
context.RegisterDiagnostics(generateSingleStub.SelectMany((stubInfo, ct) => stubInfo.Item2));
114+
context.RegisterDiagnostics(generateManagedToNativeStub.SelectMany((stubInfo, ct) => stubInfo.Item2));
114115

115-
context.RegisterConcatenatedSyntaxOutputs(generateSingleStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs");
116+
context.RegisterConcatenatedSyntaxOutputs(generateManagedToNativeStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs");
116117

117118
IncrementalValuesProvider<MemberDeclarationSyntax> generateNativeInterface = generateStubInformation
118119
.Select(static (context, ct) => context.ContainingSyntaxContext)

src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BoundGenerators.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,16 @@ static IEnumerable<int> GetInfoDependencies(TypePositionInfo info)
101101

102102
static int GetInfoIndex(TypePositionInfo info)
103103
{
104+
// A TypePositionInfo needs to have either a managed or native index.
105+
// We use negative values of the native index to distinguish them from the managed index.
104106
if (info.ManagedIndex == TypePositionInfo.UnsetIndex)
105107
{
106-
// A TypePositionInfo needs to have either a managed or native index.
107-
// We use negative values of the native index to distinguish them from the managed index.
108+
if (info.NativeIndex == 0)
109+
{
110+
// If we don't have a managed index and the native index is zero, use ReturnIndex + 1 as our
111+
// index to avoid conflict with managed parameter 0.
112+
return TypePositionInfo.ReturnIndex + 1;
113+
}
108114
return -info.NativeIndex;
109115
}
110116
return info.ManagedIndex;

src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,90 @@ sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider<NoCasting>, INativ
8585
}
8686
";
8787

88+
public static string BasicParametersAndModifiers(string typeName) => $@"
89+
using System.Runtime.CompilerServices;
90+
using System.Runtime.InteropServices;
91+
92+
[assembly:DisableRuntimeMarshalling]
93+
94+
readonly record struct NoCasting {{}}
95+
partial interface INativeAPI
96+
{{
97+
public static readonly NoCasting TypeKey = default;
98+
[VirtualMethodIndex(0)]
99+
{typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue);
100+
}}
101+
102+
// Try using the generated native interface
103+
sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider<NoCasting>, INativeAPI.Native
104+
{{
105+
public VirtualMethodTableInfo GetFunctionPointerForIndex(NoCasting typeKey) => throw null;
106+
}}
107+
";
108+
109+
public static string BasicParametersAndModifiers<T>() => BasicParametersAndModifiers(typeof(T).FullName!);
110+
111+
public const string CustomTypeMarshallingTestsTypeName = "S";
112+
113+
public static string SimpleCustomTypeMarshallingDeclaration = $@"
114+
[NativeMarshalling(typeof(Marshaller))]
115+
struct {CustomTypeMarshallingTestsTypeName} {{}}
116+
117+
[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}))]
118+
struct Marshaller
119+
{{
120+
public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}}
121+
122+
public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null;
123+
}}
124+
";
125+
126+
public static string TwoStageCustomTypeMarshallingDeclaration = $@"
127+
[NativeMarshalling(typeof(Marshaller))]
128+
struct {CustomTypeMarshallingTestsTypeName} {{}}
129+
130+
[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)]
131+
struct Marshaller
132+
{{
133+
public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}}
134+
135+
public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null;
136+
137+
public int ToNativeValue() => throw null;
138+
139+
public void FromNativeValue(int i) => throw null;
140+
}}
141+
";
142+
143+
public static string OptionalCallerAllocatedBufferMarshallingDeclaration = $@"
144+
[NativeMarshalling(typeof(Marshaller))]
145+
struct {CustomTypeMarshallingTestsTypeName}
146+
{{
147+
}}
148+
149+
[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)]
150+
struct Marshaller
151+
{{
152+
public Marshaller({CustomTypeMarshallingTestsTypeName} s, System.Span<byte> b) {{}}
153+
public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}}
154+
155+
public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null;
156+
}}
157+
";
158+
159+
public static string UnmanagedResourcesCustomTypeMarshallingDeclaration = $@"
160+
[NativeMarshalling(typeof(Marshaller))]
161+
struct {CustomTypeMarshallingTestsTypeName} {{}}
162+
163+
[CustomTypeMarshaller(typeof({CustomTypeMarshallingTestsTypeName}), Features = CustomTypeMarshallerFeatures.UnmanagedResources)]
164+
struct Marshaller
165+
{{
166+
public Marshaller({CustomTypeMarshallingTestsTypeName} managed) {{}}
167+
168+
public {CustomTypeMarshallingTestsTypeName} ToManaged() => throw null;
169+
170+
public void FreeNative() {{}}
171+
}}
172+
";
88173
}
89174
}

src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Threading.Tasks;
67
using Microsoft.CodeAnalysis;
@@ -16,6 +17,29 @@ public static IEnumerable<object[]> VTableIndexCodeSnippetsToCompile()
1617
yield return new[] { CodeSnippets.SpecifiedMethodIndexNoExplicitParameters };
1718
yield return new[] { CodeSnippets.SpecifiedMethodIndexNoExplicitParametersNoImplicitThis };
1819
yield return new[] { CodeSnippets.SpecifiedMethodIndexNoExplicitParametersCallConvWithCallingConventions };
20+
21+
// Basic marshalling validation
22+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<byte>() };
23+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<sbyte>() };
24+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<short>() };
25+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<ushort>() };
26+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<int>() };
27+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<uint>() };
28+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<long>() };
29+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<ulong>() };
30+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<float>() };
31+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<double>() };
32+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<IntPtr>() };
33+
yield return new[] { CodeSnippets.BasicParametersAndModifiers<UIntPtr>() };
34+
35+
// Attributed marshalling model validation
36+
yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.SimpleCustomTypeMarshallingDeclaration };
37+
yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.TwoStageCustomTypeMarshallingDeclaration };
38+
yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.OptionalCallerAllocatedBufferMarshallingDeclaration };
39+
yield return new[] { CodeSnippets.BasicParametersAndModifiers(CodeSnippets.CustomTypeMarshallingTestsTypeName) + CodeSnippets.UnmanagedResourcesCustomTypeMarshallingDeclaration };
40+
41+
// SafeHandles
42+
yield return new[] { CodeSnippets.BasicParametersAndModifiers("Microsoft.Win32.SafeHandles.SafeFileHandle") };
1943
}
2044

2145
[Theory]

0 commit comments

Comments
 (0)