Skip to content

Commit 2563a1f

Browse files
authored
Add 'WinRTManagedOnlyTypeDetails' type (#1815)
* 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'
1 parent 3740e12 commit 2563a1f

File tree

8 files changed

+212
-25
lines changed

8 files changed

+212
-25
lines changed

src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,19 @@ private static (VtableAttribute, EquatableArray<VtableAttribute>) GetVtableAttri
238238
bool checkForComponentTypes,
239239
bool isCsWinRTCcwLookupTableGeneratorEnabled)
240240
{
241+
var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(compilation);
241242
var isWinRTTypeFunc = checkForComponentTypes ?
242243
GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(compilation) :
243244
GeneratorHelper.IsWinRTType;
244-
var vtableAttribute = GetVtableAttributeToAdd(symbol, isWinRTTypeFunc, typeMapper, compilation, false);
245+
var vtableAttribute = GetVtableAttributeToAdd(symbol, isManagedOnlyType, isWinRTTypeFunc, typeMapper, compilation, false);
245246
if (vtableAttribute != default)
246247
{
247248
HashSet<VtableAttribute> vtableAttributesForLookupTable = [];
248249
// Add any adapter types which may be needed if certain functions
249250
// from some known interfaces are called.
250251
if (isCsWinRTCcwLookupTableGeneratorEnabled)
251252
{
252-
AddVtableAdapterTypeForKnownInterface(symbol, compilation, isWinRTTypeFunc, typeMapper, vtableAttributesForLookupTable);
253+
AddVtableAdapterTypeForKnownInterface(symbol, compilation, isManagedOnlyType, isWinRTTypeFunc, typeMapper, vtableAttributesForLookupTable);
253254
}
254255
return (vtableAttribute, vtableAttributesForLookupTable.ToImmutableArray());
255256
}
@@ -315,6 +316,7 @@ private static VtableAttribute GetVtableAttributesForTaskAdapters(GeneratorSynta
315316
var constructedAdapterType = adpaterType.Construct([.. symbol.TypeArguments]);
316317
return GetVtableAttributeToAdd(
317318
constructedAdapterType,
319+
GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
318320
!isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation),
319321
typeMapper,
320322
context.SemanticModel.Compilation,
@@ -568,6 +570,7 @@ private static string GetRuntimeClassName(
568570

569571
internal static VtableAttribute GetVtableAttributeToAdd(
570572
ITypeSymbol symbol,
573+
Func<ISymbol, bool> isManagedOnlyType,
571574
Func<ISymbol, TypeMapper, bool> isWinRTType,
572575
TypeMapper mapper,
573576
Compilation compilation,
@@ -584,6 +587,13 @@ internal static VtableAttribute GetVtableAttributeToAdd(
584587
return default;
585588
}
586589

590+
// Skip all types explicitly blocked for marshalling.
591+
// We don't want them to affect the codegen at all.
592+
if (isManagedOnlyType(symbol))
593+
{
594+
return default;
595+
}
596+
587597
HashSet<string> interfacesToAddToVtable = new();
588598
HashSet<GenericInterface> genericInterfacesToAddToVtable = new();
589599

@@ -1169,13 +1179,15 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
11691179
return GetVtableAttributesToAddOnLookupTable(
11701180
context,
11711181
typeMapper,
1182+
GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
11721183
!isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation),
11731184
GeneratorHelper.IsWinRTClass(context.SemanticModel.Compilation));
11741185
}
11751186

11761187
private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupTable(
11771188
GeneratorSyntaxContext context,
11781189
TypeMapper typeMapper,
1190+
Func<ISymbol, bool> isManagedOnlyType,
11791191
Func<ISymbol, TypeMapper, bool> isWinRTType,
11801192
Func<ISymbol, bool> isWinRTClass)
11811193
{
@@ -1316,14 +1328,14 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
13161328
}
13171329
visitedTypes.Add(arrayType);
13181330

1319-
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
1331+
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
13201332
if (vtableAtribute != default)
13211333
{
13221334
vtableAttributes.Add(vtableAtribute);
13231335
}
13241336

13251337
// Also add the enumerator type to the lookup table as the native caller can call it.
1326-
AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isWinRTType, vtableAttributes);
1338+
AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
13271339
}
13281340
}
13291341
else if (instantiatedType.Type is not null || instantiatedType.ConvertedType is not null)
@@ -1349,7 +1361,7 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
13491361
convertedToTypeSymbol.SpecialType == SpecialType.System_Object)
13501362
{
13511363
var argumentClassNamedTypeSymbol = instantiatedTypeSymbol as INamedTypeSymbol;
1352-
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
1364+
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
13531365
if (vtableAtribute != default)
13541366
{
13551367
vtableAttributes.Add(vtableAtribute);
@@ -1392,13 +1404,13 @@ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType
13921404

13931405
if (addClassOnLookupTable)
13941406
{
1395-
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
1407+
var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
13961408
if (vtableAtribute != default)
13971409
{
13981410
vtableAttributes.Add(vtableAtribute);
13991411
}
14001412

1401-
AddVtableAdapterTypeForKnownInterface(instantiatedTypeSymbol, context.SemanticModel.Compilation, isWinRTType, typeMapper, vtableAttributes);
1413+
AddVtableAdapterTypeForKnownInterface(instantiatedTypeSymbol, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, typeMapper, vtableAttributes);
14021414
}
14031415
}
14041416
}
@@ -1410,6 +1422,7 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
14101422
TypeMapper typeMapper,
14111423
bool isCsWinRTComponent)
14121424
{
1425+
var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation);
14131426
var isWinRTType = !isCsWinRTComponent ? GeneratorHelper.IsWinRTType : GeneratorHelper.IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(context.SemanticModel.Compilation);
14141427
HashSet<VtableAttribute> vtableAttributes = new();
14151428

