From 49da0bdacbebbedff735eef2e155cea3e76c3fd6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 7 Mar 2023 13:03:33 -0800 Subject: [PATCH] Change the ComInterfaceGenerator to output source in one file per interface and use file-scoped types (#83055) --- .../ComInterfaceGenerator/ComInterfaceGenerator.cs | 526 +++++++++++++++++---- .../ComInterfaceGenerator/GeneratorDiagnostics.cs | 11 + .../IncrementalMethodStubGenerationContext.cs | 5 +- .../IncrementalValuesProviderExtensions.cs | 6 + .../ComInterfaceGenerator/Resources/Strings.resx | 453 +++++++++--------- .../Resources/xlf/Strings.cs.xlf | 15 + .../Resources/xlf/Strings.de.xlf | 15 + .../Resources/xlf/Strings.es.xlf | 15 + .../Resources/xlf/Strings.fr.xlf | 15 + .../Resources/xlf/Strings.it.xlf | 15 + .../Resources/xlf/Strings.ja.xlf | 15 + .../Resources/xlf/Strings.ko.xlf | 15 + .../Resources/xlf/Strings.pl.xlf | 15 + .../Resources/xlf/Strings.pt-BR.xlf | 15 + .../Resources/xlf/Strings.ru.xlf | 15 + .../Resources/xlf/Strings.tr.xlf | 15 + .../Resources/xlf/Strings.zh-Hans.xlf | 15 + .../Resources/xlf/Strings.zh-Hant.xlf | 15 + .../VirtualMethodPointerStubGenerator.cs | 200 ++++++++ .../VtableIndexStubGenerator.cs | 188 +------- .../SyntaxEquivalentNode.cs | 21 + .../TypeNames.cs | 15 + .../Ancillary.Interop/IUnknownDerivedAttribute.cs | 1 - .../CallingConventionForwarding.cs | 2 +- .../CodeSnippets.cs | 37 +- .../ComInterfaceGeneratorOutputShape.cs | 145 ++++++ .../ComInterfaceGenerator.Unit.Tests/Compiles.cs | 2 - .../GeneratedComInterfaceAttributeProvider.cs | 4 +- .../IVirtualMethodIndexSignatureProvider.cs | 26 +- ...rfaceShape.cs => VTableGeneratorOutputShape.cs} | 32 +- 30 files changed, 1340 insertions(+), 529 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VirtualMethodPointerStubGenerator.cs create mode 100644 src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentNode.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs rename src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/{NativeInterfaceShape.cs => VTableGeneratorOutputShape.cs} (84%) diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs index a2c9391..659570a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading; -using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -18,21 +16,33 @@ namespace Microsoft.Interop [Generator] public sealed class ComInterfaceGenerator : IIncrementalGenerator { - private sealed record ComInterfaceContext(int MethodStartIndex, Guid InterfaceId); + private sealed record ComInterfaceContext( + ManagedTypeInfo InterfaceType, + ContainingSyntaxContext TypeDefinitionContext, + ContainingSyntax InterfaceTypeSyntax, + int MethodStartIndex, + Guid InterfaceId); + + private sealed record class GeneratedStubCodeContext( + ManagedTypeInfo OriginalDefiningType, + ContainingSyntaxContext ContainingSyntaxContext, + SyntaxEquivalentNode Stub, + SequenceEqualImmutableArray Diagnostics) : GeneratedMethodContextBase(OriginalDefiningType, Diagnostics); + + private sealed record SkippedStubContext(ManagedTypeInfo OriginalDefiningType) : GeneratedMethodContextBase(OriginalDefiningType, new(ImmutableArray.Empty)); public static class StepNames { public const string CalculateStubInformation = nameof(CalculateStubInformation); public const string GenerateManagedToNativeStub = nameof(GenerateManagedToNativeStub); public const string GenerateNativeToManagedStub = nameof(GenerateNativeToManagedStub); + public const string GenerateManagedToNativeInterfaceImplementation = nameof(GenerateManagedToNativeInterfaceImplementation); + public const string GenerateNativeToManagedVTableMethods = nameof(GenerateNativeToManagedVTableMethods); + public const string GenerateNativeToManagedVTable = nameof(GenerateNativeToManagedVTable); + public const string GenerateInterfaceInformation = nameof(GenerateInterfaceInformation); + public const string GenerateIUnknownDerivedAttribute = nameof(GenerateIUnknownDerivedAttribute); } - private static readonly ContainingSyntax NativeTypeContainingSyntax = new( - TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.PartialKeyword)), - SyntaxKind.InterfaceDeclaration, - Identifier("Native"), - null); - public void Initialize(IncrementalGeneratorInitializationContext context) { // Get all methods with the [GeneratedComInterface] attribute. @@ -60,25 +70,33 @@ namespace Microsoft.Interop { // Start at offset 3 as 0-2 are IUnknown. // TODO: Calculate starting offset based on base types. - // TODO: Extract IID from source. - return new ComInterfaceContext(3, Guid.Empty); + Guid? guid = null; + var guidAttr = data.Symbol.GetAttributes().Where(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_GuidAttribute).SingleOrDefault(); + if (guidAttr is not null) + { + string? guidstr = guidAttr.ConstructorArguments.SingleOrDefault().Value as string; + if (guidstr is not null) + guid = new Guid(guidstr); + } + return new ComInterfaceContext( + ManagedTypeInfo.CreateTypeInfoForTypeSymbol(data.Symbol), + new ContainingSyntaxContext(data.Syntax), + new ContainingSyntax(data.Syntax.Modifiers, data.Syntax.Kind(), data.Syntax.Identifier, data.Syntax.TypeParameterList), + 3, + guid ?? Guid.Empty); }); - context.RegisterSourceOutput(invalidTypeDiagnostics, static (context, invalidType) => - { - context.ReportDiagnostic(invalidType.Diagnostic); - }); + context.RegisterDiagnostics(invalidTypeDiagnostics.Select((data, ct) => data.Diagnostic)); // Zip the incremental interface context back with the symbols and syntax for the interface // to calculate the methods to generate. // The generator infrastructure preserves ordering of the tables once Select statements are in use, // so we can rely on the order matching here. - var methodsWithDiagnostics = interfacesToGenerate + var interfacesWithMethods = interfacesToGenerate .Zip(interfaceContexts) - .SelectMany(static (data, ct) => + .Select(static (data, ct) => { var (interfaceData, interfaceContext) = data; - ContainingSyntaxContext containingSyntax = new(interfaceData.Syntax); Location interfaceLocation = interfaceData.Syntax.GetLocation(); var methods = ImmutableArray.CreateBuilder<(MethodDeclarationSyntax Syntax, IMethodSymbol Symbol, int Index, Diagnostic? Diagnostic)>(); int methodVtableOffset = interfaceContext.MethodStartIndex; @@ -120,9 +138,35 @@ namespace Microsoft.Interop } } } - return methods.ToImmutable(); + return (Interface: interfaceContext, Methods: methods.ToImmutable()); }); + var interfaceWithMethodsContexts = interfacesWithMethods + .Where(data => data.Methods.Length > 0) + .Select(static (data, ct) => data.Interface); + + // Marker interfaces are COM interfaces that don't have any methods. + // The lack of methods breaks the mechanism we use later to stitch back together interface-level data + // and method-level data, but that's okay because marker interfaces are much simpler. + // We'll handle them seperately because they are so simple. + var markerInterfaces = interfacesWithMethods + .Where(data => data.Methods.Length == 0) + .Select(static (data, ct) => data.Interface); + + var markerInterfaceIUnknownDerived = markerInterfaces.Select(static (context, ct) => GenerateIUnknownDerivedAttributeApplication(context)) + .WithComparer(SyntaxEquivalentComparer.Instance) + .SelectNormalized(); + + context.RegisterSourceOutput(markerInterfaces.Zip(markerInterfaceIUnknownDerived), (context, data) => + { + var (interfaceContext, iUnknownDerivedAttributeApplication) = data; + context.AddSource( + interfaceContext.InterfaceType.FullTypeName.Replace("global::", ""), + GenerateMarkerInterfaceSource(interfaceContext) + iUnknownDerivedAttributeApplication); + }); + + var methodsWithDiagnostics = interfacesWithMethods.SelectMany(static (data, ct) => data.Methods); + // Split the methods we want to generate and the ones we don't into two separate groups. var methodsToGenerate = methodsWithDiagnostics.Where(static data => data.Diagnostic is null); var invalidMethodDiagnostics = methodsWithDiagnostics.Where(static data => data.Diagnostic is not null); @@ -149,55 +193,159 @@ namespace Microsoft.Interop .WithTrackingName(StepNames.CalculateStubInformation); // Generate the code for the managed-to-unmanaged stubs and the diagnostics from code-generation. - IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray)> generateManagedToNativeStub = generateStubInformation - .Where(data => data.VtableIndexData.Direction is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional) + var generateManagedToNativeStub = generateStubInformation .Select( - static (data, ct) => VtableIndexStubGenerator.GenerateManagedToNativeStub(data) + static (data, ct) => + { + if (data.VtableIndexData.Direction is not (MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional)) + { + return (GeneratedMethodContextBase)new SkippedStubContext(data.OriginalDefiningType); + } + var (methodStub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateManagedToNativeStub(data); + return new GeneratedStubCodeContext(data.TypeKeyOwner, data.ContainingSyntaxContext, new(methodStub), new(diagnostics)); + } ) - .WithComparer(Comparers.GeneratedSyntax) .WithTrackingName(StepNames.GenerateManagedToNativeStub); - context.RegisterDiagnostics(generateManagedToNativeStub.SelectMany((stubInfo, ct) => stubInfo.Item2)); + context.RegisterDiagnostics(generateManagedToNativeStub.SelectMany((stubInfo, ct) => stubInfo.Diagnostics.Array)); - context.RegisterConcatenatedSyntaxOutputs(generateManagedToNativeStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs"); + var managedToNativeInterfaceImplementations = generateManagedToNativeStub + .Collect() + .SelectMany(static (stubs, ct) => GroupContextsForInterfaceGeneration(stubs)) + .Select(static (interfaceGroup, ct) => GenerateImplementationInterface(interfaceGroup.Array)) + .WithTrackingName(StepNames.GenerateManagedToNativeInterfaceImplementation) + .WithComparer(SyntaxEquivalentComparer.Instance) + .SelectNormalized(); // Filter the list of all stubs to only the stubs that requested unmanaged-to-managed stub generation. IncrementalValuesProvider nativeToManagedStubContexts = generateStubInformation - .Where(data => data.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional); + .Where(static data => data.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional); // Generate the code for the unmanaged-to-managed stubs and the diagnostics from code-generation. - IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray)> generateNativeToManagedStub = nativeToManagedStubContexts + var generateNativeToManagedStub = generateStubInformation .Select( - static (data, ct) => VtableIndexStubGenerator.GenerateNativeToManagedStub(data) + static (data, ct) => + { + if (data.VtableIndexData.Direction is not (MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional)) + { + return (GeneratedMethodContextBase)new SkippedStubContext(data.OriginalDefiningType); + } + var (methodStub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateNativeToManagedStub(data); + return new GeneratedStubCodeContext(data.OriginalDefiningType, data.ContainingSyntaxContext, new(methodStub), new(diagnostics)); + } ) - .WithComparer(Comparers.GeneratedSyntax) .WithTrackingName(StepNames.GenerateNativeToManagedStub); - context.RegisterDiagnostics(generateNativeToManagedStub.SelectMany((stubInfo, ct) => stubInfo.Item2)); - - context.RegisterConcatenatedSyntaxOutputs(generateNativeToManagedStub.Select((data, ct) => data.Item1), "NativeToManagedStubs.g.cs"); + context.RegisterDiagnostics(generateNativeToManagedStub.SelectMany((stubInfo, ct) => stubInfo.Diagnostics.Array)); - // Generate the native interface metadata for each interface that contains a method with the [VirtualMethodIndex] attribute. - IncrementalValuesProvider generateNativeInterface = generateStubInformation - .Select(static (context, ct) => context.ContainingSyntaxContext) + var nativeToManagedVtableMethods = generateNativeToManagedStub .Collect() - .SelectMany(static (syntaxContexts, ct) => syntaxContexts.Distinct()) - .Select(static (context, ct) => GenerateNativeInterfaceMetadata(context)); + .SelectMany(static (stubs, ct) => GroupContextsForInterfaceGeneration(stubs)) + .Select(static (interfaceGroup, ct) => GenerateImplementationVTableMethods(interfaceGroup.Array)) + .WithTrackingName(StepNames.GenerateNativeToManagedVTableMethods) + .WithComparer(SyntaxEquivalentComparer.Instance) + .SelectNormalized(); + + // Generate the native interface metadata for each [GeneratedComInterface]-attributed interface. + var nativeInterfaceInformation = interfaceWithMethodsContexts + .Select(static (context, ct) => GenerateInterfaceInformation(context)) + .WithTrackingName(StepNames.GenerateInterfaceInformation) + .WithComparer(SyntaxEquivalentComparer.Instance) + .SelectNormalized(); + + // Generate a method named CreateManagedVirtualFunctionTable on the native interface implementation + // that allocates and fills in the memory for the vtable. + var nativeToManagedVtables = + generateStubInformation + .Collect() + .SelectMany(static (data, ct) => GroupContextsForInterfaceGeneration(data.CastArray())) + .Select(static (vtable, ct) => GenerateImplementationVTable(ImmutableArray.CreateRange(vtable.Array.Cast()))) + .WithTrackingName(StepNames.GenerateNativeToManagedVTable) + .WithComparer(SyntaxEquivalentComparer.Instance) + .SelectNormalized(); + + var iUnknownDerivedAttributeApplication = interfaceWithMethodsContexts + .Select(static (context, ct) => GenerateIUnknownDerivedAttributeApplication(context)) + .WithTrackingName(StepNames.GenerateIUnknownDerivedAttribute) + .WithComparer(SyntaxEquivalentComparer.Instance) + .SelectNormalized(); + + var filesToGenerate = interfaceWithMethodsContexts + .Zip(nativeInterfaceInformation) + .Zip(managedToNativeInterfaceImplementations) + .Zip(nativeToManagedVtableMethods) + .Zip(nativeToManagedVtables) + .Zip(iUnknownDerivedAttributeApplication) + .Select(static (data, ct) => + { + var (((((interfaceContext, interfaceInfo), managedToNativeStubs), nativeToManagedStubs), nativeToManagedVtable), iUnknownDerivedAttribute) = data; + + using StringWriter source = new(); + interfaceInfo.WriteTo(source); + // Two newlines looks cleaner than one + source.WriteLine(); + source.WriteLine(); + // TODO: Merge the three InterfaceImplementation partials? We have them all right here. + managedToNativeStubs.WriteTo(source); + source.WriteLine(); + source.WriteLine(); + nativeToManagedStubs.WriteTo(source); + source.WriteLine(); + source.WriteLine(); + nativeToManagedVtable.WriteTo(source); + source.WriteLine(); + source.WriteLine(); + iUnknownDerivedAttribute.WriteTo(source); + return new { TypeName = interfaceContext.InterfaceType.FullTypeName, Source = source.ToString() }; + }); + + context.RegisterSourceOutput(filesToGenerate, (context, data) => + { + context.AddSource(data.TypeName.Replace("global::", ""), data.Source); + }); + } - context.RegisterConcatenatedSyntaxOutputs(generateNativeInterface, "NativeInterfaces.g.cs"); + private static string GenerateMarkerInterfaceSource(ComInterfaceContext iface) => $$""" + file unsafe class InterfaceInformation : global::System.Runtime.InteropServices.Marshalling.IIUnknownInterfaceType + { + public static global::System.Guid Iid => new(new global::System.ReadOnlySpan(new byte[] { {{string.Join(",", iface.InterfaceId.ToByteArray())}} })); - // Generate a method named PopulateUnmanagedVirtualMethodTable on the native interface implementation - // that fills in a span with the addresses of the unmanaged-to-managed stub functions at their correct - // indices. - IncrementalValuesProvider populateVTable = - nativeToManagedStubContexts - .Collect() - .SelectMany(static (data, ct) => data.GroupBy(stub => stub.ContainingSyntaxContext)) - .Select(static (vtable, ct) => GeneratePopulateVTableMethod(vtable)); + private static void** m_vtable; - context.RegisterConcatenatedSyntaxOutputs(populateVTable, "PopulateVTable.g.cs"); - } + public static void* VirtualMethodTableManagedImplementation + { + get + { + if (m_vtable == null) + { + nint* vtable = (nint*)global::System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(typeof({{iface.InterfaceType.FullTypeName}}), sizeof(nint) * 3); + global::System.Runtime.InteropServices.ComWrappers.GetIUnknownImpl(out vtable[0], out vtable[1], out vtable[2]); + m_vtable = (void**)vtable; + } + return m_vtable; + } + } + } + + [global::System.Runtime.InteropServices.DynamicInterfaceCastableImplementation] + file interface InterfaceImplementation : {{iface.InterfaceType.FullTypeName}} + {} + """; + + private static readonly AttributeSyntax s_iUnknownDerivedAttributeTemplate = + Attribute( + GenericName(TypeNames.IUnknownDerivedAttribute) + .AddTypeArgumentListArguments( + IdentifierName("InterfaceInformation"), + IdentifierName("InterfaceImplementation"))); + + private static MemberDeclarationSyntax GenerateIUnknownDerivedAttributeApplication(ComInterfaceContext context) + => context.TypeDefinitionContext.WrapMemberInContainingSyntaxWithUnsafeModifier( + TypeDeclaration(context.InterfaceTypeSyntax.TypeKind, context.InterfaceTypeSyntax.Identifier) + .WithModifiers(context.InterfaceTypeSyntax.Modifiers) + .WithTypeParameterList(context.InterfaceTypeSyntax.TypeParameters) + .AddAttributeLists(AttributeList(SingletonSeparatedList(s_iUnknownDerivedAttributeTemplate)))); private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, CancellationToken ct) { @@ -287,6 +435,16 @@ namespace Microsoft.Interop return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers, syntax.Identifier.GetLocation(), type.Name, typeDecl.Identifier); } } + var guidAttr = type.GetAttributes().Where(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_GuidAttribute).SingleOrDefault(); + var interfaceTypeAttr = type.GetAttributes().Where(attr => attr.AttributeClass.ToDisplayString() == TypeNames.InterfaceTypeAttribute).SingleOrDefault(); + // Assume interfaceType is IUnknown for now + if (interfaceTypeAttr is not null + && (guidAttr is null + || guidAttr.ConstructorArguments.SingleOrDefault().Value as string is null)) + { + return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedInterfaceMissingGuidAttribute, syntax.Identifier.GetLocation(), type.ToDisplayString()); + // Missing Guid + } return null; } @@ -311,47 +469,257 @@ namespace Microsoft.Interop return null; } - private static MemberDeclarationSyntax GenerateNativeInterfaceMetadata(ContainingSyntaxContext context) + private static ImmutableArray> GroupContextsForInterfaceGeneration(ImmutableArray contexts) + { + // We can end up with an empty set of contexts here as the compiler will call a SelectMany + // after a Collect with no input entries + if (contexts.IsEmpty) + { + return ImmutableArray>.Empty; + } + + ImmutableArray>.Builder allGroupsBuilder = ImmutableArray.CreateBuilder>(); + + // Due to how the source generator driver processes the input item tables and our limitation that methods on COM interfaces can only be defined in a single partial definition of the type, + // we can guarantee that the method contexts are ordered as follows: + // - I1.M1 + // - I1.M2 + // - I1.M3 + // - I2.M1 + // - I2.M2 + // - I2.M3 + // - I3.M1 + // - etc... + // This enable us to group our contexts by their containing syntax rather simply. + ManagedTypeInfo? lastSeenDefiningType = null; + ImmutableArray.Builder groupBuilder = ImmutableArray.CreateBuilder(); + foreach (var context in contexts) + { + if (lastSeenDefiningType is null || lastSeenDefiningType == context.OriginalDefiningType) + { + groupBuilder.Add(context); + } + else + { + allGroupsBuilder.Add(new(groupBuilder.ToImmutable())); + groupBuilder.Clear(); + groupBuilder.Add(context); + } + lastSeenDefiningType = context.OriginalDefiningType; + } + + allGroupsBuilder.Add(new(groupBuilder.ToImmutable())); + return allGroupsBuilder.ToImmutable(); + } + + private static readonly InterfaceDeclarationSyntax ImplementationInterfaceTemplate = InterfaceDeclaration("InterfaceImplementation") + .WithModifiers(TokenList(Token(SyntaxKind.FileKeyword), Token(SyntaxKind.UnsafeKeyword), Token(SyntaxKind.PartialKeyword))); + private static InterfaceDeclarationSyntax GenerateImplementationInterface(ImmutableArray interfaceGroup) + { + var definingType = interfaceGroup[0].OriginalDefiningType; + return ImplementationInterfaceTemplate + .AddBaseListTypes(SimpleBaseType(definingType.Syntax)) + .WithMembers(List(interfaceGroup.OfType().Select(context => context.Stub.Node))) + .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(ParseName(TypeNames.System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute))))); + } + private static InterfaceDeclarationSyntax GenerateImplementationVTableMethods(ImmutableArray interfaceGroup) { - return context.WrapMemberInContainingSyntaxWithUnsafeModifier( - InterfaceDeclaration("Native") - .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.PartialKeyword))) - .WithBaseList(BaseList(SingletonSeparatedList((BaseTypeSyntax)SimpleBaseType(IdentifierName(context.ContainingSyntax[0].Identifier))))) - .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(ParseName(TypeNames.System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute)))))); + return ImplementationInterfaceTemplate + .WithMembers(List(interfaceGroup.OfType().Select(context => context.Stub.Node))); } - private const string VTableParameterName = "vtable"; - private static readonly MethodDeclarationSyntax ManagedVirtualMethodTableImplementationSyntaxTemplate = - MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), - "PopulateUnmanagedVirtualMethodTable") - .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))) - .AddParameterListParameters( - Parameter(Identifier(VTableParameterName)) - .WithType(GenericName(TypeNames.System_Span).AddTypeArgumentListArguments(IdentifierName("nint")))); + private static readonly TypeSyntax VoidStarStarSyntax = PointerType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword)))); - private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping vtableMethods) + private const string CreateManagedVirtualFunctionTableMethodName = "CreateManagedVirtualFunctionTable"; + + private static readonly MethodDeclarationSyntax CreateManagedVirtualFunctionTableMethodTemplate = MethodDeclaration(VoidStarStarSyntax, CreateManagedVirtualFunctionTableMethodName) + .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword)); + private static InterfaceDeclarationSyntax GenerateImplementationVTable(ImmutableArray interfaceMethodStubs) { - ContainingSyntaxContext containingSyntax = vtableMethods.Key.AddContainingSyntax(NativeTypeContainingSyntax); - MethodDeclarationSyntax populateVtableMethod = ManagedVirtualMethodTableImplementationSyntaxTemplate; + const string vtableLocalName = "vtable"; + var interfaceType = interfaceMethodStubs[0].OriginalDefiningType; + + ImmutableArray vtableExposedContexts = interfaceMethodStubs + .Where(c => c.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional) + .ToImmutableArray(); - foreach (var method in vtableMethods) + // If none of the methods are exposed as part of the vtable, then don't emit + // a vtable (return null). + if (vtableExposedContexts.Length == 0) { - FunctionPointerTypeSyntax functionPointerType = VtableIndexStubGenerator.GenerateUnmanagedFunctionPointerTypeForMethod(method); + return ImplementationInterfaceTemplate + .AddMembers( + CreateManagedVirtualFunctionTableMethodTemplate + .WithBody( + Block( + ReturnStatement(LiteralExpression(SyntaxKind.NullLiteralExpression))))); + } - // [] = (nint)()&ABI_; - populateVtableMethod = populateVtableMethod.AddBodyStatements( + // void** vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(, sizeof(void*) * ); + var vtableDeclarationStatement = + LocalDeclarationStatement( + VariableDeclaration( + VoidStarStarSyntax, + SingletonSeparatedList( + VariableDeclarator(vtableLocalName) + .WithInitializer( + EqualsValueClause( + CastExpression(VoidStarStarSyntax, + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseTypeName(TypeNames.System_Runtime_CompilerServices_RuntimeHelpers), + IdentifierName("AllocateTypeAssociatedMemory"))) + .AddArgumentListArguments( + Argument(TypeOfExpression(interfaceType.Syntax)), + Argument( + BinaryExpression( + SyntaxKind.MultiplyExpression, + SizeOfExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword)))), + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(interfaceMethodStubs.Length))))))))))); + + var fillIUnknownSlots = Block() + .AddStatements( + // nint v0, v1, v2; + LocalDeclarationStatement(VariableDeclaration(ParseTypeName("nint")) + .AddVariables( + VariableDeclarator("v0"), + VariableDeclarator("v1"), + VariableDeclarator("v2") + )), + // ComWrappers.GetIUnknownImpl(out v0, out v1, out v2); ExpressionStatement( - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseTypeName(TypeNames.System_Runtime_InteropServices_ComWrappers), + IdentifierName("GetIUnknownImpl"))) + .AddArgumentListArguments( + Argument(IdentifierName("v0")) + .WithRefKindKeyword(Token(SyntaxKind.OutKeyword)), + Argument(IdentifierName("v1")) + .WithRefKindKeyword(Token(SyntaxKind.OutKeyword)), + Argument(IdentifierName("v2")) + .WithRefKindKeyword(Token(SyntaxKind.OutKeyword)))), + // m_vtable[0] = (void*)v0; + ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, ElementAccessExpression( - IdentifierName(VTableParameterName)) - .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))), - CastExpression(IdentifierName("nint"), - CastExpression(functionPointerType, - PrefixUnaryExpression(SyntaxKind.AddressOfExpression, - IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}"))))))); - } + IdentifierName(vtableLocalName), + BracketedArgumentList( + SingletonSeparatedList( + Argument( + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(0)))))), + CastExpression( + PointerType( + PredefinedType(Token(SyntaxKind.VoidKeyword))), + IdentifierName("v0")))), + // m_vtable[1] = (void*)v1; + ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + ElementAccessExpression( + IdentifierName(vtableLocalName), + BracketedArgumentList( + SingletonSeparatedList( + Argument( + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(1)))))), + CastExpression( + PointerType( + PredefinedType(Token(SyntaxKind.VoidKeyword))), + IdentifierName("v1")))), + // m_vtable[2] = (void*)v2; + ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + ElementAccessExpression( + IdentifierName(vtableLocalName), + BracketedArgumentList( + SingletonSeparatedList( + Argument( + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(2)))))), + CastExpression( + PointerType( + PredefinedType(Token(SyntaxKind.VoidKeyword))), + IdentifierName("v2"))))); + + var vtableSlotAssignments = VirtualMethodPointerStubGenerator.GenerateVirtualMethodTableSlotAssignments(interfaceMethodStubs, vtableLocalName); + + return ImplementationInterfaceTemplate + .AddMembers( + CreateManagedVirtualFunctionTableMethodTemplate + .WithBody( + Block( + vtableDeclarationStatement, + fillIUnknownSlots, + vtableSlotAssignments, + ReturnStatement(IdentifierName(vtableLocalName))))); + } + + private static readonly ClassDeclarationSyntax InterfaceInformationTypeTemplate = + ClassDeclaration("InterfaceInformation") + .AddModifiers(Token(SyntaxKind.FileKeyword), Token(SyntaxKind.UnsafeKeyword)) + .AddBaseListTypes(SimpleBaseType(ParseTypeName(TypeNames.IIUnknownInterfaceType))); + + private static ClassDeclarationSyntax GenerateInterfaceInformation(ComInterfaceContext context) + { + const string vtableFieldName = "_vtable"; + return InterfaceInformationTypeTemplate + .AddMembers( + // public static System.Guid Iid { get; } = new(); + PropertyDeclaration(ParseTypeName(TypeNames.System_Guid), "Iid") + .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) + .AddAccessorListAccessors( + AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(Token(SyntaxKind.SemicolonToken))) + .WithInitializer( + EqualsValueClause( + ImplicitObjectCreationExpression() + .AddArgumentListArguments( + Argument(CreateEmbeddedDataBlobCreationStatement(context.InterfaceId.ToByteArray()))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + // private static void** _vtable; + FieldDeclaration(VariableDeclaration(VoidStarStarSyntax, SingletonSeparatedList(VariableDeclarator(vtableFieldName)))) + .AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword)), + // public static void* VirtualMethodTableManagedImplementation => _vtable != null ? _vtable : (_vtable = InterfaceImplementation.CreateManagedVirtualMethodTable()); + PropertyDeclaration(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))), "VirtualMethodTableManagedImplementation") + .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) + .WithExpressionBody( + ArrowExpressionClause( + ConditionalExpression( + BinaryExpression(SyntaxKind.EqualsExpression, + IdentifierName(vtableFieldName), + LiteralExpression(SyntaxKind.NullLiteralExpression)), + IdentifierName(vtableFieldName), + ParenthesizedExpression( + AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + IdentifierName(vtableFieldName), + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("InterfaceImplementation"), + IdentifierName(CreateManagedVirtualFunctionTableMethodName)))))))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) + ); + + static ExpressionSyntax CreateEmbeddedDataBlobCreationStatement(ReadOnlySpan bytes) + { + var literals = new LiteralExpressionSyntax[bytes.Length]; - return containingSyntax.WrapMemberInContainingSyntaxWithUnsafeModifier(populateVtableMethod); + for (int i = 0; i < bytes.Length; i++) + { + literals[i] = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(bytes[i])); + } + + // new System.ReadOnlySpan(new[] { } ) + return ObjectCreationExpression( + GenericName(TypeNames.System_ReadOnlySpan) + .AddTypeArgumentListArguments(PredefinedType(Token(SyntaxKind.ByteKeyword)))) + .AddArgumentListArguments( + Argument( + ArrayCreationExpression( + ArrayType(PredefinedType(Token(SyntaxKind.ByteKeyword)), SingletonList(ArrayRankSpecifier())), + InitializerExpression( + SyntaxKind.ArrayInitializerExpression, + SeparatedList(literals))))); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs index c8f55e8..777680d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs @@ -21,6 +21,7 @@ namespace Microsoft.Interop public const string TypeNotSupported = Prefix + "1051"; public const string ConfigurationNotSupported = Prefix + "1052"; public const string MethodNotDeclaredInAttributedInterface = Prefix + "1091"; + public const string InvalidGeneratedComInterfaceAttributeUsage = Prefix + "1092"; } private const string Category = "ComInterfaceGenerator"; @@ -175,11 +176,21 @@ namespace Microsoft.Interop isEnabledByDefault: true, description: GetResourceString(nameof(SR.MethodNotDeclaredInAttributedInterfaceDescription))); + public static readonly DiagnosticDescriptor InvalidAttributedInterfaceMissingGuidAttribute = + new DiagnosticDescriptor( + Ids.InvalidGeneratedComInterfaceAttributeUsage, + GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageTitle)), + GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageDescription))); private readonly List _diagnostics = new List(); public IEnumerable Diagnostics => _diagnostics; + /// /// Report diagnostic for invalid configuration for string marshalling. /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs index e437df7..94f094a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs @@ -4,9 +4,12 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; +using System.Collections.Immutable; namespace Microsoft.Interop { + internal abstract record GeneratedMethodContextBase(ManagedTypeInfo OriginalDefiningType, SequenceEqualImmutableArray Diagnostics); + internal sealed record IncrementalMethodStubGenerationContext( SignatureContext SignatureContext, ContainingSyntaxContext ContainingSyntaxContext, @@ -19,5 +22,5 @@ namespace Microsoft.Interop MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> UnmanagedToManagedGeneratorFactory, ManagedTypeInfo TypeKeyOwner, SequenceEqualImmutableArray Diagnostics, - TypeSyntax UnwrapperSyntax); + TypeSyntax UnwrapperSyntax) : GeneratedMethodContextBase(TypeKeyOwner, Diagnostics); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs index f240567..870368e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs @@ -30,5 +30,11 @@ namespace Microsoft.Interop return builder.MoveToImmutable(); }); } + + public static IncrementalValuesProvider SelectNormalized(this IncrementalValuesProvider provider) + where TNode : SyntaxNode + { + return provider.Select((node, ct) => node.NormalizeWhitespace()); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx index 9748657..4838380 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx @@ -1,222 +1,231 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Source-generated P/Invokes will ignore any configuration that is not supported. - - - The '{0}' configuration is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. - - - The specified marshalling configuration is not supported by source-generated P/Invokes. {0}. - - - The specified '{0}' configuration for parameter '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. - - - The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. - - - The specified value '{0}' for '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. - - - Specified configuration is not supported by source-generated P/Invokes. - - - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. - - - The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} - {1} is a message containing additional details about what is not valid - - - 'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'. - - - 'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified. - - - For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type. - - - The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter '{1}'. - - - {0} The generated source will not handle marshalling of parameter '{1}'. - {0} is a message containing additional details about what is not supported -{1} is the name of the parameter - - - The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of the return value of method '{1}'. - - - {0} The generated source will not handle marshalling of the return value of method '{1}'. - {0} is a message containing additional details about what is not supported -{1} is the name of the method - - - Specified type is not supported by source-generated P/Invokes - - - Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'. - - - Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. - - - Method '{0}' should be 'static', 'partial', and non-generic when marked with 'LibraryImportAttribute'. P/Invoke source generation will ignore method '{0}'. - - - Invalid 'VirtualMethodIndexAttribute' usage - - - The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' is invalid. - - - The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' on method '{0}' is invalid. {1} - {1} is a message containing additional details about what is not valid - - - 'ExceptionMarshallingCustomType' must be specified when 'ExceptionMarshalling' is set to 'ExceptionMarshalling.Custom'. - - - 'ExceptionMarshalling' should be set to 'ExceptionMarshalling.Custom' when 'ExceptionMarshallingCustomType' is specified. - - - The provided value is not a known flag of the 'ExceptionMarshalling' enum. - - - 'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type. - - - Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'. - - - Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. - - - All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets. - - - The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute - - - Method is declared in different partial declaration than the 'GeneratedComInterface' attribute. - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Source-generated P/Invokes will ignore any configuration that is not supported. + + + The '{0}' configuration is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. + + + The specified marshalling configuration is not supported by source-generated P/Invokes. {0}. + + + The specified '{0}' configuration for parameter '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. + + + The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. + + + The specified value '{0}' for '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead. + + + Specified configuration is not supported by source-generated P/Invokes. + + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. + + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1} + {1} is a message containing additional details about what is not valid + + + 'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'. + + + 'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified. + + + For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type. + + + The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter '{1}'. + + + {0} The generated source will not handle marshalling of parameter '{1}'. + {0} is a message containing additional details about what is not supported +{1} is the name of the parameter + + + The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of the return value of method '{1}'. + + + {0} The generated source will not handle marshalling of the return value of method '{1}'. + {0} is a message containing additional details about what is not supported +{1} is the name of the method + + + Specified type is not supported by source-generated P/Invokes + + + Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'. + + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. + + + Method '{0}' should be 'static', 'partial', and non-generic when marked with 'LibraryImportAttribute'. P/Invoke source generation will ignore method '{0}'. + + + Invalid 'VirtualMethodIndexAttribute' usage + + + The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' is invalid. + + + The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' on method '{0}' is invalid. {1} + {1} is a message containing additional details about what is not valid + + + 'ExceptionMarshallingCustomType' must be specified when 'ExceptionMarshalling' is set to 'ExceptionMarshalling.Custom'. + + + 'ExceptionMarshalling' should be set to 'ExceptionMarshalling.Custom' when 'ExceptionMarshallingCustomType' is specified. + + + The provided value is not a known flag of the 'ExceptionMarshalling' enum. + + + 'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type. + + + Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'. + + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + + All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets. + + + The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute + + + Method is declared in different partial declaration than the 'GeneratedComInterface' attribute. + + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + Invalid 'GeneratedComInterfaceAttribute' usage. + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf index 2500fbf..e09326f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf @@ -92,6 +92,21 @@ Zadaná hodnota není známý příznak výčtu ExceptionMarsnuming. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. Konfigurace StringMarshalling a StringMarshallingCustomType je neplatná. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf index a18b8e2..2d3cb6f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf @@ -92,6 +92,21 @@ Der angegebene Wert ist kein bekanntes Flag der „ExceptionMarshalling“-Enumeration. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. Die Konfiguration von \"StringMarshalling\" und \"StringMarshallingCustomType\" ist ungültig. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf index 0f24506..8c3e75e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf @@ -92,6 +92,21 @@ El valor proporcionado no es una marca conocida de la enumeración “ExceptionMarshalling”. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. La configuración de “StringMarshalling” y “StringMarshallingCustomType” no es válida. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf index c732bf0..b784a2c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf @@ -92,6 +92,21 @@ La valeur fournie n’est pas un indicateur connu de l’énumération « ExceptionMarshalling ». + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. La configuration de « StringMarshalling » et de « StringMarshallingCustomType » n’est pas valide. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf index b8204ea..2a05223 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf @@ -92,6 +92,21 @@ Il valore specificato non è un flag noto dell'enumerazione 'ExceptionMarshalling'. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. La configurazione di 'StringMarshalling' e 'StringMarshallingCustomType' non è valida. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf index 57e2961..410641b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf @@ -92,6 +92,21 @@ 指定された値は、'ExceptionMarshalling' 列挙型の既知のフラグではありません。 + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. 'StringMarshalling' と 'StringMarshallingCustomType' の構成が無効です。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf index 9d14554..cc21fc0 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf @@ -92,6 +92,21 @@ 제공된 값은 'ExceptionMarshalling' 열거형의 알려진 플래그가 아닙니다. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. 'StringMarshalling' 및 'StringMarshallingCustomType'의 구성이 잘못되었습니다. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf index f11c3fe..01369f4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf @@ -92,6 +92,21 @@ Podana wartość nie jest znaną flagą wyliczenia „'ExceptionMarshalling”. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. Konfiguracja elementów „StringMarshalling” i „StringMarshallingCustomType” jest nieprawidłowa. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf index 7740470..6863fce 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf @@ -92,6 +92,21 @@ O valor fornecido não é um sinalizador conhecido da enumeração 'ExceptionMarshalling'. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. A configuração de 'StringMarshalling' e 'StringMarshallingCustomType' é inválida. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf index 8ca45f5..0cca0a0 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf @@ -92,6 +92,21 @@ Указанное значение не является известным флагом перечисления ExceptionMarshalling. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. Конфигурация \"StringMarshalling\" и \"StringMarshallingCustomType\" недопустима. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf index 7483677..8516282 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf @@ -92,6 +92,21 @@ Sağlanan değer bilinen bir 'ExceptionMarshalling' sabit listesi bayrağı değil. + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. 'StringMarshalling' ve 'StringMarshallingCustomType' yapılandırması geçersiz. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf index 48f7ef0..9d7ba65 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf @@ -92,6 +92,21 @@ 提供的值不是 “ExceptionMarshalling” 枚举的已知标志。 + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. “StringMarshalling” 和 “StringMarshallingCustomType” 的配置无效。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf index 9e6a835..8581994 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf @@ -92,6 +92,21 @@ 提供的值不是 'ExceptionMarshalling' 列舉的已知旗標。 + + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'. + + + + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'. + + + + Invalid 'GeneratedComInterfaceAttribute' usage. + Invalid 'GeneratedComInterfaceAttribute' usage. + + The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid. 'StringMarshalling' 和 'StringMarshallingCustomType' 的設定無效。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VirtualMethodPointerStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VirtualMethodPointerStubGenerator.cs new file mode 100644 index 0000000..c878616 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VirtualMethodPointerStubGenerator.cs @@ -0,0 +1,200 @@ +// 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.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using Microsoft.CodeAnalysis; +using System.Diagnostics; + +namespace Microsoft.Interop +{ + internal static class VirtualMethodPointerStubGenerator + { + public static (MethodDeclarationSyntax, ImmutableArray) GenerateManagedToNativeStub( + IncrementalMethodStubGenerationContext methodStub) + { + var diagnostics = new GeneratorDiagnostics(); + + // Generate stub code + var stubGenerator = new ManagedToNativeVTableMethodGenerator( + methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFramework, + methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFrameworkVersion, + methodStub.SignatureContext.ElementTypeInformation, + methodStub.VtableIndexData.SetLastError, + methodStub.VtableIndexData.ImplicitThisParameter, + (elementInfo, ex) => + { + diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails); + }, + methodStub.ManagedToUnmanagedGeneratorFactory.GeneratorFactory); + + BlockSyntax code = stubGenerator.GenerateStubBody( + methodStub.VtableIndexData.Index, + methodStub.CallingConvention.Array, + methodStub.TypeKeyOwner.Syntax); + + // The owner type will always be an interface type, so the syntax will always be a NameSyntax as it's the name of a named type + // with no additional decorators. + Debug.Assert(methodStub.TypeKeyOwner.Syntax is NameSyntax); + + return ( + PrintGeneratedSource( + methodStub.StubMethodSyntaxTemplate, + methodStub.SignatureContext, + code) + .WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifier((NameSyntax)methodStub.TypeKeyOwner.Syntax)), + methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics)); + } + private static MethodDeclarationSyntax PrintGeneratedSource( + ContainingSyntax stubMethodSyntax, + SignatureContext stub, + BlockSyntax stubCode) + { + // Create stub function + return MethodDeclaration(stub.StubReturnType, stubMethodSyntax.Identifier) + .AddAttributeLists(stub.AdditionalAttributes.ToArray()) + .WithModifiers(stubMethodSyntax.Modifiers.StripTriviaFromTokens()) + .WithParameterList(ParameterList(SeparatedList(stub.StubParameters))) + .WithBody(stubCode); + } + + private const string ThisParameterIdentifier = "@this"; + + public static (MethodDeclarationSyntax, ImmutableArray) GenerateNativeToManagedStub( + IncrementalMethodStubGenerationContext methodStub) + { + var diagnostics = new GeneratorDiagnostics(); + ImmutableArray elements = AddImplicitElementInfos(methodStub); + + // Generate stub code + var stubGenerator = new UnmanagedToManagedStubGenerator( + methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFramework, + methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion, + elements, + (elementInfo, ex) => + { + diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails); + }, + methodStub.UnmanagedToManagedGeneratorFactory.GeneratorFactory); + + BlockSyntax code = stubGenerator.GenerateStubBody( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(ThisParameterIdentifier), + IdentifierName(methodStub.StubMethodSyntaxTemplate.Identifier))); + + (ParameterListSyntax unmanagedParameterList, TypeSyntax returnType, _) = stubGenerator.GenerateAbiMethodSignatureData(); + + AttributeSyntax unmanagedCallersOnlyAttribute = Attribute( + ParseName(TypeNames.UnmanagedCallersOnlyAttribute)); + + if (methodStub.CallingConvention.Array.Length != 0) + { + unmanagedCallersOnlyAttribute = unmanagedCallersOnlyAttribute.AddArgumentListArguments( + AttributeArgument( + ImplicitArrayCreationExpression( + InitializerExpression(SyntaxKind.CollectionInitializerExpression, + SeparatedList( + methodStub.CallingConvention.Array.Select(callConv => TypeOfExpression(ParseName($"System.Runtime.CompilerServices.CallConv{callConv.Name.ValueText}"))))))) + .WithNameEquals(NameEquals(IdentifierName("CallConvs")))); + } + + MethodDeclarationSyntax unmanagedToManagedStub = + MethodDeclaration(returnType, $"ABI_{methodStub.StubMethodSyntaxTemplate.Identifier.Text}") + .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))) + .WithParameterList(unmanagedParameterList) + .AddAttributeLists(AttributeList(SingletonSeparatedList(unmanagedCallersOnlyAttribute))) + .WithBody(code); + + return ( + unmanagedToManagedStub, + methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics)); + } + private static ImmutableArray AddImplicitElementInfos(IncrementalMethodStubGenerationContext methodStub) + { + ImmutableArray originalElements = methodStub.SignatureContext.ElementTypeInformation; + + var elements = ImmutableArray.CreateBuilder(originalElements.Length + 2); + + elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, new NativeThisInfo(methodStub.UnwrapperSyntax)) + { + InstanceIdentifier = ThisParameterIdentifier, + NativeIndex = 0, + }); + foreach (TypePositionInfo element in originalElements) + { + elements.Add(element with + { + NativeIndex = TypePositionInfo.IncrementIndex(element.NativeIndex) + }); + } + + if (methodStub.ExceptionMarshallingInfo != NoMarshallingInfo.Instance) + { + elements.Add( + new TypePositionInfo( + new ReferenceTypeInfo($"global::{TypeNames.System_Exception}", TypeNames.System_Exception), + methodStub.ExceptionMarshallingInfo) + { + InstanceIdentifier = "__exception", + ManagedIndex = TypePositionInfo.ExceptionIndex, + NativeIndex = TypePositionInfo.ReturnIndex + }); + } + + return elements.ToImmutable(); + } + + public static BlockSyntax GenerateVirtualMethodTableSlotAssignments(IEnumerable vtableMethods, string vtableIdentifier) + { + List statements = new(); + foreach (var method in vtableMethods) + { + FunctionPointerTypeSyntax functionPointerType = GenerateUnmanagedFunctionPointerTypeForMethod(method); + + // [] = (void*)()&ABI_; + statements.Add( + ExpressionStatement( + AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + ElementAccessExpression( + IdentifierName(vtableIdentifier)) + .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))), + CastExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))), + CastExpression(functionPointerType, + PrefixUnaryExpression(SyntaxKind.AddressOfExpression, + IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}"))))))); + } + + return Block(statements); + } + + private static FunctionPointerTypeSyntax GenerateUnmanagedFunctionPointerTypeForMethod(IncrementalMethodStubGenerationContext method) + { + var stubGenerator = new UnmanagedToManagedStubGenerator( + method.UnmanagedToManagedGeneratorFactory.Key.TargetFramework, + method.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion, + AddImplicitElementInfos(method), + // Swallow diagnostics here since the diagnostics will be reported by the unmanaged->managed stub generation + (elementInfo, ex) => { }, + method.UnmanagedToManagedGeneratorFactory.GeneratorFactory); + + List functionPointerParameters = new(); + var (paramList, retType, _) = stubGenerator.GenerateAbiMethodSignatureData(); + functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type))); + // We add the return type as the last "parameter" here as that's what the function pointer syntax requires. + functionPointerParameters.Add(FunctionPointerParameter(retType)); + + // delegate* unmanaged<...> + ImmutableArray callConv = method.CallingConvention.Array; + FunctionPointerTypeSyntax functionPointerType = FunctionPointerType( + FunctionPointerCallingConvention(Token(SyntaxKind.UnmanagedKeyword), callConv.IsEmpty ? null : FunctionPointerUnmanagedCallingConventionList(SeparatedList(callConv))), + FunctionPointerParameterList(SeparatedList(functionPointerParameters))); + return functionPointerType; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs index 9ff0512..dda8473 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs @@ -155,30 +155,6 @@ namespace Microsoft.Interop } return callingConventions.ToImmutable(); } - - private static SyntaxTokenList StripTriviaFromModifiers(SyntaxTokenList tokenList) - { - SyntaxToken[] strippedTokens = new SyntaxToken[tokenList.Count]; - for (int i = 0; i < tokenList.Count; i++) - { - strippedTokens[i] = tokenList[i].WithoutTrivia(); - } - return new SyntaxTokenList(strippedTokens); - } - - private static MethodDeclarationSyntax PrintGeneratedSource( - ContainingSyntax stubMethodSyntax, - SignatureContext stub, - BlockSyntax stubCode) - { - // Create stub function - return MethodDeclaration(stub.StubReturnType, stubMethodSyntax.Identifier) - .AddAttributeLists(stub.AdditionalAttributes.ToArray()) - .WithModifiers(StripTriviaFromModifiers(stubMethodSyntax.Modifiers)) - .WithParameterList(ParameterList(SeparatedList(stub.StubParameters))) - .WithBody(stubCode); - } - private static VirtualMethodIndexCompilationData? ProcessVirtualMethodIndexAttribute(AttributeData attrData) { // Found the attribute, but it has an error so report the error. @@ -409,129 +385,30 @@ namespace Microsoft.Interop return NoMarshallingInfo.Instance; } - internal static (MemberDeclarationSyntax, ImmutableArray) GenerateManagedToNativeStub( + private static (MemberDeclarationSyntax, ImmutableArray) GenerateManagedToNativeStub( IncrementalMethodStubGenerationContext methodStub) { - var diagnostics = new GeneratorDiagnostics(); - - // Generate stub code - var stubGenerator = new ManagedToNativeVTableMethodGenerator( - methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFramework, - methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFrameworkVersion, - methodStub.SignatureContext.ElementTypeInformation, - methodStub.VtableIndexData.SetLastError, - methodStub.VtableIndexData.ImplicitThisParameter, - (elementInfo, ex) => - { - diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails); - }, - methodStub.ManagedToUnmanagedGeneratorFactory.GeneratorFactory); - - BlockSyntax code = stubGenerator.GenerateStubBody( - methodStub.VtableIndexData.Index, - methodStub.CallingConvention.Array, - methodStub.TypeKeyOwner.Syntax); + var (stub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateManagedToNativeStub(methodStub); return ( methodStub.ContainingSyntaxContext.AddContainingSyntax( NativeTypeContainingSyntax) .WrapMemberInContainingSyntaxWithUnsafeModifier( - PrintGeneratedSource( - methodStub.StubMethodSyntaxTemplate, - methodStub.SignatureContext, - code) - .WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifier(IdentifierName(methodStub.ContainingSyntaxContext.ContainingSyntax[0].Identifier)))), - methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics)); + stub), + methodStub.Diagnostics.Array.AddRange(diagnostics)); } - private const string ThisParameterIdentifier = "@this"; - - internal static (MemberDeclarationSyntax, ImmutableArray) GenerateNativeToManagedStub( + private static (MemberDeclarationSyntax, ImmutableArray) GenerateNativeToManagedStub( IncrementalMethodStubGenerationContext methodStub) { - var diagnostics = new GeneratorDiagnostics(); - ImmutableArray elements = AddImplicitElementInfos(methodStub); - - // Generate stub code - var stubGenerator = new UnmanagedToManagedStubGenerator( - methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFramework, - methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion, - elements, - (elementInfo, ex) => - { - diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails); - }, - methodStub.UnmanagedToManagedGeneratorFactory.GeneratorFactory); - - BlockSyntax code = stubGenerator.GenerateStubBody( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(ThisParameterIdentifier), - IdentifierName(methodStub.StubMethodSyntaxTemplate.Identifier))); - - (ParameterListSyntax unmanagedParameterList, TypeSyntax returnType, _) = stubGenerator.GenerateAbiMethodSignatureData(); - - AttributeSyntax unmanagedCallersOnlyAttribute = Attribute( - ParseName(TypeNames.UnmanagedCallersOnlyAttribute)); - - if (methodStub.CallingConvention.Array.Length != 0) - { - unmanagedCallersOnlyAttribute = unmanagedCallersOnlyAttribute.AddArgumentListArguments( - AttributeArgument( - ImplicitArrayCreationExpression( - InitializerExpression(SyntaxKind.CollectionInitializerExpression, - SeparatedList( - methodStub.CallingConvention.Array.Select(callConv => TypeOfExpression(ParseName($"System.Runtime.CompilerServices.CallConv{callConv.Name.ValueText}"))))))) - .WithNameEquals(NameEquals(IdentifierName("CallConvs")))); - } - - MethodDeclarationSyntax unmanagedToManagedStub = - MethodDeclaration(returnType, $"ABI_{methodStub.StubMethodSyntaxTemplate.Identifier.Text}") - .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))) - .WithParameterList(unmanagedParameterList) - .AddAttributeLists(AttributeList(SingletonSeparatedList(unmanagedCallersOnlyAttribute))) - .WithBody(code); + var (stub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateNativeToManagedStub(methodStub); return ( methodStub.ContainingSyntaxContext.AddContainingSyntax( NativeTypeContainingSyntax) .WrapMemberInContainingSyntaxWithUnsafeModifier( - unmanagedToManagedStub), - methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics)); - } - - private static ImmutableArray AddImplicitElementInfos(IncrementalMethodStubGenerationContext methodStub) - { - ImmutableArray originalElements = methodStub.SignatureContext.ElementTypeInformation; - - var elements = ImmutableArray.CreateBuilder(originalElements.Length + 2); - - elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, new NativeThisInfo(methodStub.UnwrapperSyntax)) - { - InstanceIdentifier = ThisParameterIdentifier, - NativeIndex = 0, - }); - foreach (TypePositionInfo element in originalElements) - { - elements.Add(element with - { - NativeIndex = TypePositionInfo.IncrementIndex(element.NativeIndex) - }); - } - - if (methodStub.ExceptionMarshallingInfo != NoMarshallingInfo.Instance) - { - elements.Add( - new TypePositionInfo( - new ReferenceTypeInfo($"global::{TypeNames.System_Exception}", TypeNames.System_Exception), - methodStub.ExceptionMarshallingInfo) - { - InstanceIdentifier = "__exception", - ManagedIndex = TypePositionInfo.ExceptionIndex, - NativeIndex = TypePositionInfo.ReturnIndex - }); - } - - return elements.ToImmutable(); + stub), + methodStub.Diagnostics.Array.AddRange(diagnostics)); } private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method) @@ -584,57 +461,18 @@ namespace Microsoft.Interop private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping vtableMethods) { - const string vtableParameter = "vtable"; ContainingSyntaxContext containingSyntax = vtableMethods.Key.AddContainingSyntax(NativeTypeContainingSyntax); + + const string vtableParameter = "vtable"; MethodDeclarationSyntax populateVtableMethod = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), "PopulateUnmanagedVirtualMethodTable") - .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword))) + .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.UnsafeKeyword))) .AddParameterListParameters( Parameter(Identifier(vtableParameter)) .WithType(PointerType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword)))))); - foreach (var method in vtableMethods) - { - FunctionPointerTypeSyntax functionPointerType = GenerateUnmanagedFunctionPointerTypeForMethod(method); - - // [] = (void*)()&ABI_; - populateVtableMethod = populateVtableMethod.AddBodyStatements( - ExpressionStatement( - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - ElementAccessExpression( - IdentifierName(vtableParameter)) - .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))), - CastExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))), - CastExpression(functionPointerType, - PrefixUnaryExpression(SyntaxKind.AddressOfExpression, - IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}"))))))); - } - - return containingSyntax.WrapMemberInContainingSyntaxWithUnsafeModifier(populateVtableMethod); - } - - internal static FunctionPointerTypeSyntax GenerateUnmanagedFunctionPointerTypeForMethod(IncrementalMethodStubGenerationContext method) - { - var stubGenerator = new UnmanagedToManagedStubGenerator( - method.UnmanagedToManagedGeneratorFactory.Key.TargetFramework, - method.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion, - AddImplicitElementInfos(method), - // Swallow diagnostics here since the diagnostics will be reported by the unmanaged->managed stub generation - (elementInfo, ex) => { }, - method.UnmanagedToManagedGeneratorFactory.GeneratorFactory); - - List functionPointerParameters = new(); - var (paramList, retType, _) = stubGenerator.GenerateAbiMethodSignatureData(); - functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type))); - // We add the return type as the last "parameter" here as that's what the function pointer syntax requires. - functionPointerParameters.Add(FunctionPointerParameter(retType)); - - // delegate* unmanaged<...> - ImmutableArray callConv = method.CallingConvention.Array; - FunctionPointerTypeSyntax functionPointerType = FunctionPointerType( - FunctionPointerCallingConvention(Token(SyntaxKind.UnmanagedKeyword), callConv.IsEmpty ? null : FunctionPointerUnmanagedCallingConventionList(SeparatedList(callConv))), - FunctionPointerParameterList(SeparatedList(functionPointerParameters))); - return functionPointerType; + return containingSyntax.WrapMembersInContainingSyntaxWithUnsafeModifier( + populateVtableMethod.WithBody(VirtualMethodPointerStubGenerator.GenerateVirtualMethodTableSlotAssignments(vtableMethods, vtableParameter))); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentNode.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentNode.cs new file mode 100644 index 0000000..9a9de4d --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentNode.cs @@ -0,0 +1,21 @@ +// 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 +{ + public record struct SyntaxEquivalentNode(T Node) + where T : SyntaxNode + { + public bool Equals(SyntaxEquivalentNode other) + { + return SyntaxEquivalentComparer.Instance.Equals(Node, other.Node); + } + + public override int GetHashCode() => throw new UnreachableException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs index 0a5b8ac..c74ca0b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs @@ -101,10 +101,25 @@ namespace Microsoft.Interop public const string System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute = "System.Runtime.InteropServices.DynamicInterfaceCastableImplementationAttribute"; + public const string System_Guid = "System.Guid"; + + public const string System_Runtime_CompilerServices_RuntimeHelpers = "System.Runtime.CompilerServices.RuntimeHelpers"; + public const string GeneratedComInterfaceAttribute = "System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute"; + public const string InterfaceTypeAttribute = "System.Runtime.InteropServices.InterfaceTypeAttribute"; + public const string ComInterfaceTypeAttribute = "System.Runtime.InteropServices.ComInterfaceType"; + + public const string System_Runtime_InteropServices_GuidAttribute = "System.Runtime.InteropServices.GuidAttribute"; + + public const string System_Runtime_InteropServices_ComWrappers = "System.Runtime.InteropServices.ComWrappers"; + public const string System_Runtime_InteropServices_ComWrappers_ComInterfaceDispatch = "System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch"; + + public const string IIUnknownInterfaceType = "System.Runtime.InteropServices.Marshalling.IIUnknownInterfaceType"; + public const string IUnknownDerivedAttribute = "System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute"; + public const string ComWrappersUnwrapper = "System.Runtime.InteropServices.Marshalling.ComWrappersUnwrapper"; public const string UnmanagedObjectUnwrapperAttribute = "System.Runtime.InteropServices.Marshalling.UnmanagedObjectUnwrapperAttribute`1"; diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs index 9addfc0..5c45200 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs @@ -9,7 +9,6 @@ namespace System.Runtime.InteropServices.Marshalling [AttributeUsage(AttributeTargets.Interface)] public class IUnknownDerivedAttribute : Attribute, IUnknownDerivedDetails where T : IIUnknownInterfaceType - where TImpl : T { public IUnknownDerivedAttribute() { diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs index 59829ca..a5e4481 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs @@ -204,7 +204,7 @@ namespace ComInterfaceGenerator.Unit.Tests INamedTypeSymbol generatedInterfaceImplementation = Assert.Single(userDefinedInterface.GetTypeMembers("Native")); - IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"{userDefinedInterfaceName}.{methodName}").OfType()); + IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"global::{userDefinedInterfaceName}.{methodName}").OfType()); SyntaxNode emittedImplementationSyntax = await methodImplementation.DeclaringSyntaxReferences[0].GetSyntaxAsync(); 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 db03aaa..0553592 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 @@ -50,13 +50,6 @@ namespace ComInterfaceGenerator.Unit.Tests public static readonly string DisableRuntimeMarshalling = "[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]"; public static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;"; - public static string NativeInterfaceUsage() => @" -// Try using the generated native interface -sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native -{ - public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(System.Type type) => throw null; -} -"; public string SpecifiedMethodIndexNoExplicitParameters => $@" using System.Runtime.InteropServices; @@ -68,7 +61,7 @@ partial interface INativeAPI {{ {VirtualMethodIndex(0)} void Method(); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string SpecifiedMethodIndexNoExplicitParametersNoImplicitThis => $@" using System.Runtime.InteropServices; @@ -81,7 +74,7 @@ partial interface INativeAPI {VirtualMethodIndex(0, ImplicitThisParameter: false)} void Method(); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string SpecifiedMethodIndexNoExplicitParametersCallConvWithCallingConventions => $@" using System.Runtime.CompilerServices; @@ -113,7 +106,7 @@ partial interface INativeAPI [SuppressGCTransition] {VirtualMethodIndex(4)} void Method4(); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string BasicParametersAndModifiers(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -129,7 +122,7 @@ partial interface INativeAPI {{ {VirtualMethodIndex(0)} {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string BasicParametersAndModifiersManagedToUnmanaged(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -145,7 +138,7 @@ partial interface INativeAPI {{ {VirtualMethodIndex(0, Direction: MarshalDirection.ManagedToUnmanaged)} {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string BasicParametersAndModifiers() => BasicParametersAndModifiers(typeof(T).FullName!); public string BasicParametersAndModifiersNoRef(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -161,7 +154,7 @@ partial interface INativeAPI {{ {VirtualMethodIndex(0)} {typeName} Method({typeName} value, in {typeName} inValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string BasicParametersAndModifiersNoImplicitThis(string typeName) => $@" using System.Runtime.CompilerServices; @@ -174,7 +167,7 @@ partial interface INativeAPI {{ {VirtualMethodIndex(0, ImplicitThisParameter: false)} {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string BasicParametersAndModifiersNoImplicitThis() => BasicParametersAndModifiersNoImplicitThis(typeof(T).FullName!); public string MarshalUsingCollectionCountInfoParametersAndModifiers() => MarshalUsingCollectionCountInfoParametersAndModifiers(typeof(T).ToString()); @@ -197,7 +190,7 @@ partial interface INativeAPI [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef, [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut, out int pOutSize); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string BasicReturnTypeComExceptionHandling(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -211,7 +204,7 @@ partial interface INativeAPI {{ {VirtualMethodIndex(0, ExceptionMarshalling : ExceptionMarshalling.Com)} {typeName} Method(); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public string BasicReturnTypeCustomExceptionHandling(string typeName, string customExceptionType, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -225,7 +218,7 @@ partial interface INativeAPI {{ {VirtualMethodIndex(0, ExceptionMarshallingType : Type.GetType(customExceptionType))} {typeName} Method(); -}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); public class ManagedToUnmanaged : IVirtualMethodIndexSignatureProvider { @@ -233,8 +226,6 @@ partial interface INativeAPI public bool ImplicitThisParameter => true; - public string NativeInterfaceUsage() => CodeSnippets.NativeInterfaceUsage(); - public ManagedToUnmanaged(IComInterfaceAttributeProvider attributeProvider) { AttributeProvider = attributeProvider; @@ -248,8 +239,6 @@ partial interface INativeAPI public bool ImplicitThisParameter => false; - public string NativeInterfaceUsage() => CodeSnippets.NativeInterfaceUsage(); - public ManagedToUnmanagedNoImplicitThis(IComInterfaceAttributeProvider attributeProvider) { AttributeProvider = attributeProvider; @@ -263,10 +252,6 @@ partial interface INativeAPI public bool ImplicitThisParameter => true; - // Unmanaged-to-managed-only stubs don't provide implementations for the interface, so we don't want to try to use the generated nested interface - // since it won't have managed implementations for the methods - public string NativeInterfaceUsage() => string.Empty; - public UnmanagedToManaged(IComInterfaceAttributeProvider attributeProvider) { AttributeProvider = attributeProvider; @@ -280,8 +265,6 @@ partial interface INativeAPI public bool ImplicitThisParameter => true; - public string NativeInterfaceUsage() => CodeSnippets.NativeInterfaceUsage(); - public Bidirectional(IComInterfaceAttributeProvider attributeProvider) { AttributeProvider = attributeProvider; diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs new file mode 100644 index 0000000..c2ea6ee --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs @@ -0,0 +1,145 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Interop.UnitTests; +using Xunit; + +namespace ComInterfaceGenerator.Unit.Tests +{ + public class ComInterfaceGeneratorOutputShape + { + [Fact] + public async Task SingleComInterface() + { + string source = """ + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + partial interface INativeAPI + { + void Method(); + void Method2(); + } + """; + Compilation comp = await TestUtils.CreateCompilation(source); + TestUtils.AssertPreSourceGeneratorCompilation(comp); + + var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator()); + TestUtils.AssertPostSourceGeneratorCompilation(newComp); + // We'll create one syntax tree for the new interface. + Assert.Equal(comp.SyntaxTrees.Count() + 1, newComp.SyntaxTrees.Count()); + + VerifyShape(newComp, "INativeAPI"); + } + + [Fact] + public async Task MultipleComInterfaces() + { + string source = $$""" + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + partial interface I + { + void Method(); + void Method2(); + } + [GeneratedComInterface] + partial interface J + { + void Method(); + void Method2(); + } + """; + Compilation comp = await TestUtils.CreateCompilation(source); + TestUtils.AssertPreSourceGeneratorCompilation(comp); + + var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator()); + TestUtils.AssertPostSourceGeneratorCompilation(newComp); + // We'll create one syntax tree per user-defined interface. + Assert.Equal(comp.SyntaxTrees.Count() + 2, newComp.SyntaxTrees.Count()); + + VerifyShape(newComp, "I"); + VerifyShape(newComp, "J"); + } + + [Fact] + public async Task EmptyComInterface() + { + string source = $$""" + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + partial interface I + { + void Method(); + void Method2(); + } + [GeneratedComInterface] + partial interface Empty + { + } + [GeneratedComInterface] + partial interface J + { + void Method(); + void Method2(); + } + """; + Compilation comp = await TestUtils.CreateCompilation(source); + TestUtils.AssertPreSourceGeneratorCompilation(comp); + + var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator()); + TestUtils.AssertPostSourceGeneratorCompilation(newComp); + // We'll create one syntax tree per user-defined interface. + Assert.Equal(comp.SyntaxTrees.Count() + 3, newComp.SyntaxTrees.Count()); + + VerifyShape(newComp, "I"); + VerifyShape(newComp, "J"); + } + + private static void VerifyShape(Compilation comp, string userDefinedInterfaceMetadataName) + { + INamedTypeSymbol? userDefinedInterface = comp.Assembly.GetTypeByMetadataName(userDefinedInterfaceMetadataName); + Assert.NotNull(userDefinedInterface); + + INamedTypeSymbol? iUnknownDerivedAttributeType = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute`2"); + + Assert.NotNull(iUnknownDerivedAttributeType); + + AttributeData iUnknownDerivedAttribute = Assert.Single( + userDefinedInterface.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, iUnknownDerivedAttributeType)); + + Assert.Collection(Assert.IsAssignableFrom(iUnknownDerivedAttribute.AttributeClass).TypeArguments, + infoType => + { + Assert.True(Assert.IsAssignableFrom(infoType).IsFileLocal); + }, + implementationType => + { + Assert.True(Assert.IsAssignableFrom(implementationType).IsFileLocal); + Assert.Contains(userDefinedInterface, implementationType.Interfaces, SymbolEqualityComparer.Default); + Assert.Contains(implementationType.GetAttributes(), attr => attr.AttributeClass?.ToDisplayString() == typeof(DynamicInterfaceCastableImplementationAttribute).FullName); + Assert.All(userDefinedInterface.GetMembers().OfType().Where(method => method.IsAbstract && !method.IsStatic), + method => + { + Assert.NotNull(implementationType.FindImplementationForInterfaceMember(method)); + }); + }); + } + } +} 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 cae2083..1f4e361 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 @@ -337,8 +337,6 @@ namespace ComInterfaceGenerator.Unit.Tests { // Duplicate 'using' "CS0105", - // ComWrappersUnwrapper is inaccessible due to its protection level - "CS0122", // Variable assigned to but never read "CS0219" }; diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAttributeProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAttributeProvider.cs index d23ec74..83bac9f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAttributeProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAttributeProvider.cs @@ -22,8 +22,8 @@ namespace ComInterfaceGenerator.Unit.Tests public string UnmanagedObjectUnwrapper(Type t) => ""; - public string GeneratedComInterface => "[global::System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute]"; + public string GeneratedComInterface => @$"[global::System.Runtime.InteropServices.Marshalling.GeneratedComInterface, global::System.Runtime.InteropServices.Guid(""0A52B77C-E08B-4274-A1F4-1A2BF2C07E60"")]"; public string AdditionalUserRequiredInterfaces(string userDefinedInterfaceName) => ""; } -} \ No newline at end of file +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs index 451d513..97b73a1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs @@ -21,8 +21,6 @@ namespace ComInterfaceGenerator.Unit.Tests public static readonly string DisableRuntimeMarshalling = "[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]"; public static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;"; - string NativeInterfaceUsage(); - string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifiers(string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -37,7 +35,7 @@ partial interface INativeAPI {{ {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)} {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifiersNoRef(string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -52,7 +50,7 @@ partial interface INativeAPI {{ {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)} {typeName} Method({typeName} value, in {typeName} inValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.BasicParameterByValue(string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; @@ -66,7 +64,7 @@ partial interface INativeAPI {{ {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)} void Method({typeName} value); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.BasicParameterWithByRefModifier(string modifier, string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; @@ -82,7 +80,7 @@ partial interface INativeAPI {{ {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)} void Method({modifier} {typeName} value); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.BasicReturnType(string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -95,7 +93,7 @@ partial interface INativeAPI {{ {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)} {typeName} Method(); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.MarshalUsingParametersAndModifiers(string typeName, string marshallerTypeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -113,7 +111,7 @@ partial interface INativeAPI [MarshalUsing(typeof({marshallerTypeName}))] in {typeName} pIn, [MarshalUsing(typeof({marshallerTypeName}))] ref {typeName} pRef, [MarshalUsing(typeof({marshallerTypeName}))] out {typeName} pOut); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionCountInfoParametersAndModifiers(string collectionType) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -133,7 +131,7 @@ partial interface INativeAPI [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef, [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut, out int pOutSize); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionParametersAndModifiers(string collectionType, string marshallerType) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -155,7 +153,7 @@ partial interface INativeAPI [MarshalUsing(typeof({marshallerType}), CountElementName = ""pOutSize"")] out {collectionType} pOut, out int pOutSize ); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionReturnValueLength(string collectionType, string marshallerType) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -171,7 +169,7 @@ partial interface INativeAPI int Method( [MarshalUsing(typeof({marshallerType}), CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out {collectionType} pOut ); -}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionOutConstantLength(string collectionType, string predeclaration) => $@" using System.Runtime.CompilerServices; @@ -190,7 +188,7 @@ partial interface INativeAPI [MarshalUsing(ConstantElementCount = 10)] out {collectionType} pOut ); }} -" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionReturnConstantLength(string collectionType, string predeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -207,7 +205,7 @@ partial interface INativeAPI [return:MarshalUsing(ConstantElementCount = 10)] {collectionType} Method(); }} -" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); string ICustomMarshallingSignatureTestProvider.CustomElementMarshalling(string collectionType, string elementMarshaller, string predeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -232,6 +230,6 @@ partial interface INativeAPI out int pOutSize ); }} -" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); +" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI"); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/VTableGeneratorOutputShape.cs similarity index 84% rename from src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs rename to src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/VTableGeneratorOutputShape.cs index c4c724a..80c7702 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/VTableGeneratorOutputShape.cs @@ -14,7 +14,7 @@ using Xunit; namespace ComInterfaceGenerator.Unit.Tests { - public class NativeInterfaceShape + public class VTableGeneratorOutputShape { [Fact] public async Task NativeInterfaceNestedInUserInterface() @@ -32,7 +32,6 @@ namespace ComInterfaceGenerator.Unit.Tests } """; Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation TestUtils.AssertPreSourceGeneratorCompilation(comp); var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); @@ -59,7 +58,6 @@ namespace ComInterfaceGenerator.Unit.Tests } """; Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation TestUtils.AssertPreSourceGeneratorCompilation(comp); var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); @@ -71,7 +69,7 @@ namespace ComInterfaceGenerator.Unit.Tests } [Fact] - public async Task NativeInterfaceHasDynamicInterfaceCastableImplementationAttribute() + public async Task NativeInterfaceImplementsUserInterfaceMethods() { string source = $$""" using System.Runtime.InteropServices; @@ -84,9 +82,34 @@ namespace ComInterfaceGenerator.Unit.Tests [VirtualMethodIndex(0)] void Method(); } + + // Ensure we can implement the native interface without defining any implementations. + class C : INativeAPI.Native {} """; Compilation comp = await TestUtils.CreateCompilation(source); // Allow the Native nested type name to be missing in the pre-source-generator compilation + TestUtils.AssertPreSourceGeneratorCompilation(comp, "CS0426"); + + var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); + TestUtils.AssertPostSourceGeneratorCompilation(newComp); + } + + [Fact] + public async Task NativeInterfaceHasDynamicInterfaceCastableImplementationAttribute() + { + string source = $$""" + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType + { + static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; + [VirtualMethodIndex(0)] + void Method(); + } + """; + Compilation comp = await TestUtils.CreateCompilation(source); TestUtils.AssertPreSourceGeneratorCompilation(comp); var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); @@ -118,7 +141,6 @@ namespace ComInterfaceGenerator.Unit.Tests } """; Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation TestUtils.AssertPreSourceGeneratorCompilation(comp); var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); -- 2.7.4