Skip to content

Commit

Permalink
Don't create vtable entries for WinRT generic interfaces constru… (#193)
Browse files Browse the repository at this point in the history
* Don't create vtable entries for WinRT generic interfaces constructed over non-projected .NET types.

* Expose top-level covariant interfaces on CCWs.
  • Loading branch information
jkoritzinsky authored Apr 23, 2020
1 parent e56d172 commit fb40cb4
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 13 deletions.
9 changes: 9 additions & 0 deletions UnitTest/TestComponentCSharp_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1410,5 +1410,14 @@ public void TestStringUnboxing()
Assert.Equal(string.Empty, (string)str1);
Assert.Equal(string.Empty, (string)str2);
}

internal class ManagedType { }

[Fact]
public void CCWOfListOfManagedType()
{
using var ccw = ComWrappersSupport.CreateCCWForObject(new List<ManagedType>());
using var qiResult = ccw.As(GuidGenerator.GetIID(typeof(ABI.System.Collections.Generic.IEnumerable<object>)));
}
}
}
27 changes: 17 additions & 10 deletions WinRT.Runtime/ComWrappersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,19 +119,26 @@ internal static List<ComInterfaceEntry> GetInterfaceTableEntries(object obj)
var interfaces = obj.GetType().GetInterfaces();
foreach (var iface in interfaces)
{
var ifaceAbiType = iface.FindHelperType();
if (ifaceAbiType == null)
{
// This interface isn't a WinRT interface.
// TODO: Handle WinRT -> .NET projected interfaces.
continue;
if (Projections.IsTypeWindowsRuntimeType(iface))
{
var ifaceAbiType = iface.FindHelperType();
entries.Add(new ComInterfaceEntry
{
IID = GuidGenerator.GetIID(ifaceAbiType),
Vtable = (IntPtr)ifaceAbiType.FindVftblType().GetField("AbiToProjectionVftablePtr", BindingFlags.Public | BindingFlags.Static).GetValue(null)
});
}

entries.Add(new ComInterfaceEntry
if (iface.IsConstructedGenericType
&& Projections.TryGetCompatibleWindowsRuntimeTypeForVariantType(iface, out var compatibleIface))
{
IID = GuidGenerator.GetIID(ifaceAbiType),
Vtable = (IntPtr)ifaceAbiType.FindVftblType().GetField("AbiToProjectionVftablePtr", BindingFlags.Public | BindingFlags.Static).GetValue(null)
});
var compatibleIfaceAbiType = compatibleIface.FindHelperType();
entries.Add(new ComInterfaceEntry
{
IID = GuidGenerator.GetIID(compatibleIfaceAbiType),
Vtable = (IntPtr)compatibleIfaceAbiType.FindVftblType().GetField("AbiToProjectionVftablePtr", BindingFlags.Public | BindingFlags.Static).GetValue(null)
});
}
}

if (obj is Delegate)
Expand Down
63 changes: 60 additions & 3 deletions WinRT.Runtime/Projections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,68 @@ public static bool IsTypeWindowsRuntimeType(Type type)
{
typeToTest = typeToTest.GetElementType();
}
if (typeToTest.IsGenericType)
return IsTypeWindowsRuntimeTypeNoArray(typeToTest);
}

private static bool IsTypeWindowsRuntimeTypeNoArray(Type type)
{
if (type.IsConstructedGenericType)
{
if(IsTypeWindowsRuntimeTypeNoArray(type.GetGenericTypeDefinition()))
{
foreach (var arg in type.GetGenericArguments())
{
if (!IsTypeWindowsRuntimeTypeNoArray(arg))
{
return false;
}
}
return true;
}
return false;
}
return CustomTypeToAbiTypeNameMappings.ContainsKey(type) || type.GetCustomAttribute<WindowsRuntimeTypeAttribute>() is object;
}

public static bool TryGetCompatibleWindowsRuntimeTypeForVariantType(Type type, out Type compatibleType)
{
compatibleType = null;
if (!type.IsConstructedGenericType)
{
throw new ArgumentException(nameof(type));
}

var definition = type.GetGenericTypeDefinition();

if (!IsTypeWindowsRuntimeTypeNoArray(definition))
{
return false;
}

var genericConstraints = definition.GetGenericArguments();
var genericArguments = type.GetGenericArguments();
var newArguments = new Type[genericArguments.Length];
for (int i = 0; i < genericArguments.Length; i++)
{
typeToTest = typeToTest.GetGenericTypeDefinition();
if (!IsTypeWindowsRuntimeTypeNoArray(genericArguments[i]))
{
bool argumentCovariant = (genericConstraints[i].GenericParameterAttributes & GenericParameterAttributes.VarianceMask) == GenericParameterAttributes.Covariant;
if (argumentCovariant && !genericArguments[i].IsValueType)
{
newArguments[i] = typeof(object);
}
else
{
return false;
}
}
else
{
newArguments[i] = genericArguments[i];
}
}
return CustomTypeToAbiTypeNameMappings.ContainsKey(typeToTest) || typeToTest.GetCustomAttribute<WindowsRuntimeTypeAttribute>() is object;
compatibleType = definition.MakeGenericType(newArguments);
return true;
}

internal static bool TryGetDefaultInterfaceTypeForRuntimeClassType(Type runtimeClass, out Type defaultInterface)
Expand Down

0 comments on commit fb40cb4

Please sign in to comment.