зеркало из https://github.com/DeGsoft/maui-linux.git
[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:
Родитель
28b343b278
Коммит
c096037329
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче