Skip to content

Commit

Permalink
Add 'WinRTManagedOnlyTypeDetails' type (#1815)
Browse files Browse the repository at this point in the history
* Add 'WinRTManagedOnlyTypeDetails' type

* Throw from runtime class name class too

* Update all generators to ignore managed only types

* Optimize 'IsManagedOnlyType'

* Update API compat/ref, fix build errors

* Remove checks in 'TypeNameSupport'

* Add functional tests for 'WinRTManagedOnlyTypeDetails'
  • Loading branch information
Sergio0694 authored Nov 9, 2024
1 parent 3740e12 commit 2563a1f
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 25 deletions.
63 changes: 47 additions & 16 deletions src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,18 +238,19 @@ private static (VtableAttribute, EquatableArray<VtableAttribute>) GetVtableAttri
bool checkForComponentTypes,
bool isCsWinRTCcwLookupTableGeneratorEnabled)
{
var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(compilation);
var isWinRTTypeFunc = checkForComponentTypes ?
GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(compilation) :
GeneratorHelper.IsWinRTType;
var vtableAttribute = GetVtableAttributeToAdd(symbol, isWinRTTypeFunc, typeMapper, compilation, false);
var vtableAttribute = GetVtableAttributeToAdd(symbol, isManagedOnlyType, isWinRTTypeFunc, typeMapper, 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.
if (isCsWinRTCcwLookupTableGeneratorEnabled)
{
AddVtableAdapterTypeForKnownInterface(symbol, compilation, isWinRTTypeFunc, typeMapper, vtableAttributesForLookupTable);
AddVtableAdapterTypeForKnownInterface(symbol, compilation, isManagedOnlyType, isWinRTTypeFunc, typeMapper, vtableAttributesForLookupTable);
}
return (vtableAttribute, vtableAttributesForLookupTable.ToImmutableArray());
}
Expand Down Expand Up @@ -315,6 +316,7 @@ private static VtableAttribute GetVtableAttributesForTaskAdapters(GeneratorSynta
var constructedAdapterType = adpaterType.Construct([.. symbol.TypeArguments]);
return GetVtableAttributeToAdd(
constructedAdapterType,
GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
!isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation),
typeMapper,
context.SemanticModel.Compilation,
Expand Down Expand Up @@ -568,6 +570,7 @@ private static string GetRuntimeClassName(

internal static VtableAttribute GetVtableAttributeToAdd(
ITypeSymbol symbol,
Func<ISymbol, bool> isManagedOnlyType,
Func<ISymbol, TypeMapper, bool> isWinRTType,
TypeMapper mapper,
Compilation compilation,
Expand All @@ -584,6 +587,13 @@ internal static VtableAttribute GetVtableAttributeToAdd(
return default;
}

// Skip all types explicitly blocked for marshalling.
// We don't want them to affect the codegen at all.
if (isManagedOnlyType(symbol))
{
return default;
}

HashSet<string> interfacesToAddToVtable = new();
HashSet<GenericInterface> genericInterfacesToAddToVtable = new();

Expand Down Expand Up @@ -1169,13 +1179,15 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
return GetVtableAttributesToAddOnLookupTable(
context,
typeMapper,
GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
!isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation),
GeneratorHelper.IsWinRTClass(context.SemanticModel.Compilation));
}

private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupTable(
GeneratorSyntaxContext context,
TypeMapper typeMapper,
Func<ISymbol, bool> isManagedOnlyType,
Func<ISymbol, TypeMapper, bool> isWinRTType,
Func<ISymbol, bool> isWinRTClass)
{
Expand Down Expand Up @@ -1316,14 +1328,14 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
}
visitedTypes.Add(arrayType);

var vtableAtribute = GetVtableAttributeToAdd(arrayType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isManagedOnlyType, isWinRTType, typeMapper, 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, typeMapper, context.SemanticModel.Compilation, isWinRTType, vtableAttributes);
AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
}
}
else if (instantiatedType.Type is not null || instantiatedType.ConvertedType is not null)
Expand All @@ -1349,7 +1361,7 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
convertedToTypeSymbol.SpecialType == SpecialType.System_Object)
{
var argumentClassNamedTypeSymbol = instantiatedTypeSymbol as INamedTypeSymbol;
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
if (vtableAtribute != default)
{
vtableAttributes.Add(vtableAtribute);
Expand Down Expand Up @@ -1392,13 +1404,13 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType

if (addClassOnLookupTable)
{
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
if (vtableAtribute != default)
{
vtableAttributes.Add(vtableAtribute);
}

AddVtableAdapterTypeForKnownInterface(instantiatedTypeSymbol, context.SemanticModel.Compilation, isWinRTType, typeMapper, vtableAttributes);
AddVtableAdapterTypeForKnownInterface(instantiatedTypeSymbol, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, typeMapper, vtableAttributes);
}
}
}
Expand All @@ -1410,6 +1422,7 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
TypeMapper typeMapper,
bool isCsWinRTComponent)
{
var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation);
var isWinRTType = !isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation);
HashSet<VtableAttribute> vtableAttributes = new();

