Skip to content

Commit

Permalink
Fix marshaling in AOT source generator (#1628)
Browse files Browse the repository at this point in the history
* Fix bool marshaling and array marshaling

* Fix lookup table generation to properly handle authoring scenarios

* Improve comment
  • Loading branch information
manodasanW authored Jun 2, 2024
1 parent 6257088 commit 7602014
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 63 deletions.
60 changes: 36 additions & 24 deletions src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

var vtablesToAddFromClassTypes = context.SyntaxProvider.CreateSyntaxProvider(
static (n, _) => NeedVtableAttribute(n),
static (n, _) => GetVtableAttributeToAdd(n)
static (n, _) => GetVtableAttributeToAdd(n, false)
).Where(static vtableAttribute => vtableAttribute != default);

var vtableAttributesToAdd = vtablesToAddFromClassTypes.Select(static (vtable, _) => vtable.Item1);
Expand All @@ -36,7 +36,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
// that will already be generated by the component generator.
var vtablesFromComponentTypes = context.SyntaxProvider.CreateSyntaxProvider(
static (n, _) => IsComponentType(n),
static (n, _) => GetVtableAttributeToAdd(n)
static (n, _) => GetVtableAttributeToAdd(n, true)
).Where(static vtableAttribute => vtableAttribute != default).Collect().Combine(properties).
// Get component types if only authoring scenario
SelectMany(static ((ImmutableArray<(VtableAttribute vtableAttribute, EquatableArray<VtableAttribute> adapterTypes)> classTypes, (bool, bool isCsWinRTComponent, bool) properties) value, CancellationToken _) =>
Expand All @@ -45,7 +45,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var instantiatedTypesToAddOnLookupTable = context.SyntaxProvider.CreateSyntaxProvider(
static (n, _) => NeedVtableOnLookupTable(n),
static (n, _) => GetVtableAttributesToAddOnLookupTable(n)
).SelectMany(static (vtable, _) => vtable).
).Combine(properties)
// Get component types if only authoring scenario
.Select(static (((EquatableArray<VtableAttribute> lookupTable, EquatableArray<VtableAttribute> componentLookupTable) vtableAttributes, (bool, bool isCsWinRTComponent, bool) properties) value, CancellationToken _) =>
value.properties.isCsWinRTComponent ? value.vtableAttributes.componentLookupTable : value.vtableAttributes.lookupTable)
.SelectMany(static (vtable, _) => vtable).
Where(static vtableAttribute => vtableAttribute != null);

var instantiatedTaskAdapters = context.SyntaxProvider.CreateSyntaxProvider(
Expand Down Expand Up @@ -108,16 +112,19 @@ private static bool IsComponentType(SyntaxNode node)
!GeneratorHelper.IsWinRTType(declaration); // Making sure it isn't an RCW we are projecting.
}

private static (VtableAttribute, EquatableArray<VtableAttribute>) GetVtableAttributeToAdd(GeneratorSyntaxContext context)
private static (VtableAttribute, EquatableArray<VtableAttribute>) GetVtableAttributeToAdd(GeneratorSyntaxContext context, bool checkForComponentTypes)
{
var isWinRTTypeFunc = checkForComponentTypes ?
GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation) :
GeneratorHelper.IsWinRTType;
var symbol = context.SemanticModel.GetDeclaredSymbol(context.Node as ClassDeclarationSyntax);
var vtableAttribute = GetVtableAttributeToAdd(symbol, GeneratorHelper.IsWinRTType, context.SemanticModel.Compilation, false);
var vtableAttribute = GetVtableAttributeToAdd(symbol, isWinRTTypeFunc, context.SemanticModel.Compilation, false);
if (vtableAttribute != default)
{
HashSet<VtableAttribute> vtableAttributesForLookupTable = [];
// Add any adapter types which may be needed if certain functions
// from some known interfaces are called.
AddVtableAdapterTypeForKnownInterface(symbol, context.SemanticModel.Compilation, GeneratorHelper.IsWinRTType, vtableAttributesForLookupTable);
AddVtableAdapterTypeForKnownInterface(symbol, context.SemanticModel.Compilation, isWinRTTypeFunc, vtableAttributesForLookupTable);
return (vtableAttribute, vtableAttributesForLookupTable.ToImmutableArray());
}

Expand Down Expand Up @@ -194,19 +201,13 @@ private static (VtableAttribute, VtableAttribute) GetVtableAttributesForTaskAdap
{
var constructedAdapterType = adpaterType.Construct([.. symbol.TypeArguments]);
return (GetVtableAttributeToAdd(constructedAdapterType, GeneratorHelper.IsWinRTType, context.SemanticModel.Compilation, false),
GetVtableAttributeToAdd(constructedAdapterType, IsWinRTTypeWithPotentialAuthoringComponentTypes, context.SemanticModel.Compilation, false));
GetVtableAttributeToAdd(constructedAdapterType, GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation), context.SemanticModel.Compilation, false));
}
}
}
}

return default;

bool IsWinRTTypeWithPotentialAuthoringComponentTypes(ISymbol type)
{
var winrtTypeAttribute = context.SemanticModel.Compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute");
return GeneratorHelper.IsWinRTType(type, winrtTypeAttribute, true, context.SemanticModel.Compilation.Assembly);
}
}

private static string ToFullyQualifiedString(ISymbol symbol)
Expand Down Expand Up @@ -836,7 +837,18 @@ node is VariableDeclarationSyntax ||
node is ReturnStatementSyntax;
}