@@ -1419,24 +1432,24 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
14191432
{
14201433
if (vtableType is IArrayTypeSymbol arrayType)
14211434
{
1422-
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
1435+
var vtableAtribute = GetVtableAttributeToAdd(arrayType, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
14231436
if (vtableAtribute != default)
14241437
{
14251438
vtableAttributes.Add(vtableAtribute);
14261439
}
14271440

14281441
// Also add the enumerator type to the lookup table as the native caller can call it.
1429-
AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isWinRTType, vtableAttributes);
1442+
AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
14301443
}
14311444
else
14321445
{
1433-
var vtableAtribute = GetVtableAttributeToAdd(vtableType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
1446+
var vtableAtribute = GetVtableAttributeToAdd(vtableType, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
14341447
if (vtableAtribute != default)
14351448
{
14361449
vtableAttributes.Add(vtableAtribute);
14371450
}
14381451

1439-
AddVtableAdapterTypeForKnownInterface(vtableType, context.SemanticModel.Compilation, isWinRTType, typeMapper, vtableAttributes);
1452+
AddVtableAdapterTypeForKnownInterface(vtableType, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, typeMapper, vtableAttributes);
14401453
}
14411454
}
14421455
}
@@ -1447,7 +1460,13 @@ private static EquatableArray<VtableAttribute> GetVtableAttributesToAddOnLookupT
14471460
// Any of the IEnumerable interfaces on the vtable can be used to get the enumerator. Given IEnumerable is
14481461
// a covariant interface, it means that we can end up getting an instance of the enumerable adapter for any one
14491462
// of those covariant interfaces and thereby need vtable lookup entries for all of them.
1450-
private static void AddEnumeratorAdapterForType(ITypeSymbol type, TypeMapper mapper, Compilation compilation, Func<ISymbol, TypeMapper, bool> isWinRTType, HashSet<VtableAttribute> vtableAttributes)
1463+
private static void AddEnumeratorAdapterForType(
1464+
ITypeSymbol type,
1465+
TypeMapper mapper,
1466+
Compilation compilation,
1467+
Func<ISymbol, bool> isManagedOnlyType,
1468+
Func<ISymbol, TypeMapper, bool> isWinRTType,
1469+
HashSet<VtableAttribute> vtableAttributes)
14511470
{
14521471
var enumerableType = compilation.GetTypeByMetadataName("System.Collections.Generic.IEnumerable`1");
14531472
if (enumerableType != null)
@@ -1464,7 +1483,7 @@ private static void AddEnumeratorAdapterForType(ITypeSymbol type, TypeMapper map
14641483
if (enumeratorAdapterType != null)
14651484
{
14661485
var constructedEnumeratorAdapterType = enumeratorAdapterType.Construct(compatibleIface.TypeArguments[0]);
1467-
var vtableAttribute = GetVtableAttributeToAdd(constructedEnumeratorAdapterType, isWinRTType, mapper, compilation, false);
1486+
var vtableAttribute = GetVtableAttributeToAdd(constructedEnumeratorAdapterType, isManagedOnlyType, isWinRTType, mapper, compilation, false);
14681487
if (vtableAttribute != default)
14691488
{
14701489
vtableAttributes.Add(vtableAttribute);
@@ -1476,13 +1495,25 @@ private static void AddEnumeratorAdapterForType(ITypeSymbol type, TypeMapper map
14761495
}
14771496
}
14781497

1479-
internal static void AddVtableAdapterTypeForKnownInterface(ITypeSymbol classType, Compilation compilation, Func<ISymbol, TypeMapper, bool> isWinRTType, TypeMapper mapper, HashSet<VtableAttribute> vtableAttributes)
1498+
internal static void AddVtableAdapterTypeForKnownInterface(
1499+
ITypeSymbol classType,
1500+
Compilation compilation,
1501+
Func<ISymbol, bool> isManagedOnlyType,
1502+
Func<ISymbol, TypeMapper, bool> isWinRTType,
1503+
TypeMapper mapper,
1504+
HashSet<VtableAttribute> vtableAttributes)
14801505
{
1506+
// If the type is blocked for marshalling, don't generate any code for any interfaces
1507+
if (isManagedOnlyType(classType))
1508+
{
1509+
return;
1510+
}
1511+
14811512
foreach (var iface in classType.AllInterfaces)
14821513
{
14831514
if (iface.MetadataName == "IEnumerable`1")
14841515
{
1485-
AddEnumeratorAdapterForType(iface.TypeArguments[0], mapper, compilation, isWinRTType, vtableAttributes);
1516+
AddEnumeratorAdapterForType(iface.TypeArguments[0], mapper, compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
14861517
}
14871518
else if (iface.MetadataName == "IDictionary`2")
14881519
{
@@ -1541,7 +1572,7 @@ void LookupAndAddVtableAttributeForGenericType(string type, ImmutableArray<IType
15411572
if (genericType != default)
15421573
{
15431574
var constructedGenericType = genericType.Construct([.. genericArgs]);
1544-
var vtableAttribute = GetVtableAttributeToAdd(constructedGenericType, isWinRTType, mapper, compilation, false);
1575+
var vtableAttribute = GetVtableAttributeToAdd(constructedGenericType, isManagedOnlyType, isWinRTType, mapper, compilation, false);
15451576
if (vtableAttribute != default)
15461577
{
15471578
vtableAttributes.Add(vtableAttribute);

src/Authoring/WinRT.SourceGenerator/Helper.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,30 @@ public static bool HasAttributeWithType(ISymbol symbol, ITypeSymbol attributeTyp
644644
return false;
645645
}
646646

647+
/// <summary>
648+
/// Checks whether a symbol is annotated with <c>[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]</c>.
649+
/// </summary>
650+
public static bool IsManagedOnlyType(ISymbol symbol, ITypeSymbol winrtExposedTypeAttribute, ITypeSymbol winrtManagedOnlyTypeDetails)
651+
{
652+
foreach (AttributeData attribute in symbol.GetAttributes())
653+
{
654+
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, winrtExposedTypeAttribute))
655+
{
656+
if (attribute.ConstructorArguments is [{ Kind: TypedConstantKind.Type, Type: ITypeSymbol exposedTypeDetails }] &&
657+
SymbolEqualityComparer.Default.Equals(exposedTypeDetails, winrtManagedOnlyTypeDetails))
658+
{
659+
return true;
660+
}
661+
662+
// A type can have just one [WinRTExposedType] attribute. If the details are not WinRTManagedOnlyTypeDetails,
663+
// we can immediatley stop here and avoid checking all remaining attributes, as we couldn't possibly match.
664+
return false;
665+
}
666+
}
667+
668+
return false;
669+
}
670+
647671
public static Func<ISymbol, TypeMapper, bool> IsWinRTTypeWithPotentialAuthoringComponentTypesFunc(Compilation compilation)
648672
{
649673
var winrtTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute");
@@ -655,6 +679,19 @@ bool IsWinRTTypeHelper(ISymbol type, TypeMapper typeMapper)
655679
}
656680
}
657681

682+
public static Func<ISymbol, bool> IsManagedOnlyType(Compilation compilation)
683+
{
684+
var winrtExposedTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WinRTExposedTypeAttribute");
685+
var winrtManagedOnlyTypeDetails = compilation.GetTypeByMetadataName("WinRT.WinRTManagedOnlyTypeDetails");
686+
687+
return IsManagedOnlyTypeHelper;
688+
689+
bool IsManagedOnlyTypeHelper(ISymbol type)
690+
{
691+
return IsManagedOnlyType(type, winrtExposedTypeAttribute, winrtManagedOnlyTypeDetails);
692+
}
693+
}
694+
658695
private static string GetAbiTypeForFundamentalType(ISymbol type)
659696
{
660697
if (type is INamedTypeSymbol namedTypeSymbol)

src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2753,19 +2753,21 @@ bool IsWinRTType(ISymbol symbol, TypeMapper mapper)
27532753
List<VtableAttribute> vtableAttributesToAdd = new();
27542754
HashSet<VtableAttribute> vtableAttributesToAddOnLookupTable = new();
27552755

2756+
Func<ISymbol, bool> isManagedOnlyTypeFunc = GeneratorHelper.IsManagedOnlyType(context.Compilation);
2757+
27562758
foreach (var typeDeclaration in typeDefinitionMapping.Values)
27572759
{
27582760
if (typeDeclaration.IsComponentType &&
27592761
typeDeclaration.Node is INamedTypeSymbol symbol &&
27602762
symbol.TypeKind == TypeKind.Class &&
27612763
!symbol.IsStatic)
27622764
{
2763-
var vtableAttribute = WinRTAotSourceGenerator.GetVtableAttributeToAdd(symbol, IsWinRTType, mapper, context.Compilation, true, typeDeclaration.DefaultInterface);
2765+
var vtableAttribute = WinRTAotSourceGenerator.GetVtableAttributeToAdd(symbol, isManagedOnlyTypeFunc, IsWinRTType, mapper, context.Compilation, true, typeDeclaration.DefaultInterface);
27642766
if (vtableAttribute != default)
27652767
{
27662768
vtableAttributesToAdd.Add(vtableAttribute);
27672769
}
2768-
WinRTAotSourceGenerator.AddVtableAdapterTypeForKnownInterface(symbol, context.Compilation, IsWinRTType, mapper, vtableAttributesToAddOnLookupTable);
2770+
WinRTAotSourceGenerator.AddVtableAdapterTypeForKnownInterface(symbol, context.Compilation, isManagedOnlyTypeFunc, IsWinRTType, mapper, vtableAttributesToAddOnLookupTable);
27692771
}
27702772
}
27712773

src/Tests/FunctionalTests/CCW/Program.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,18 @@
326326
return 136;
327327
}
328328

329+
#if NET8_0_OR_GREATER
330+
if (RunAndGetException(() => ComWrappersSupport.CreateCCWForObject(new ManagedOnlyClass())) is not NotSupportedException)
331+
{
332+
return 137;
333+
}
334+
335+
if (RunAndGetException(() => ComWrappersSupport.CreateCCWForObject(new ManagedOnlyStruct())) is not NotSupportedException)
336+
{
337+
return 138;
338+
}
339+
#endif
340+
329341
return 100;
330342

331343

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

350+
#if NET8_0_OR_GREATER
351+
static Exception RunAndGetException(Action action)
352+
{
353+
try
354+
{
355+
action();
356+
357+
return null;
358+
}
359+
catch (Exception e)
360+
{
361+
return e;
362+
}
363+
}
364+
#endif
365+
338366
unsafe bool CheckRuntimeClassName(IObjectReference objRef, string expected)
339367
{
340368
objRef.TryAs<IInspectable.Vftbl>(IID.IID_IInspectable, out var inspectable);
@@ -772,4 +800,16 @@ public string this[int i]
772800
}
773801
}
774802
}
775-
}
803+
}
804+
805+
#if NET8_0_OR_GREATER
806+
[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]
807+
public sealed partial class ManagedOnlyClass
808+
{
809+
}
810+
811+
[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]
812+
public partial struct ManagedOnlyStruct
813+
{
814+
}
815+
#endif
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
Compat issues with assembly WinRT.Runtime:
22
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.
3-
Total Issues: 1
3+
CannotRemoveAttribute : Attribute 'System.ComponentModel.EditorBrowsableAttribute' exists on 'WinRT.WinRTExposedTypeAttribute' in the contract but not the implementation.
4+
Total Issues: 2

0 commit comments

Comments
 (0)