[X] resolve generic indexer types (#5794)

- fixes #5510
This commit is contained in:
Stephane Delcroix 2019-04-03 15:19:33 +02:00 коммит произвёл GitHub
Родитель 38992485dc
Коммит ab164c0ede
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 127 добавлений и 34 удалений

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

@ -475,7 +475,7 @@ namespace Xamarin.Forms.Build.Tasks
var indexerName = defaultMemberAttribute?.ConstructorArguments?.FirstOrDefault().Value as string ?? "Item";
var indexer = previousPartTypeRef.GetProperty(pd => pd.Name == indexerName && pd.GetMethod != null && pd.GetMethod.IsPublic, out var indexerDeclTypeRef);
properties.Add((indexer, indexerDeclTypeRef, indexArg));
var indexType = indexer.GetMethod.Parameters[0].ParameterType;
var indexType = indexer.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(indexerDeclTypeRef);
if (!TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String) && !TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32))
throw new XamlParseException($"Binding: Unsupported indexer index type: {indexType.FullName}", lineInfo);
previousPartTypeRef = indexer.PropertyType.ResolveGenericParameters(indexerDeclTypeRef);
@ -536,7 +536,6 @@ namespace Xamarin.Forms.Build.Tasks
for (int i = 0; i < properties.Count; i++) {
(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg) = properties[i];
if (i > 0 && propDeclTypeRef.IsValueType) {
var importedPropDeclTypeRef = module.ImportReference(propDeclTypeRef);
@ -580,9 +579,10 @@ namespace Xamarin.Forms.Build.Tasks
}
if (indexArg != null) {
if (TypeRefComparer.Default.Equals(property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.String))
var indexType = property.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(propDeclTypeRef);
if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
il.Emit(Ldstr, indexArg);
else if (TypeRefComparer.Default.Equals(property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.Int32) && int.TryParse(indexArg, out int index))
else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32) && int.TryParse(indexArg, out int index))
il.Emit(Ldc_I4, index);
else
throw new XamlParseException($"Binding: {indexArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
@ -655,22 +655,20 @@ namespace Xamarin.Forms.Build.Tasks
else
il.Emit(Ldarg_0);
for (int i = 0; i < properties.Count - 1; i++) {
var property = properties[i].property;
var propDeclType = properties[i].propDeclTypeRef;
var indexerArg = properties[i].indexArg;
if (indexerArg != null) {
if (TypeRefComparer.Default.Equals(property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.String))
il.Emit(Ldstr, indexerArg);
else if (TypeRefComparer.Default.Equals(property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.Int32)) {
if (!int.TryParse(indexerArg, out var index))
throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg) = properties[i];
if (indexArg != null) {
var indexType = property.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(propDeclTypeRef);
if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
il.Emit(Ldstr, indexArg);
else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32)) {
if (!int.TryParse(indexArg, out var index))
throw new XamlParseException($"Binding: {indexArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
il.Emit(Ldc_I4, index);
}
}
var getMethod = module.ImportReference(property.GetMethod);
getMethod = module.ImportReference(getMethod.ResolveGenericParameters(propDeclType, module));
getMethod = module.ImportReference(getMethod.ResolveGenericParameters(propDeclTypeRef, module));
if (property.GetMethod.IsVirtual)
il.Emit(Callvirt, getMethod);
@ -678,13 +676,14 @@ namespace Xamarin.Forms.Build.Tasks
il.Emit(Call, getMethod);
}
var indexer = properties.Last().indexArg;
if (indexer != null) {
if(TypeRefComparer.Default.Equals(properties.Last().property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.String))
il.Emit(Ldstr, indexer);
else if (TypeRefComparer.Default.Equals(properties.Last().property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.Int32)) {
if (!int.TryParse(indexer, out int index))
throw new XamlParseException($"Binding: {indexer} could not be parsed as an index for a {properties.Last().property.Name}", node as IXmlLineInfo);
(PropertyDefinition lastProperty, TypeReference lastPropDeclTypeRef, string lastIndexArg) = properties.Last();
if (lastIndexArg != null) {
var indexType = lastProperty.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(lastPropDeclTypeRef);
if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
il.Emit(Ldstr, lastIndexArg);
else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32)) {
if (!int.TryParse(lastIndexArg, out int index))
throw new XamlParseException($"Binding: {lastIndexArg} could not be parsed as an index for a {lastProperty.Name}", node as IXmlLineInfo);
il.Emit(Ldc_I4, index);
}
}
@ -764,23 +763,20 @@ namespace Xamarin.Forms.Build.Tasks
il.Emit(Ldarg_0);
var lastGetterTypeRef = tSourceRef;
for (int j = 0; j < i; j++) {
var propTuple = properties [j];
var property = propTuple.property;
var indexerArg = propTuple.indexArg;
var propDeclType = propTuple.propDeclTypeRef;
if (indexerArg != null) {
if (TypeRefComparer.Default.Equals(property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.String))
il.Emit(Ldstr, indexerArg);
else if (TypeRefComparer.Default.Equals(property.GetMethod.Parameters[0].ParameterType, module.TypeSystem.Int32)) {
if (!int.TryParse(indexerArg, out var index))
throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg) = properties[j];
if (indexArg != null) {
var indexType = property.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(propDeclTypeRef);
if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
il.Emit(Ldstr, indexArg);
else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32)) {
if (!int.TryParse(indexArg, out var index))
throw new XamlParseException($"Binding: {indexArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
il.Emit(Ldc_I4, index);
}
}
var getMethod = module.ImportReference(property.GetMethod);
getMethod = module.ImportReference(getMethod.ResolveGenericParameters(propDeclType, module));
getMethod = module.ImportReference(getMethod.ResolveGenericParameters(propDeclTypeRef, module));
if (property.GetMethod.IsVirtual)
il.Emit(Callvirt, getMethod);

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

@ -0,0 +1,19 @@
<?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="using:Xamarin.Forms.Xaml.UnitTests"
x:Class="Xamarin.Forms.Xaml.UnitTests.Gh5510"
x:DataType="local:Gh5510VM">
<StackLayout>
<Label Text="Name"/>
<Entry Text="{Binding Name}" TextColor="Red" x:Name="entry">
<Entry.Triggers>
<DataTrigger TargetType="Entry" Binding="{Binding Errors[Name]}" Value="{x:Null}">
<Setter Property="TextColor" Value="Black" />
</DataTrigger>
</Entry.Triggers>
</Entry>
<Button Text="Clear error"/>
</StackLayout>
</ContentPage>

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

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.Forms.Core.UnitTests;
namespace Xamarin.Forms.Xaml.UnitTests
{
public class Gh5510VM : INotifyPropertyChanged
{
private string name = "Bill";
private Dictionary<string, string> errors;
public Gh5510VM()
{
errors = new Dictionary<string, string>
{
{ nameof(Name), "An error" }
};
}
public string Name {
get => name;
set => SetProperty(ref name, value);
}
public Dictionary<string, string> Errors {
get => errors;
private set => SetProperty(ref errors, value);
}
public event PropertyChangedEventHandler PropertyChanged;
public void ClearErrorForPerson() => Errors = new Dictionary<string, string>();
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(field, value))
return false;
field = value;
RaisePropertyChanged(propertyName);
return true;
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) => OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
}
public partial class Gh5510 : ContentPage
{
public Gh5510() => InitializeComponent();
public Gh5510(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() => Device.PlatformServices = null;
[Test]
public void CompileBindingWithIndexer([Values(false, true)]bool useCompiledXaml)
{
if (useCompiledXaml)
Assert.DoesNotThrow(() => MockCompiler.Compile(typeof(Gh5510)));
var vm = new Gh5510VM();
var layout = new Gh5510(useCompiledXaml) { BindingContext = vm };
Assert.That(layout.entry.TextColor, Is.EqualTo(Color.Red));
vm.ClearErrorForPerson();
Assert.That(layout.entry.TextColor, Is.EqualTo(Color.Black));
}
}
}
}