private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupTable(GeneratorSyntaxContext context)
private static (EquatableArray<VtableAttribute>, EquatableArray<VtableAttribute>) GetVtableAttributesToAddOnLookupTable(GeneratorSyntaxContext context)
{
// Get the lookup table as if we are running in an authoring component scenario and as if we are not
// and then use the properties later on when we have access to it to check if we are to choose the right one.
// Otherwise we will end up generating lookup tables which don't have vtable entries for authoring types.
return (GetVtableAttributesToAddOnLookupTable(context, GeneratorHelper.IsWinRTType),
GetVtableAttributesToAddOnLookupTable(context, GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation)));
}

private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupTable(
GeneratorSyntaxContext context,
Func<ISymbol, bool> isWinRTType)
{
HashSet<ITypeSymbol> visitedTypes = new(SymbolEqualityComparer.Default);
HashSet<VtableAttribute> vtableAttributes = new();
Expand All @@ -850,7 +862,7 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
// and end up calling a projection function (i.e. ones generated by XAML compiler)
// In theory, another library can also be called which can call a projected function
// but not handling those scenarios for now.
(GeneratorHelper.IsWinRTType(methodSymbol.ContainingSymbol) ||
(isWinRTType(methodSymbol.ContainingSymbol) ||
SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
{
// Get the concrete types directly from the argument rather than
Expand Down Expand Up @@ -879,13 +891,13 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
{
var leftSymbol = context.SemanticModel.GetSymbolInfo(assignment.Left).Symbol;
if (leftSymbol is IPropertySymbol propertySymbol &&
(GeneratorHelper.IsWinRTType(propertySymbol.ContainingSymbol) ||
(isWinRTType(propertySymbol.ContainingSymbol) ||
SymbolEqualityComparer.Default.Equals(propertySymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
{
AddVtableAttributesForType(context.SemanticModel.GetTypeInfo(assignment.Right), propertySymbol.Type);
}
else if (leftSymbol is IFieldSymbol fieldSymbol &&
(GeneratorHelper.IsWinRTType(fieldSymbol.ContainingSymbol) ||
(isWinRTType(fieldSymbol.ContainingSymbol) ||
SymbolEqualityComparer.Default.Equals(fieldSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
{
AddVtableAttributesForType(context.SemanticModel.GetTypeInfo(assignment.Right), fieldSymbol.Type);
Expand Down Expand Up @@ -953,14 +965,14 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
}
visitedTypes.Add(arrayType);

var vtableAtribute = GetVtableAttributeToAdd(arrayType, GeneratorHelper.IsWinRTType, context.SemanticModel.Compilation, false);
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isWinRTType, context.SemanticModel.Compilation, false);
if (vtableAtribute != default)
{
vtableAttributes.Add(vtableAtribute);
}

// Also add the enumerator type to the lookup table as the native caller can call it.
AddEnumeratorAdapterForType(arrayType.ElementType, context.SemanticModel.Compilation, GeneratorHelper.IsWinRTType, vtableAttributes);
AddEnumeratorAdapterForType(arrayType.ElementType, context.SemanticModel.Compilation, isWinRTType, vtableAttributes);
}
}
else if (instantiatedType.Type is not null || instantiatedType.ConvertedType is not null)
Expand All @@ -982,11 +994,11 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
// information is available.
if (instantiatedTypeSymbol.TypeKind == TypeKind.Delegate &&
instantiatedTypeSymbol.MetadataName.Contains("`") &&
GeneratorHelper.IsWinRTType(instantiatedTypeSymbol) &&
isWinRTType(instantiatedTypeSymbol) &&
convertedToTypeSymbol.SpecialType == SpecialType.System_Object)
{
var argumentClassNamedTypeSymbol = instantiatedTypeSymbol as INamedTypeSymbol;
vtableAttributes.Add(GetVtableAttributeToAdd(instantiatedTypeSymbol, GeneratorHelper.IsWinRTType, context.SemanticModel.Compilation, false));
vtableAttributes.Add(GetVtableAttributeToAdd(instantiatedTypeSymbol, isWinRTType, context.SemanticModel.Compilation, false));
}

// This handles the case where the source generator wasn't able to run
Expand All @@ -998,21 +1010,21 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
// library which happened to not run the AOT optimizer. So as a best effort,
// we handle it here.
if (instantiatedTypeSymbol.TypeKind == TypeKind.Class &&
(instantiatedTypeSymbol.MetadataName.Contains("`") || !GeneratorHelper.IsWinRTType(instantiatedTypeSymbol)) &&
(instantiatedTypeSymbol.MetadataName.Contains("`") || !isWinRTType(instantiatedTypeSymbol)) &&
!GeneratorHelper.HasWinRTExposedTypeAttribute(instantiatedTypeSymbol) &&
// If the type is defined in the same assembly as what the source generator is running on,
// we let the WinRTExposedType attribute generator handle it.
!SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly) &&
// Make sure the type we are passing is being boxed or cast to another interface.
!SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol, convertedToTypeSymbol))
{
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, GeneratorHelper.IsWinRTType, context.SemanticModel.Compilation, false);
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isWinRTType, context.SemanticModel.Compilation, false);
if (vtableAtribute != default)
{
vtableAttributes.Add(vtableAtribute);
}

AddVtableAdapterTypeForKnownInterface(instantiatedTypeSymbol, context.SemanticModel.Compilation, GeneratorHelper.IsWinRTType, vtableAttributes);
AddVtableAdapterTypeForKnownInterface(instantiatedTypeSymbol, context.SemanticModel.Compilation, isWinRTType, vtableAttributes);
}
}
}
Expand Down
Loading

0 comments on commit 7602014

Please sign in to comment.