Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interface inheritance follow up #85117

Merged
merged 36 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
54eaf06
wip
jtschuster Apr 14, 2023
153f52a
wip
jtschuster Apr 17, 2023
39869a7
wip
jtschuster Apr 18, 2023
a5132ad
Wip
jtschuster Apr 19, 2023
b5b3004
wip
jtschuster Apr 19, 2023
b7a677c
wip
jtschuster Apr 20, 2023
8b03401
delete unused code
jtschuster Apr 20, 2023
d5b188c
More cleaup and renaming
jtschuster Apr 20, 2023
b0f0bd1
Merge branch 'main' of https://github.com/dotnet/runtime into interfa…
jtschuster Apr 20, 2023
93a9033
Add ComInterfaceContext, create diagnostics in Record constructor
jtschuster Apr 28, 2023
b3ad04c
wip
jtschuster May 1, 2023
d039410
Generate shadowing definitions and implementations
jtschuster May 4, 2023
3915490
rename
jtschuster May 4, 2023
3eba497
Don't generate Vtable methods for inherited methods
jtschuster May 4, 2023
04b8309
Stub out inherited methods on InterfaceImplementation
jtschuster May 5, 2023
5b2337a
Merge branch 'main' of https://github.com/dotnet/runtime into interfa…
jtschuster May 5, 2023
6d78a2d
Don't emit compiler generated files
jtschuster May 5, 2023
2308998
Fix test and add additional doc comments
jtschuster May 8, 2023
f6ac52b
Merge branch 'main' of https://github.com/dotnet/runtime into interfa…
jtschuster May 9, 2023
8883b28
Format and comment update
jtschuster May 9, 2023
130faae
Use the right diagnostic property
jtschuster May 9, 2023
033d817
Remove extra usings
jtschuster May 9, 2023
ae2fe39
Remove commented code, update doc comment
jtschuster May 10, 2023
299b3f3
Make methodInfo creation follow the same pattern as InterfaceInfo
jtschuster May 10, 2023
1730faa
Make methodInfo creation follow the same pattern as InterfaceInfo
jtschuster May 10, 2023
d928a89
Merge branch 'interfaceInheritanceFollowUp' of https://github.com/jts…
jtschuster May 10, 2023
f18c21c
Remove Diagnostic from ComMethodInfo
jtschuster May 10, 2023
38e6267
Create flat list of methods, group with interfaceContext so we don't
jtschuster May 12, 2023
7c1d275
Clean up, rename variables, and add comments
jtschuster May 15, 2023
9293619
Merge branch 'main' of https://github.com/dotnet/runtime into interfa…
jtschuster May 15, 2023
d78aeaf
PR Feedback
jtschuster May 15, 2023
5659559
Forgot to add new file
jtschuster May 15, 2023
2d201d1
Update src/libraries/System.Runtime.InteropServices/gen/Microsoft.Int…
jtschuster May 15, 2023
68ea987
Report diagnostic when unable to analyze code
jtschuster May 15, 2023
ec978c6
Inline local variables in HashCode
jtschuster May 15, 2023
01fa078
Return IUnknown methods in VTable for empty interface
jtschuster May 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace Microsoft.Interop
{
internal sealed record AttributeInfo(ManagedTypeInfo Type, SequenceEqualImmutableArray<string> Arguments)
{
internal static AttributeInfo From(AttributeData attribute)
{
var type = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(attribute.AttributeClass);
var args = attribute.ConstructorArguments.Select(ca => ca.ToCSharpString());
return new(type, args.ToSequenceEqualImmutableArray());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public sealed partial class ComInterfaceGenerator
/// <summary>
/// Represents an interface and all of the methods that need to be generated for it (methods declared on the interface and methods inherited from base interfaces).
/// </summary>
private sealed record ComInterfaceAndMethodsContext(ComInterfaceContext Interface, SequenceEqualImmutableArray<ComMethodContext> Methods)
private sealed record ComInterfaceAndMethodsContext(ComInterfaceContext Interface, SequenceEqualImmutableArray<ComMethodContext> Methods, SequenceEqualImmutableArray<ComMethodContext> ShadowingMethods)
{
/// <summary>
/// COM methods that are declared on the attributed interface declaration.
Expand All @@ -24,23 +24,23 @@ private sealed record ComInterfaceAndMethodsContext(ComInterfaceContext Interfac
/// <summary>
/// COM methods that are declared on an interface the interface inherits from.
/// </summary>
public IEnumerable<ComMethodContext> ShadowingMethods => Methods.Where(m => m.DeclaringInterface != Interface);
public IEnumerable<ComMethodContext> InheritedMethods => Methods.Where(m => m.DeclaringInterface != Interface);

internal static ComInterfaceAndMethodsContext From((ComInterfaceContext, SequenceEqualImmutableArray<ComMethodContext>) data, CancellationToken _)
=> new ComInterfaceAndMethodsContext(data.Item1, data.Item2);
//internal static ComInterfaceAndMethodsContext From((ComInterfaceContext, SequenceEqualImmutableArray<ComMethodContext>) data, CancellationToken _)
// => new ComInterfaceAndMethodsContext(data.Item1, data.Item2);

public static IEnumerable<ComInterfaceAndMethodsContext> CalculateAllMethods(ValueEqualityImmutableDictionary<ComInterfaceContext, SequenceEqualImmutableArray<ComMethodInfo>> ifaceToDeclaredMethodsMap, StubEnvironment environment, CancellationToken ct)
{
Dictionary<ComInterfaceContext, ImmutableArray<ComMethodContext>> allMethodsCache = new();
Dictionary<ComInterfaceContext, (ImmutableArray<ComMethodContext> Methods, ImmutableArray<ComMethodContext> ShadowingMethods)> allMethodsCache = new();

foreach (var kvp in ifaceToDeclaredMethodsMap)
{
AddMethods(kvp.Key, kvp.Value);
}

return allMethodsCache.Select(kvp => new ComInterfaceAndMethodsContext(kvp.Key, kvp.Value.ToSequenceEqual()));
return allMethodsCache.Select(kvp => new ComInterfaceAndMethodsContext(kvp.Key, kvp.Value.Methods.ToSequenceEqual(), kvp.Value.ShadowingMethods.ToSequenceEqualImmutableArray()));

ImmutableArray<ComMethodContext> AddMethods(ComInterfaceContext iface, IEnumerable<ComMethodInfo> declaredMethods)
(ImmutableArray<ComMethodContext> Methods, ImmutableArray<ComMethodContext> ShadowingMethods) AddMethods(ComInterfaceContext iface, IEnumerable<ComMethodInfo> declaredMethods)
{
if (allMethodsCache.TryGetValue(iface, out var cachedValue))
{
Expand All @@ -53,23 +53,33 @@ ImmutableArray<ComMethodContext> AddMethods(ComInterfaceContext iface, IEnumerab
if (iface.Base is not null)
{
var baseComIface = iface.Base;
if (!allMethodsCache.TryGetValue(baseComIface, out var baseMethods))
ImmutableArray<ComMethodContext> baseMethods;
if (!allMethodsCache.TryGetValue(baseComIface, out var pair))
{
baseMethods = AddMethods(baseComIface, ifaceToDeclaredMethodsMap[baseComIface]);
baseMethods = AddMethods(baseComIface, ifaceToDeclaredMethodsMap[baseComIface]).Methods;
}
else
{
baseMethods = pair.Methods;
}
methods.AddRange(baseMethods);
startingIndex += baseMethods.Length;
}
var shadowingMethods = methods.Select(method =>
{
var info = method.MethodInfo;
var ctx = CalculateStubInformation(info.Syntax, info.Symbol, startingIndex, environment, iface.Info.Type, ct);
return new ComMethodContext(iface, info, startingIndex++, ctx);
}).ToImmutableArray();
// Then we append the declared methods in vtable order
foreach (var method in declaredMethods)
{
var ctx = CalculateStubInformation(method.Syntax, method.Symbol, startingIndex, environment, ct);
var ctx = CalculateStubInformation(method.Syntax, method.Symbol, startingIndex, environment, iface.Info.Type, ct);
jtschuster marked this conversation as resolved.
Show resolved Hide resolved
methods.Add(new ComMethodContext(iface, method, startingIndex++, ctx));
}
// Cache so we don't recalculate if many interfaces inherit from the same one
var immutableMethods = methods.ToImmutableArray();
allMethodsCache[iface] = immutableMethods;
return immutableMethods;
var finalPair = (methods.ToImmutableArray(), shadowingMethods);
allMethodsCache[iface] = finalPair;
return finalPair;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private sealed record ComInterfaceContext(ComInterfaceInfo Info, ComInterfaceCon
/// <summary>
/// Takes a list of ComInterfaceInfo, and creates a list of ComInterfaceContext. Does not guarantee the ordering of the output.
/// </summary>
public static Dictionary<string, ComInterfaceContext>.ValueCollection GetContexts(ImmutableArray<ComInterfaceInfo> data, CancellationToken _)
public static IEnumerable<ComInterfaceContext> GetContexts(ImmutableArray<ComInterfaceInfo> data, CancellationToken _)
{
Dictionary<string, ComInterfaceInfo> symbolToInterfaceInfoMap = new();
foreach (var iface in data)
Expand All @@ -25,9 +25,8 @@ public static Dictionary<string, ComInterfaceContext>.ValueCollection GetContext

foreach (var iface in data)
{
AddContext(iface);
yield return AddContext(iface);
}
return symbolToContextMap.Values;

ComInterfaceContext AddContext(ComInterfaceInfo iface)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.WithComparer(SyntaxEquivalentComparer.Instance)
.SelectNormalized();

var shadowingMethods = interfacesWithMethodsAndItsMethods
.Select((data, ct) =>
{
var context = data.Interface.Info;
var methods = data.InheritedMethods.Select(m => (MemberDeclarationSyntax)m.GenerateShadow());
var asdf = TypeDeclaration(context.ContainingSyntax.TypeKind, context.ContainingSyntax.Identifier)
.WithModifiers(context.ContainingSyntax.Modifiers)
.WithTypeParameterList(context.ContainingSyntax.TypeParameters)
.WithMembers(List(methods));
return data.Interface.Info.TypeDefinitionContext.WrapMemberInContainingSyntaxWithUnsafeModifier(asdf);
})
.SelectNormalized();

// Generate a method named CreateManagedVirtualFunctionTable on the native interface implementation
// that allocates and fills in the memory for the vtable.
var nativeToManagedVtables = interfacesWithMethodsAndItsMethods
Expand All @@ -184,9 +197,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.Zip(nativeToManagedVtableMethods)
.Zip(nativeToManagedVtables)
.Zip(iUnknownDerivedAttributeApplication)
.Zip(shadowingMethods)
.Select(static (data, ct) =>
{
var (((((interfaceContext, interfaceInfo), managedToNativeStubs), nativeToManagedStubs), nativeToManagedVtable), iUnknownDerivedAttribute) = data;
var ((((((interfaceContext, interfaceInfo), managedToNativeStubs), nativeToManagedStubs), nativeToManagedVtable), iUnknownDerivedAttribute), shadowingMethod) = data;

using StringWriter source = new();
interfaceInfo.WriteTo(source);
Expand All @@ -204,6 +218,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
source.WriteLine();
source.WriteLine();
iUnknownDerivedAttribute.WriteTo(source);
source.WriteLine();
source.WriteLine();
shadowingMethod.WriteTo(source);
return new { TypeName = interfaceContext.Info.Type.FullTypeName, Source = source.ToString() };
});

Expand Down Expand Up @@ -255,7 +272,7 @@ private static MemberDeclarationSyntax GenerateIUnknownDerivedAttributeApplicati
.AddAttributeLists(AttributeList(SingletonSeparatedList(s_iUnknownDerivedAttributeTemplate))));

// Todo: extract info needed from the IMethodSymbol into MethodInfo and only pass a MethodInfo here
private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, CancellationToken ct)
private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, ManagedTypeInfo typeKeyOwner, CancellationToken ct)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeKeyOwner will be the deriving type for shadowing methods and cannot just be the containingType of the methods

{
ct.ThrowIfCancellationRequested();
INamedTypeSymbol? lcidConversionAttrType = environment.Compilation.GetTypeByMetadataName(TypeNames.LCIDConversionAttribute);
Expand Down Expand Up @@ -348,8 +365,6 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M

ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> callConv = VtableIndexStubGenerator.GenerateCallConvSyntaxFromAttributes(suppressGCTransitionAttribute, unmanagedCallConvAttribute);

var typeKeyOwner = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol.ContainingType);

var virtualMethodIndexData = new VirtualMethodIndexData(index, ImplicitThisParameter: true, MarshalDirection.Bidirectional, true, ExceptionMarshalling.Com);

return new IncrementalMethodStubGenerationContext(
Expand All @@ -375,15 +390,31 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M
private static InterfaceDeclarationSyntax GenerateImplementationInterface(ComInterfaceAndMethodsContext interfaceGroup, CancellationToken _)
{
var definingType = interfaceGroup.Interface.Info.Type;
var shadowImplementations = interfaceGroup.ShadowingMethods.Select(m => (m, m.ManagedToUnmanagedStub))
.Where(p => p.ManagedToUnmanagedStub is GeneratedStubCodeContext)
.Select(ctx => ((GeneratedStubCodeContext)ctx.ManagedToUnmanagedStub).Stub.Node
.WithExplicitInterfaceSpecifier(
ExplicitInterfaceSpecifier(ParseName(definingType.FullTypeName))));
return ImplementationInterfaceTemplate
.AddBaseListTypes(SimpleBaseType(definingType.Syntax))
.WithMembers(List<MemberDeclarationSyntax>(interfaceGroup.DeclaredMethods.Select(m => m.ManagedToUnmanagedStub).OfType<GeneratedStubCodeContext>().Select(ctx => ctx.Stub.Node)))
.WithMembers(
List<MemberDeclarationSyntax>(
interfaceGroup.Methods
.Select(m => m.ManagedToUnmanagedStub)
.OfType<GeneratedStubCodeContext>()
.Select(ctx => ctx.Stub.Node)
.Concat(shadowImplementations)))
.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(ParseName(TypeNames.System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute)))));
}
private static InterfaceDeclarationSyntax GenerateImplementationVTableMethods(ComInterfaceAndMethodsContext comInterfaceAndMethods, CancellationToken _)
{
return ImplementationInterfaceTemplate
.WithMembers(List<MemberDeclarationSyntax>(comInterfaceAndMethods.DeclaredMethods.Select(m => m.NativeToManagedStub).OfType<GeneratedStubCodeContext>().Select(context => context.Stub.Node)));
.WithMembers(
List<MemberDeclarationSyntax>(
comInterfaceAndMethods.Methods
.Select(m => m.NativeToManagedStub)
.OfType<GeneratedStubCodeContext>()
.Select(context => context.Stub.Node) ));
}

private static readonly TypeSyntax VoidStarStarSyntax = PointerType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))));
Expand Down Expand Up @@ -549,7 +580,7 @@ private static InterfaceDeclarationSyntax GenerateImplementationVTable(ComInterf
ParenthesizedExpression(
BinaryExpression(SyntaxKind.MultiplyExpression,
SizeOfExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword)))),
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(interfaceMethods.ShadowingMethods.Count() + 3))))))
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(interfaceMethods.InheritedMethods.Count() + 3))))))
})))));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,21 @@ public MethodDeclarationSyntax GenerateShadow()
// {
// return ((<baseInterfaceType>)this).<MethodName>(<Arguments>);
// }
return MethodInfo.Syntax.WithBody(
Block(
ReturnStatement(
// TODO: Copy full name of parameter types and attributes / attribute arguments for parameters
return MethodInfo.Syntax
.WithModifiers(TokenList(Token(SyntaxKind.NewKeyword)))
.WithExpressionBody(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrow expression body makes it easier to handle void methods - no need to worry about the return keyword being there or not.

ArrowExpressionClause(
InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
CastExpression(DeclaringInterface.Info.Type.Syntax, IdentifierName(Token(SyntaxKind.ThisKeyword))),
ParenthesizedExpression(
CastExpression(DeclaringInterface.Info.Type.Syntax, IdentifierName("this"))),
IdentifierName(MethodInfo.MethodName)),
ArgumentList(
// TODO: RefKind keywords
SeparatedList(MethodInfo.Parameters.Select(p => Argument(IdentifierName(p.Name)))))))));
SeparatedList(MethodInfo.Parameters.Select(p =>
Argument(IdentifierName(p.Name))))))));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ namespace Microsoft.Interop
{
public sealed partial class ComInterfaceGenerator
{

/// <summary>
/// Represents a method that has been determined to be a COM interface method. Only contains info immediately available from an IMethodSymbol and MethodDeclarationSyntax.
/// </summary>
private sealed record ComMethodInfo(
[property: Obsolete] IMethodSymbol Symbol,
MethodDeclarationSyntax Syntax,
string MethodName,
SequenceEqualImmutableArray<(ManagedTypeInfo Type, string Name, RefKind RefKind)> Parameters,
SequenceEqualImmutableArray<ParameterInfo> Parameters,
Diagnostic? Diagnostic)
{
private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax comMethodDeclaringSyntax, IMethodSymbol method)
Expand Down Expand Up @@ -87,10 +88,10 @@ public static bool IsComMethod(ComInterfaceInfo ifaceContext, ISymbol member, [N
if (comMethodDeclaringSyntax is null)
throw new NotImplementedException("Found a method that was declared in the attributed interface declaration, but couldn't find the syntax for it.");

List<(ManagedTypeInfo ParameterType, string Name, RefKind RefKind)> parameters = new();
List<ParameterInfo> parameters = new();
foreach (var parameter in ((IMethodSymbol)member).Parameters)
{
parameters.Add((ManagedTypeInfo.CreateTypeInfoForTypeSymbol(parameter.Type), parameter.Name, parameter.RefKind));
parameters.Add(ParameterInfo.From(parameter));
}

diag = GetDiagnosticIfInvalidMethodForGeneration(comMethodDeclaringSyntax, (IMethodSymbol)member);
Expand All @@ -102,4 +103,17 @@ public static bool IsComMethod(ComInterfaceInfo ifaceContext, ISymbol member, [N
}
}
}

internal record struct ParameterInfo(ManagedTypeInfo Type, string Name, RefKind RefKind, SequenceEqualImmutableArray<AttributeInfo> Attributes)
jtschuster marked this conversation as resolved.
Show resolved Hide resolved
{
public static ParameterInfo From(IParameterSymbol parameter)
{
var attributes = new List<AttributeInfo>();
foreach (var attribute in parameter.GetAttributes())
{
attributes.Add(AttributeInfo.From(attribute));
}
return new(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(parameter.Type), parameter.Name, parameter.RefKind, attributes.ToSequenceEqualImmutableArray());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeCont
}
break;
case StubCodeContext.Stage.Cleanup:
return _nativeTypeMarshaller.GenerateCleanupStatements(info, context);
if (elementMarshalDirection is MarshalDirection.UnmanagedToManaged)
{
return _nativeTypeMarshaller.GenerateCleanupStatements(info, context);
}
break;
default:
break;
}
Expand Down
Loading