Expand All @@ -1419,24 +1432,24 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
{
if (vtableType is IArrayTypeSymbol arrayType)
{
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isManagedOnlyType, isWinRTType, typeMapper, 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, typeMapper, context.SemanticModel.Compilation, isWinRTType, vtableAttributes);
AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
}
else
{
var vtableAtribute = GetVtableAttributeToAdd(vtableType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
var vtableAtribute = GetVtableAttributeToAdd(vtableType, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
if (vtableAtribute != default)
{
vtableAttributes.Add(vtableAtribute);
}

AddVtableAdapterTypeForKnownInterface(vtableType, context.SemanticModel.Compilation, isWinRTType, typeMapper, vtableAttributes);
AddVtableAdapterTypeForKnownInterface(vtableType, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, typeMapper, vtableAttributes);
}
}
}
Expand All @@ -1447,7 +1460,13 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
// Any of the IEnumerable interfaces on the vtable can be used to get the enumerator. Given IEnumerable is
// a covariant interface, it means that we can end up getting an instance of the enumerable adapter for any one
// of those covariant interfaces and thereby need vtable lookup entries for all of them.
private static void AddEnumeratorAdapterForType(ITypeSymbol type, TypeMapper mapper, Compilation compilation, Func<ISymbol, TypeMapper, bool> isWinRTType, HashSet<VtableAttribute> vtableAttributes)
private static void AddEnumeratorAdapterForType(
ITypeSymbol type,
TypeMapper mapper,
Compilation compilation,
Func<ISymbol, bool> isManagedOnlyType,
Func<ISymbol, TypeMapper, bool> isWinRTType,
HashSet<VtableAttribute> vtableAttributes)
{
var enumerableType = compilation.GetTypeByMetadataName("System.Collections.Generic.IEnumerable`1");
if (enumerableType != null)
Expand All @@ -1464,7 +1483,7 @@ private static void AddEnumeratorAdapterForType(ITypeSymbol type, TypeMapper map
if (enumeratorAdapterType != null)
{
var constructedEnumeratorAdapterType = enumeratorAdapterType.Construct(compatibleIface.TypeArguments[0]);
var vtableAttribute = GetVtableAttributeToAdd(constructedEnumeratorAdapterType, isWinRTType, mapper, compilation, false);
var vtableAttribute = GetVtableAttributeToAdd(constructedEnumeratorAdapterType, isManagedOnlyType, isWinRTType, mapper, compilation, false);
if (vtableAttribute != default)
{
vtableAttributes.Add(vtableAttribute);
Expand All @@ -1476,13 +1495,25 @@ private static void AddEnumeratorAdapterForType(ITypeSymbol type, TypeMapper map
}
}

internal static void AddVtableAdapterTypeForKnownInterface(ITypeSymbol classType, Compilation compilation, Func<ISymbol, TypeMapper, bool> isWinRTType, TypeMapper mapper, HashSet<VtableAttribute> vtableAttributes)
internal static void AddVtableAdapterTypeForKnownInterface(
ITypeSymbol classType,
Compilation compilation,
Func<ISymbol, bool> isManagedOnlyType,
Func<ISymbol, TypeMapper, bool> isWinRTType,
TypeMapper mapper,
HashSet<VtableAttribute> vtableAttributes)
{
// If the type is blocked for marshalling, don't generate any code for any interfaces
if (isManagedOnlyType(classType))
{
return;
}

foreach (var iface in classType.AllInterfaces)
{
if (iface.MetadataName == "IEnumerable`1")
{
AddEnumeratorAdapterForType(iface.TypeArguments[0], mapper, compilation, isWinRTType, vtableAttributes);
AddEnumeratorAdapterForType(iface.TypeArguments[0], mapper, compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
}
else if (iface.MetadataName == "IDictionary`2")
{
Expand Down Expand Up @@ -1541,7 +1572,7 @@ void LookupAndAddVtableAttributeForGenericType(string type, ImmutableArray<IType
if (genericType != default)
{
var constructedGenericType = genericType.Construct([.. genericArgs]);
var vtableAttribute = GetVtableAttributeToAdd(constructedGenericType, isWinRTType, mapper, compilation, false);
var vtableAttribute = GetVtableAttributeToAdd(constructedGenericType, isManagedOnlyType, isWinRTType, mapper, compilation, false);
if (vtableAttribute != default)
{
vtableAttributes.Add(vtableAttribute);
Expand Down
37 changes: 37 additions & 0 deletions src/Authoring/WinRT.SourceGenerator/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,30 @@ public static bool HasAttributeWithType(ISymbol symbol, ITypeSymbol attributeTyp
return false;
}

/// <summary>
/// Checks whether a symbol is annotated with <c>[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]</c>.
/// </summary>
public static bool IsManagedOnlyType(ISymbol symbol, ITypeSymbol winrtExposedTypeAttribute, ITypeSymbol winrtManagedOnlyTypeDetails)
{
foreach (AttributeData attribute in symbol.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, winrtExposedTypeAttribute))
{
if (attribute.ConstructorArguments is [{ Kind: TypedConstantKind.Type, Type: ITypeSymbol exposedTypeDetails }] &&
SymbolEqualityComparer.Default.Equals(exposedTypeDetails, winrtManagedOnlyTypeDetails))
{
return true;
}

// A type can have just one [WinRTExposedType] attribute. If the details are not WinRTManagedOnlyTypeDetails,
// we can immediatley stop here and avoid checking all remaining attributes, as we couldn't possibly match.
return false;
}
}

return false;
}

public static Func<ISymbol, TypeMapper, bool> IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(Compilation compilation)
{
var winrtTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute");
Expand All @@ -655,6 +679,19 @@ bool IsWinRTTypeHelper(ISymbol type, TypeMapper typeMapper)
}
}

public static Func<ISymbol, bool> IsManagedOnlyType(Compilation compilation)
{
var winrtExposedTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WinRTExposedTypeAttribute");
var winrtManagedOnlyTypeDetails = compilation.GetTypeByMetadataName("WinRT.WinRTManagedOnlyTypeDetails");

return IsManagedOnlyTypeHelper;

bool IsManagedOnlyTypeHelper(ISymbol type)
{
return IsManagedOnlyType(type, winrtExposedTypeAttribute, winrtManagedOnlyTypeDetails);
}
}

private static string GetAbiTypeForFundamentalType(ISymbol type)
{
if (type is INamedTypeSymbol namedTypeSymbol)
Expand Down
6 changes: 4 additions & 2 deletions src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2753,19 +2753,21 @@ bool IsWinRTType(ISymbol symbol, TypeMapper mapper)
List<VtableAttribute> vtableAttributesToAdd = new();
HashSet<VtableAttribute> vtableAttributesToAddOnLookupTable = new();

Func<ISymbol, bool> isManagedOnlyTypeFunc = GeneratorHelper.IsManagedOnlyType(context.Compilation);

foreach (var typeDeclaration in typeDefinitionMapping.Values)
{
if (typeDeclaration.IsComponentType &&
typeDeclaration.Node is INamedTypeSymbol symbol &&
symbol.TypeKind == TypeKind.Class &&
!symbol.IsStatic)
{
var vtableAttribute = WinRTAotSourceGenerator.GetVtableAttributeToAdd(symbol, IsWinRTType, mapper, context.Compilation, true, typeDeclaration.DefaultInterface);
var vtableAttribute = WinRTAotSourceGenerator.GetVtableAttributeToAdd(symbol, isManagedOnlyTypeFunc, IsWinRTType, mapper, context.Compilation, true, typeDeclaration.DefaultInterface);
if (vtableAttribute != default)
{
vtableAttributesToAdd.Add(vtableAttribute);
}
WinRTAotSourceGenerator.AddVtableAdapterTypeForKnownInterface(symbol, context.Compilation, IsWinRTType, mapper, vtableAttributesToAddOnLookupTable);
WinRTAotSourceGenerator.AddVtableAdapterTypeForKnownInterface(symbol, context.Compilation, isManagedOnlyTypeFunc, IsWinRTType, mapper, vtableAttributesToAddOnLookupTable);
}
}

Expand Down
42 changes: 41 additions & 1 deletion src/Tests/FunctionalTests/CCW/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,18 @@
return 136;
}

#if NET8_0_OR_GREATER
if (RunAndGetException(() => ComWrappersSupport.CreateCCWForObject(new ManagedOnlyClass())) is not NotSupportedException)
{
return 137;
}

if (RunAndGetException(() => ComWrappersSupport.CreateCCWForObject(new ManagedOnlyStruct())) is not NotSupportedException)
{
return 138;
}
#endif

return 100;


Expand All @@ -335,6 +347,22 @@
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
static extern int WindowsDeleteString(IntPtr hstring);

#if NET8_0_OR_GREATER
static Exception RunAndGetException(Action action)
{
try
{
action();

return null;
}
catch (Exception e)
{
return e;
}
}
#endif

unsafe bool CheckRuntimeClassName(IObjectReference objRef, string expected)
{
objRef.TryAs<IInspectable.Vftbl>(IID.IID_IInspectable, out var inspectable);
Expand Down Expand Up @@ -772,4 +800,16 @@ public string this[int i]
}
}
}
}
}

#if NET8_0_OR_GREATER
[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]
public sealed partial class ManagedOnlyClass
{
}

[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]
public partial struct ManagedOnlyStruct
{
}
#endif
3 changes: 2 additions & 1 deletion src/WinRT.Runtime/ApiCompatBaseline.net8.0.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Compat issues with assembly WinRT.Runtime:
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'WinRT.GeneratedBindableCustomPropertyAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation.
Total Issues: 1
CannotRemoveAttribute : Attribute 'System.ComponentModel.EditorBrowsableAttribute' exists on 'WinRT.WinRTExposedTypeAttribute' in the contract but not the implementation.
Total Issues: 2
Loading

0 comments on commit 2563a1f

Please sign in to comment.