[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
This commit is contained in:
Stephane Delcroix 2017-06-21 10:32:58 +02:00 коммит произвёл GitHub
Родитель 28b343b278
Коммит c096037329
9 изменённых файлов: 201 добавлений и 57 удалений

Просмотреть файл

@ -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)

Просмотреть файл

@ -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;
}
}
}
}

Просмотреть файл

@ -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];
}
}
}

Просмотреть файл

@ -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;

Просмотреть файл

@ -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>

Просмотреть файл

@ -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"));
}
}
}
}

Просмотреть файл

@ -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" />
@ -898,6 +902,9 @@
</EmbeddedResource>
<EmbeddedResource Include="Issues\Bz56852.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="Issues\Bz53350.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -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);
}
}
}

Просмотреть файл

@ -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);
}
}
}