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

Authoring bug fixes #720

Merged
merged 12 commits into from
Feb 6, 2021
78 changes: 57 additions & 21 deletions src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -935,25 +935,29 @@ internal static (string, string, string, bool, bool) GetSystemTypeCustomMapping(
private void ProcessCustomMappedInterfaces(INamedTypeSymbol classSymbol)
{
Logger.Log("writing custom mapped interfaces for " + QualifiedName(classSymbol));
Dictionary<INamedTypeSymbol, bool> isPublicImplementation = new Dictionary<INamedTypeSymbol, bool>();

// Mark custom mapped interface members for removal later.
// Note we want to also mark members from interfaces without mappings.
foreach (var implementedInterface in GetInterfaces(classSymbol, true).
Where(symbol => MappedCSharpTypes.ContainsKey(QualifiedName(symbol)) ||
ImplementedInterfacesWithoutMapping.Contains(QualifiedName(symbol))))
{
string interfaceName = QualifiedName(implementedInterface);
Logger.Log("custom mapped interface: " + interfaceName);
bool isPubliclyImplemented = false;
Logger.Log("custom mapped interface: " + QualifiedName(implementedInterface, true));
foreach (var interfaceMember in implementedInterface.GetMembers())
{
var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember);
currentTypeDeclaration.CustomMappedSymbols.Add(classMember);
isPubliclyImplemented |= (classMember.DeclaredAccessibility == Accessibility.Public);
manodasanW marked this conversation as resolved.
Show resolved Hide resolved
}
isPublicImplementation[implementedInterface] = isPubliclyImplemented;
}

foreach (var implementedInterface in GetInterfaces(classSymbol)
.Where(symbol => MappedCSharpTypes.ContainsKey(QualifiedName(symbol))))
{
WriteCustomMappedTypeMembers(implementedInterface, true);
WriteCustomMappedTypeMembers(implementedInterface, true, isPublicImplementation[implementedInterface]);
}
}

Expand All @@ -975,17 +979,42 @@ INamedTypeSymbol GetTypeByMetadataName(string metadataName)
return types.FirstOrDefault();
}

private void WriteCustomMappedTypeMembers(INamedTypeSymbol symbol, bool isDefinition)
// Convert the entire type name including the generic types to WinMD format.
private string GetMappedQualifiedTypeName(ITypeSymbol symbol)
{
string qualifiedName = QualifiedName(symbol);
if (MappedCSharpTypes.ContainsKey(qualifiedName))
{
var (@namespace, mappedTypeName, _, _, _) = MappedCSharpTypes[qualifiedName].GetMapping(currentTypeDeclaration.Node);
qualifiedName = QualifiedName(@namespace, mappedTypeName);
if (symbol is INamedTypeSymbol namedType && namedType.TypeArguments.Length > 0)
{
return string.Format("{0}<{1}>", qualifiedName, string.Join(", ", namedType.TypeArguments.Select(type => GetMappedQualifiedTypeName(type))));
}
}
else if((symbol.ContainingNamespace.ToString() == "System" && symbol.IsValueType) || qualifiedName == "System.String")
{
// WinRT fundamental types
return symbol.Name;
}

return qualifiedName;
}

