[XamlC] Identify generic instance types correctly when importing ctor (#5309)
authorAkihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp>
Fri, 22 Feb 2019 00:56:42 +0000 (09:56 +0900)
committerSamantha Houts <samhouts@users.noreply.github.com>
Fri, 22 Feb 2019 00:56:42 +0000 (16:56 -0800)
The old implementation fails to identify generic instance types for typed
binding getter, whose type is:
System.Func`2<TSource, ValueTuple<TProperty, bool>>

This fixes the issue by taking generic arguments into account.

Xamarin.Forms.Build.Tasks/ModuleDefinitionExtensions.cs
Xamarin.Forms.Xaml.UnitTests/XamlC/ModuleDefinitionExtensionsTests.cs [new file with mode: 0644]

index 29693e8..b8e08ad 100644 (file)
@@ -47,7 +47,7 @@ namespace Xamarin.Forms.Build.Tasks
 
                public static MethodReference ImportCtorReference(this ModuleDefinition module, TypeReference type, TypeReference[] parameterTypes)
                {
-                       var ctorKey = $"{type}.ctor({(parameterTypes == null ? "" : string.Join(",", parameterTypes.Select(tr => (tr.Scope.Name, tr.Namespace, tr.Name))))})";
+                       var ctorKey = $"{type}.ctor({(parameterTypes == null ? "" : string.Join(",", parameterTypes.Select(SerializeTypeReference)))})";
                        if (MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
                                return ctorRef;
                        ctorRef = module.ImportCtorReference(type, classArguments: null, predicate: md => {
@@ -88,7 +88,7 @@ namespace Xamarin.Forms.Build.Tasks
 
                public static MethodReference ImportCtorReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, int paramCount, TypeReference[] classArguments)
                {
-                       var ctorKey = $"{type}<{(string.Join(",", classArguments.Select(tr => (tr.Scope.Name, tr.Namespace, tr.Name))))}>.ctor({(string.Join(",", Enumerable.Repeat("_", paramCount)))})";
+                       var ctorKey = $"{type}<{string.Join(",", classArguments.Select(SerializeTypeReference))}>.ctor({(string.Join(",", Enumerable.Repeat("_", paramCount)))})";
                        if (!MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
                                MethodRefCache.Add((module, ctorKey), ctorRef = module.ImportCtorReference(module.GetTypeDefinition(type), classArguments, predicate: md => md.Parameters.Count == paramCount));
                        return ctorRef;
@@ -303,5 +303,12 @@ namespace Xamarin.Forms.Build.Tasks
                        foreach (var property in typedef.BaseType.ResolveCached().Properties(true))
                                yield return property;
                }
+
+               static string SerializeTypeReference(TypeReference tr)
+               {
+                       var serialized = $"{tr.Scope.Name},{tr.Namespace},{tr.Name}";
+                       var gitr = tr as GenericInstanceType;
+                       return gitr == null ? serialized : $"{serialized}<{string.Join(",", gitr.GenericArguments.Select(SerializeTypeReference))}>";
+               }
        }
 }
\ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/XamlC/ModuleDefinitionExtensionsTests.cs b/Xamarin.Forms.Xaml.UnitTests/XamlC/ModuleDefinitionExtensionsTests.cs
new file mode 100644 (file)
index 0000000..5fb3719
--- /dev/null
@@ -0,0 +1,62 @@
+using System;
+using NUnit.Framework;
+using Mono.Cecil;
+using Xamarin.Forms.Build.Tasks;
+
+namespace Xamarin.Forms.XamlcUnitTests
+{
+       [TestFixture]
+       public class ModuleDefinitionExtensionsTests
+       {
+               class WithGenericInstanceCtorParameter
+               {
+                       public WithGenericInstanceCtorParameter(Tuple<byte> argument)
+                       {
+                       }
+
+                       public WithGenericInstanceCtorParameter(Tuple<short> argument)
+                       {
+                       }
+               }
+
+               ModuleDefinition module;
+
+               [SetUp]
+               public void SetUp()
+               {
+                       var resolver = new XamlCAssemblyResolver();
+                       resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(ModuleDefinitionExtensionsTests).Assembly.CodeBase)).Path));
+                       resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(byte).Assembly.CodeBase)).Path));
+
+                       module = ModuleDefinition.CreateModule("foo", new ModuleParameters {
+                               AssemblyResolver = resolver,
+                               Kind = ModuleKind.Dll
+                       });
+               }
+
+               [Test]
+               public void TestImportCtorReferenceWithGenericInstanceCtorParameter()
+               {
+                       var type = module.ImportReference(typeof(WithGenericInstanceCtorParameter));
+                       var byteTuple = module.ImportReference(typeof(Tuple<byte>));
+                       var byteTupleCtor = module.ImportCtorReference(type, new[] { byteTuple });
+                       var int16Tuple = module.ImportReference(typeof(Tuple<short>));
+                       var int16TupleCtor = module.ImportCtorReference(type, new[] { int16Tuple });
+
+                       Assert.AreEqual("System.Tuple`1<System.Byte>", byteTupleCtor.Parameters[0].ParameterType.FullName);
+                       Assert.AreEqual("System.Tuple`1<System.Int16>", int16TupleCtor.Parameters[0].ParameterType.FullName);
+               }
+
+               [Test]
+               public void TestImportCtorReferenceWithGenericInstanceTypeParameter()
+               {
+                       var byteTuple = module.ImportReference(typeof(Tuple<byte>));
+                       var byteTupleCtor = module.ImportCtorReference(("mscorlib", "System", "Tuple`1"), 1, new[] { byteTuple });
+                       var in16Tuple = module.ImportReference(typeof(Tuple<short>));
+                       var int16TupleCtor = module.ImportCtorReference(("mscorlib", "System", "Tuple`1"), 1, new[] { in16Tuple });
+
+                       Assert.AreEqual("System.Tuple`1<System.Byte>", ((GenericInstanceType)byteTupleCtor.DeclaringType).GenericArguments[0].FullName);
+                       Assert.AreEqual("System.Tuple`1<System.Int16>", ((GenericInstanceType)int16TupleCtor.DeclaringType).GenericArguments[0].FullName);
+               }
+       }
+}