summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephane Delcroix <stephane@delcroix.org>2017-06-21 10:32:58 +0200
committerGitHub <noreply@github.com>2017-06-21 10:32:58 +0200
commitc09603732916be56d27f21cea4ef1370057414bc (patch)
tree08e63c73ef6b04120e9a9b034a99d9f3d9c469d0
parent28b343b2788fa073a3d3a87610275d8f3e756b61 (diff)
downloadxamarin-forms-c09603732916be56d27f21cea4ef1370057414bc.tar.gz
xamarin-forms-c09603732916be56d27f21cea4ef1370057414bc.tar.bz2
xamarin-forms-c09603732916be56d27f21cea4ef1370057414bc.zip
[XamlC] Allow Properties and BP of generic types (#995)
* [XamlC] Allow Properties and BP of generic types * try hard to convince the compiler that the variable is initialized * Update MethodDefinitionExtensions.cs
-rw-r--r--Xamarin.Forms.Build.Tasks/BindablePropertyReferenceExtensions.cs16
-rw-r--r--Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs15
-rw-r--r--Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs14
-rw-r--r--Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs4
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml7
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml.cs63
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj7
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/XamlC/MethodDefinitionExtensionsTests.cs61
-rw-r--r--Xamarin.Forms.Xaml.UnitTests/XamlC/PropertyDefinitionExtensionsTests.cs71
9 files changed, 201 insertions, 57 deletions
diff --git a/Xamarin.Forms.Build.Tasks/BindablePropertyReferenceExtensions.cs b/Xamarin.Forms.Build.Tasks/BindablePropertyReferenceExtensions.cs
index 0edc5f59..014f9d62 100644
--- a/Xamarin.Forms.Build.Tasks/BindablePropertyReferenceExtensions.cs
+++ b/Xamarin.Forms.Build.Tasks/BindablePropertyReferenceExtensions.cs
@@ -17,9 +17,9 @@ namespace Xamarin.Forms.Build.Tasks
throw new XamlParseException($"The name of the bindable property {bpRef.Name} does not ends with \"Property\". This is the kind of convention the world is build upon, a bit like Planck's constant.", iXmlLineInfo);
var bpName = bpRef.Name.Substring(0, bpRef.Name.Length - 8);
var owner = bpRef.DeclaringType;
- TypeReference _;
+ TypeReference declaringTypeRef = null;
- var getter = owner.GetProperty(pd => pd.Name == bpName, out _)?.GetMethod;
+ var getter = owner.GetProperty(pd => pd.Name == bpName, out declaringTypeRef)?.GetMethod;
if (getter == null || getter.IsStatic || !getter.IsPublic)
getter = null;
getter = getter ?? owner.GetMethods(md => md.Name == $"Get{bpName}" &&
@@ -27,18 +27,18 @@ namespace Xamarin.Forms.Build.Tasks
md.IsPublic &&
md.Parameters.Count == 1 &&
md.Parameters[0].ParameterType.InheritsFromOrImplements(module.ImportReference(typeof(BindableObject))), module).SingleOrDefault()?.Item1;
-
if (getter == null)
throw new XamlParseException($"Missing a public static Get{bpName} or a public instance property getter for the attached property \"{bpRef.DeclaringType}.{bpRef.Name}\"", iXmlLineInfo);
- return getter.ReturnType;
+ return getter.ResolveGenericReturnType(declaringTypeRef, module);
}
public static TypeReference GetBindablePropertyTypeConverter(this FieldReference bpRef, ModuleDefinition module)
{
- TypeReference _;
+ TypeReference propertyDeclaringType;
var owner = bpRef.DeclaringType;
var bpName = bpRef.Name.EndsWith("Property", StringComparison.Ordinal) ? bpRef.Name.Substring(0, bpRef.Name.Length - 8) : bpRef.Name;
- var property = owner.GetProperty(pd => pd.Name == bpName, out _);
+ var property = owner.GetProperty(pd => pd.Name == bpName, out propertyDeclaringType);
+ var propertyType = property?.ResolveGenericPropertyType(propertyDeclaringType, module);
var staticGetter = owner.GetMethods(md => md.Name == $"Get{bpName}" &&
md.IsStatic &&
md.IsPublic &&
@@ -48,8 +48,8 @@ namespace Xamarin.Forms.Build.Tasks
var attributes = new List<CustomAttribute>();
if (property != null && property.HasCustomAttributes)
attributes.AddRange(property.CustomAttributes);
- if (property != null && property.PropertyType.Resolve().HasCustomAttributes)
- attributes.AddRange(property.PropertyType.Resolve().CustomAttributes);
+ if (propertyType != null && propertyType.Resolve().HasCustomAttributes)
+ attributes.AddRange(propertyType.Resolve().CustomAttributes);
if (staticGetter != null && staticGetter.HasCustomAttributes)
attributes.AddRange(staticGetter.CustomAttributes);
if (staticGetter != null && staticGetter.ReturnType.Resolve().HasCustomAttributes)
diff --git a/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs b/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs
index 4b50b64d..6c6c7d88 100644
--- a/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs
+++ b/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs
@@ -38,5 +38,18 @@ namespace Xamarin.Forms.Build.Tasks
}
return true;
}
+
+ public static TypeReference ResolveGenericReturnType(this MethodDefinition self, TypeReference declaringTypeRef, ModuleDefinition module)
+ {
+ if (self == null)
+ throw new System.ArgumentNullException(nameof(self));
+ if (declaringTypeRef == null)
+ throw new System.ArgumentNullException(nameof(declaringTypeRef));
+ if (!self.ReturnType.IsGenericParameter)
+ return self.ReturnType;
+
+ var t = ((GenericInstanceType)declaringTypeRef).GenericArguments[((GenericParameter)self.ReturnType).Position];
+ return t;
+ }
}
-} \ No newline at end of file
+}
diff --git a/Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs b/Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs
index 7a56c9d1..8cbbbe4a 100644
--- a/Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs
+++ b/Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs
@@ -5,11 +5,17 @@ namespace Xamarin.Forms.Build.Tasks
{
static class PropertyDefinitionExtensions
{
- public static TypeReference ResolveGenericPropertyType(this PropertyDefinition self, TypeReference declaringTypeReference)
+ public static TypeReference ResolveGenericPropertyType(this PropertyDefinition self, TypeReference declaringTypeRef,
+ ModuleDefinition module)
{
- if (self.PropertyType.IsGenericParameter)
- return ((GenericInstanceType)declaringTypeReference).GenericArguments [((GenericParameter)self.PropertyType).Position];
- return self.PropertyType;
+ if (self == null)
+ throw new ArgumentNullException(nameof(self));
+ if (declaringTypeRef == null)
+ throw new ArgumentNullException(nameof(declaringTypeRef));
+ if (!self.PropertyType.IsGenericParameter)
+ return self.PropertyType;
+
+ return ((GenericInstanceType)declaringTypeRef).GenericArguments [((GenericParameter)self.PropertyType).Position];
}
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
index 5cbf075d..f7da7689 100644
--- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
+++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
@@ -967,7 +967,7 @@ namespace Xamarin.Forms.Build.Tasks
return false;
var vardef = context.Variables [elementNode];
- var propertyType = property.ResolveGenericPropertyType(declaringTypeReference);
+ var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(propertyType, module);
if (implicitOperator != null)
@@ -998,7 +998,7 @@ namespace Xamarin.Forms.Build.Tasks
module.ImportReference(parent.VariableType.Resolve());
var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
propertySetterRef.ImportTypes(module);
- var propertyType = property.ResolveGenericPropertyType(declaringTypeReference);
+ var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
var valueNode = node as ValueNode;
var elementNode = node as IElementNode;
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml
new file mode 100644
index 00000000..ad664ba0
--- /dev/null
+++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Xamarin.Forms.Xaml.UnitTests"
+ x:Class="Xamarin.Forms.Xaml.UnitTests.Bz53350">
+ <local:Bz53350String x:Name="content" SomeBP="Foo" SomeProperty="Bar" />
+</ContentPage> \ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml.cs
new file mode 100644
index 00000000..f25c018a
--- /dev/null
+++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz53350.xaml.cs
@@ -0,0 +1,63 @@
+using System;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UnitTests;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+ public class Bz53350Generic<T> : ContentView
+ {
+ public static readonly BindableProperty SomeBPProperty =
+ BindableProperty.Create("SomeBP", typeof(T), typeof(Bz53350Generic<T>), default(T));
+
+ public T SomeBP
+ {
+ get { return (T)GetValue(SomeBPProperty); }
+ set { SetValue(SomeBPProperty, value); }
+ }
+
+ public T SomeProperty { get; set; }
+ }
+
+ public class Bz53350String : Bz53350Generic<string>
+ {
+
+ }
+
+ public partial class Bz53350
+ {
+ public Bz53350()
+ {
+ }
+
+ public Bz53350(bool useCompiledXaml)
+ {
+ //this stub will be replaced at compile time
+ }
+
+ [TestFixture]
+ class Tests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ Device.PlatformServices = new MockPlatformServices();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ Application.Current = null;
+ Device.PlatformServices = null;
+ }
+
+ [TestCase(true)]
+ [TestCase(false)]
+ public void PropertiesWithGenericType(bool useCompiledXaml)
+ {
+ var layout = new Bz53350(useCompiledXaml);
+ Assert.That(layout.content.SomeBP, Is.EqualTo("Foo"));
+ Assert.That(layout.content.SomeProperty, Is.EqualTo("Bar"));
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
index d528379e..ba470e60 100644
--- a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
+++ b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
@@ -491,6 +491,10 @@
<Compile Include="Issues\Bz56852.xaml.cs">
<DependentUpon>Bz56852.xaml</DependentUpon>
</Compile>
+ <Compile Include="Issues\Bz53350.xaml.cs">
+ <DependentUpon>Bz53350.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="XamlC\MethodDefinitionExtensionsTests.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
@@ -899,6 +903,9 @@
<EmbeddedResource Include="Issues\Bz56852.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
+ <EmbeddedResource Include="Issues\Bz53350.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
diff --git a/Xamarin.Forms.Xaml.UnitTests/XamlC/MethodDefinitionExtensionsTests.cs b/Xamarin.Forms.Xaml.UnitTests/XamlC/MethodDefinitionExtensionsTests.cs
new file mode 100644
index 00000000..6133d727
--- /dev/null
+++ b/Xamarin.Forms.Xaml.UnitTests/XamlC/MethodDefinitionExtensionsTests.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Linq;
+
+using Mono.Cecil;
+
+using Xamarin.Forms.Build.Tasks;
+
+using NUnit.Framework;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Xaml.XamlcUnitTests
+{
+ [TestFixture]
+ public class MethodDefinitionExtensionsTests
+ {
+ public class NonGenericClass
+ {
+ public object Property { get; set; }
+ }
+
+ public class GenericClass<T, U, V>
+ {
+ public object NonGeneric() => default(object);
+ public T GenericT() => default(T);
+ public U GenericU() => default(U);
+ public V GenericV() => default(V);
+ public IEnumerable<T> EnumerableT() => default(IEnumerable<T>);
+ public KeyValuePair<V, U> KvpVU() => default(KeyValuePair<V,U>);
+ }
+
+ ModuleDefinition module;
+
+ [SetUp]
+ public void SetUp()
+ {
+ module = ModuleDefinition.CreateModule("foo", ModuleKind.Dll);
+ }
+
+ [Test]
+ public void ResolveGenericReturnType()
+ {
+ var type = module.ImportReference(typeof(GenericClass<bool, string, int>));
+
+ var getter = type.GetMethods(md => md.Name == "NonGeneric", module).Single();
+ var returnType = getter.Item1.ResolveGenericReturnType(getter.Item2, module);
+ Assert.AreEqual("System.Object", returnType.FullName);
+
+ getter = type.GetMethods(md => md.Name == "GenericT", module).Single();
+ returnType = getter.Item1.ResolveGenericReturnType(getter.Item2, module);
+ Assert.AreEqual("System.Boolean", returnType.FullName);
+
+ getter = type.GetMethods(md => md.Name == "GenericU", module).Single();
+ returnType = getter.Item1.ResolveGenericReturnType(getter.Item2, module);
+ Assert.AreEqual("System.String", returnType.FullName);
+
+ getter = type.GetMethods(md => md.Name == "GenericV", module).Single();
+ returnType = getter.Item1.ResolveGenericReturnType(getter.Item2, module);
+ Assert.AreEqual("System.Int32", returnType.FullName);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/XamlC/PropertyDefinitionExtensionsTests.cs b/Xamarin.Forms.Xaml.UnitTests/XamlC/PropertyDefinitionExtensionsTests.cs
index b43e4c9d..e8e56afa 100644
--- a/Xamarin.Forms.Xaml.UnitTests/XamlC/PropertyDefinitionExtensionsTests.cs
+++ b/Xamarin.Forms.Xaml.UnitTests/XamlC/PropertyDefinitionExtensionsTests.cs
@@ -6,6 +6,7 @@ using Mono.Cecil;
using Xamarin.Forms.Build.Tasks;
using NUnit.Framework;
+using System.Collections.Generic;
namespace Xamarin.Forms.Xaml.XamlcUnitTests
{
@@ -17,10 +18,14 @@ namespace Xamarin.Forms.Xaml.XamlcUnitTests
public object Property { get; set; }
}
- public class GenericClass<T>
+ public class GenericClass<T, U, V>
{
public object Property { get; set; }
- public T GenericProperty { get; set; }
+ public T GenericT { get; set; }
+ public U GenericU { get; set; }
+ public V GenericV { get; set; }
+ public IEnumerable<T> EnumerableT { get; set; }
+ public KeyValuePair<V, U> KvpVU { get; set; }
}
ModuleDefinition module;
@@ -31,45 +36,27 @@ namespace Xamarin.Forms.Xaml.XamlcUnitTests
module = ModuleDefinition.CreateModule ("foo", ModuleKind.Dll);
}
-// [Test]
-// public void ResolveGenericsOnNonGenericPreserveAccessors ()
-// {
-// var type = module.Import (typeof (NonGenericClass));
-// TypeReference declaringTypeReference;
-// PropertyDefinition prop = type.GetProperty (fd => fd.Name == "Property", out declaringTypeReference);
-// Assert.AreEqual ("System.Object", prop.PropertyType.FullName);
-// Assert.AreEqual ("System.Void Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/NonGenericClass::set_Property(System.Object)", prop.SetMethod.FullName);
-// Assert.AreEqual ("System.Object Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/NonGenericClass::get_Property()", prop.GetMethod.FullName);
-// Assert.AreEqual ("Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/NonGenericClass", prop.DeclaringType.FullName);
-//
-// prop.ResolveGenericParameters (declaringTypeReference);
-//
-// Assert.AreEqual ("System.Object", prop.PropertyType.FullName);
-// Assert.AreEqual ("System.Void Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/NonGenericClass::set_Property(System.Object)", prop.SetMethod.FullName);
-// Assert.AreEqual ("System.Object Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/NonGenericClass::get_Property()", prop.GetMethod.FullName);
-// Assert.AreEqual ("Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/NonGenericClass", prop.DeclaringType.FullName);
-//
-// }
-//
-// [Test]
-// public void NonGenericPropertyOnGenericType ()
-// {
-// var type = module.Import (typeof (GenericClass<bool>));
-// TypeReference declaringTypeReference;
-// PropertyDefinition prop = type.GetProperty (fd => fd.Name == "Property", out declaringTypeReference);
-// Assert.AreEqual ("System.Object", prop.PropertyType.FullName);
-// Assert.AreEqual ("System.Void Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/GenericClass`1::set_Property(System.Object)", prop.SetMethod.FullName);
-// Assert.AreEqual ("System.Object Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/GenericClass`1::get_Property()", prop.GetMethod.FullName);
-// Assert.AreEqual ("Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/GenericClass`1", prop.DeclaringType.FullName);
-// Assert.False (prop.DeclaringType.IsGenericInstance);
-//
-// prop.ResolveGenericParameters (declaringTypeReference);
-// Assert.AreEqual ("System.Object", prop.PropertyType.FullName);
-// Assert.AreEqual ("System.Void Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/GenericClass`1::set_Property(System.Object)", prop.SetMethod.FullName);
-// Assert.AreEqual ("System.Object Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/GenericClass`1::get_Property()", prop.GetMethod.FullName);
-// Assert.AreEqual ("Xamarin.Forms.Xaml.XamlcUnitTests.PropertyDefinitionExtensionsTests/GenericClass`1", prop.DeclaringType.FullName);
-// Assert.True (prop.DeclaringType.IsGenericInstance);
-//
-// }
+ [Test]
+ public void ResolveGenericPropertyType ()
+ {
+ var type = module.ImportReference (typeof (GenericClass<bool, string, int>));
+ TypeReference declaringTypeReference;
+ var prop = type.GetProperty (fd => fd.Name == "Property", out declaringTypeReference);
+ var propertyType = prop.ResolveGenericPropertyType (declaringTypeReference, module);
+ Assert.AreEqual ("System.Object", propertyType.FullName);
+
+ prop = type.GetProperty(fd => fd.Name == "GenericT", out declaringTypeReference);
+ propertyType = prop.ResolveGenericPropertyType(declaringTypeReference, module);
+ Assert.AreEqual("System.Boolean", propertyType.FullName);
+
+ prop = type.GetProperty(fd => fd.Name == "GenericU", out declaringTypeReference);
+ propertyType = prop.ResolveGenericPropertyType(declaringTypeReference, module);
+ Assert.AreEqual("System.String", propertyType.FullName);
+
+ prop = type.GetProperty(fd => fd.Name == "GenericV", out declaringTypeReference);
+ propertyType = prop.ResolveGenericPropertyType(declaringTypeReference, module);
+ Assert.AreEqual("System.Int32", propertyType.FullName);
+
+ }
}
} \ No newline at end of file