private void WriteCustomMappedTypeMembers(INamedTypeSymbol symbol, bool isDefinition, bool isPublic = true)
{
var (_, mappedTypeName, _, _, _) = MappedCSharpTypes[QualifiedName(symbol)].GetMapping(currentTypeDeclaration.Node);
Logger.Log("writing custom mapped type members for " + mappedTypeName);
string qualifiedName = GetMappedQualifiedTypeName(symbol);

Logger.Log("writing custom mapped type members for " + mappedTypeName + " public: " + isPublic + " qualified name: " + qualifiedName);
void AddMethod(string name, Parameter[] parameters, Symbol returnType)
{
parameters ??= new Parameter[0];
if (isDefinition)
{
var methodDefinitionHandle = AddMethodDefinition(name, parameters, returnType, false, false);
currentTypeDeclaration.AddMethod(symbol, name, methodDefinitionHandle);
var methodName = isPublic ? name : QualifiedName(qualifiedName, name);
var methodDefinitionHandle = AddMethodDefinition(methodName, parameters, returnType, false, false, false, isPublic);
currentTypeDeclaration.AddMethod(symbol, methodName, methodDefinitionHandle);
}
else
{
Expand All @@ -998,7 +1027,8 @@ void AddProperty(string name, Symbol type, bool setProperty)
{
if (isDefinition)
{
AddPropertyDefinition(name, type, symbol, setProperty, false);
var propertyName = isPublic ? name : QualifiedName(qualifiedName, name);
AddPropertyDefinition(propertyName, type, symbol, setProperty, false, isPublic);
}
else
{
Expand All @@ -1010,7 +1040,8 @@ void AddEvent(string name, Symbol eventType)
{
if (isDefinition)
{
AddEventDeclaration(name, eventType.Type, symbol, false);
var eventName = isPublic ? name : QualifiedName(qualifiedName, name);
AddEventDeclaration(eventName, eventType.Type, symbol, false, isPublic);
}
else
{
Expand Down Expand Up @@ -2419,14 +2450,14 @@ void AddProjectedType(INamedTypeSymbol type, string projectedTypeOverride = null
}
}

typeDefinitionMapping[projectedTypeOverride ?? QualifiedName(type)] = currentTypeDeclaration;
typeDefinitionMapping[projectedTypeOverride ?? QualifiedName(type, true)] = currentTypeDeclaration;
}

void AddMappedType(INamedTypeSymbol type)
{
currentTypeDeclaration = new TypeDeclaration(type);
WriteCustomMappedTypeMembers(type, false);
typeDefinitionMapping[QualifiedName(type)] = currentTypeDeclaration;
typeDefinitionMapping[QualifiedName(type, true)] = currentTypeDeclaration;
}

enum SynthesizedInterfaceType
Expand Down Expand Up @@ -2709,15 +2740,15 @@ public void FinalizeGeneration()
Logger.Log("finalizing class " + QualifiedName(classSymbol));
foreach (var implementedInterface in GetInterfaces(classSymbol))
{
var implementedInterfaceQualifiedName = QualifiedName(implementedInterface);
if (!typeDefinitionMapping.ContainsKey(implementedInterfaceQualifiedName))
var implementedInterfaceQualifiedNameWithGenerics = QualifiedName(implementedInterface, true);
if (!typeDefinitionMapping.ContainsKey(implementedInterfaceQualifiedNameWithGenerics))
{
AddType(implementedInterface);
}

Logger.Log("finalizing interface " + implementedInterfaceQualifiedName);
var interfaceTypeDeclaration = typeDefinitionMapping[implementedInterfaceQualifiedName];
if (MappedCSharpTypes.ContainsKey(implementedInterfaceQualifiedName))
Logger.Log("finalizing interface " + implementedInterfaceQualifiedNameWithGenerics);
var interfaceTypeDeclaration = typeDefinitionMapping[implementedInterfaceQualifiedNameWithGenerics];
if (MappedCSharpTypes.ContainsKey(QualifiedName(implementedInterface)))
{
Logger.Log("adding MethodImpls for custom mapped interface");
foreach (var interfaceMember in interfaceTypeDeclaration.MethodReferences)
Expand Down Expand Up @@ -2814,9 +2845,10 @@ public void FinalizeGeneration()
foreach (var interfaceDeclaration in interfaceDeclarations)
{
INamedTypeSymbol interfaceSymbol = interfaceDeclaration.Node as INamedTypeSymbol;
if (typeDefinitionMapping[QualifiedName(interfaceSymbol)].Handle != default && GetVersion(interfaceSymbol) == -1)
string qualifiedNameWithGenerics = QualifiedName(interfaceSymbol, true);
if (typeDefinitionMapping[qualifiedNameWithGenerics].Handle != default && GetVersion(interfaceSymbol) == -1)
{
AddDefaultVersionAttribute(typeDefinitionMapping[QualifiedName(interfaceSymbol)].Handle);
AddDefaultVersionAttribute(typeDefinitionMapping[qualifiedNameWithGenerics].Handle);
}
}

Expand Down Expand Up @@ -2900,19 +2932,23 @@ public string QualifiedName(string @namespace, string identifier)
return string.Join(".", @namespace, identifier);
}

public static string GetGenericName(ISymbol symbol)
public static string GetGenericName(ISymbol symbol, bool includeGenerics = false)
{
string name = symbol.Name;
if (symbol is INamedTypeSymbol namedType && namedType.TypeArguments.Length != 0)
{
name += "`" + namedType.TypeArguments.Length;
if(includeGenerics)
{
name += string.Format("<{0}>", string.Join(", ", namedType.TypeArguments));
}
}
return name;
}

public string QualifiedName(ISymbol symbol)
public string QualifiedName(ISymbol symbol, bool includeGenerics = false)
{
return QualifiedName(symbol.ContainingNamespace.ToString(), GetGenericName(symbol));
return QualifiedName(symbol.ContainingNamespace.ToString(), GetGenericName(symbol, includeGenerics));
}

public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
name="AuthoringTest.CustomDictionary"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="AuthoringTest.CustomDictionary2"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="AuthoringTest.CustomReadOnlyDictionary"
threadingModel="both"
Expand Down Expand Up @@ -46,6 +50,14 @@
name="AuthoringTest.ExplicltlyImplementedClass"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="AuthoringTest.InterfaceInheritance"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="AuthoringTest.MultipleInterfaceMappingClass"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="AuthoringTest.SingleInterfaceClass"
threadingModel="both"
Expand Down
106 changes: 106 additions & 0 deletions src/Tests/AuthoringConsumptionTest/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ TEST(AuthoringTest, Statics)
EXPECT_EQ(TestClass::GetDefaultNumber(), 2);
EXPECT_EQ(StaticClass::GetNumber(), 4);
EXPECT_EQ(StaticClass::GetNumber(2), 2);
EXPECT_EQ(TestClass::DefaultNumber(), 0);
TestClass::DefaultNumber(4);
EXPECT_EQ(TestClass::DefaultNumber(), 4);

int result = 0;
auto token = TestClass::StaticDelegateEvent(auto_revoke, [&result](uint32_t value)
{
result = value;
});
TestClass::FireStaticDelegate(1);
EXPECT_EQ(result, 1);
token.revoke();
TestClass::FireStaticDelegate(2);
EXPECT_EQ(result, 1);

EXPECT_EQ(StaticClass::Number(), 0);
StaticClass::Number(2);
EXPECT_EQ(StaticClass::Number(), 2);

double result2 = 0;
auto token2 = StaticClass::DelegateEvent(auto_revoke, [&result2](double value)
{
result2 = value;
});
StaticClass::FireDelegate(4.5);
EXPECT_EQ(result2, 4.5);
}

TEST(AuthoringTest, FunctionCalls)
Expand Down Expand Up @@ -66,6 +92,28 @@ TEST(AuthoringTest, ImplementExternalInterface)
EXPECT_EQ(www.Value(), hstring(L"CsWinRT"));
}

TEST(AuthoringTest, InterfaceInheritance)
{
InterfaceInheritance interfaceInheritance;
EXPECT_EQ(interfaceInheritance.GetDouble(), 2);
EXPECT_EQ(interfaceInheritance.GetNumStr(2.5), hstring(L"2.5"));
interfaceInheritance.SetNumber(4);
EXPECT_EQ(interfaceInheritance.Number(), 4);
EXPECT_EQ(interfaceInheritance.Name(), hstring(L"IInterfaceInheritance"));
EXPECT_EQ(interfaceInheritance.Value(), hstring(L"InterfaceInheritance"));

IDouble doubleInterface = interfaceInheritance;
EXPECT_EQ(doubleInterface.GetDouble(false), 2.5);

IInterfaceInheritance interfaceInheritanceInterface = interfaceInheritance;
interfaceInheritanceInterface.SetNumber(2);
EXPECT_EQ(interfaceInheritanceInterface.Number(), 2);

IWwwFormUrlDecoderEntry www = interfaceInheritance;
EXPECT_EQ(www.Name(), hstring(L"IInterfaceInheritance"));
EXPECT_EQ(www.Value(), hstring(L"InterfaceInheritance"));
}

TEST(AuthoringTest, ReturnTypes)
{
BasicClass basicClass;
Expand Down Expand Up @@ -481,4 +529,62 @@ TEST(AuthoringTest, ExplicitInterfaces)
EXPECT_TRUE(eventTriggered);
EXPECT_TRUE(event2Triggered);
token.revoke();

DisposableClass disposed;
disposed.Close();
MultipleInterfaceMappingClass multipleInterfaces;
Microsoft::UI::Xaml::Interop::IBindableIterable bindable = multipleInterfaces;
Windows::Foundation::Collections::IVector<DisposableClass> vector = multipleInterfaces;
Microsoft::UI::Xaml::Interop::IBindableVector bindableVector = multipleInterfaces;
EXPECT_EQ(vector.Size(), 0);
EXPECT_EQ(bindableVector.Size(), 0);
vector.Append(DisposableClass());
vector.Append(DisposableClass());
vector.Append(disposed);
bindableVector.Append(DisposableClass());
EXPECT_EQ(vector.Size(), 4);
EXPECT_EQ(bindableVector.Size(), 4);

auto first = vector.First();
EXPECT_TRUE(first.HasCurrent());
EXPECT_FALSE(first.Current().IsDisposed());
auto bindableFirst = bindable.First();
EXPECT_TRUE(bindableFirst.HasCurrent());
EXPECT_FALSE(bindableFirst.Current().as<DisposableClass>().IsDisposed());
bindableFirst.Current().as<DisposableClass>().Close();
EXPECT_TRUE(first.Current().IsDisposed());
EXPECT_FALSE(vector.GetAt(1).IsDisposed());
EXPECT_TRUE(vector.GetAt(2).IsDisposed());
EXPECT_TRUE(bindableVector.First().Current().as<DisposableClass>().IsDisposed());
EXPECT_FALSE(bindableVector.GetAt(3).as<DisposableClass>().IsDisposed());
EXPECT_TRUE(bindableVector.GetAt(2).as<DisposableClass>().IsDisposed());
for (auto obj : vector.GetView())
{
obj.Close();
}

std::array<DisposableClass, 2> view{};
EXPECT_EQ(vector.GetMany(1, view), 2);
EXPECT_EQ(view.size(), 2);
for (auto& obj : view)
{
EXPECT_TRUE(obj.IsDisposed());
}

CustomDictionary2 dictionary;

EXPECT_FALSE(dictionary.Insert(L"first", 1));
EXPECT_FALSE(dictionary.Insert(L"second", 2));
EXPECT_TRUE(dictionary.Insert(L"second", 4));
EXPECT_FALSE(dictionary.Insert(L"third", 4));
EXPECT_EQ(dictionary.Size(), 3);

EXPECT_TRUE(dictionary.HasKey(L"first"));
EXPECT_FALSE(dictionary.HasKey(L"fourth"));
EXPECT_TRUE(dictionary.HasKey(L"third"));

dictionary.Clear();
EXPECT_FALSE(dictionary.HasKey(L"first"));
EXPECT_FALSE(dictionary.HasKey(L"fourth"));
EXPECT_FALSE(dictionary.HasKey(L"third"));
}
Loading