[XamlC] resolve generic EventArgs (#1020)

This commit is contained in:
Stephane Delcroix 2017-07-24 16:28:19 -04:00 коммит произвёл GitHub
Родитель 171fa0faec
Коммит 56fc6218ff
5 изменённых файлов: 120 добавлений и 7 удалений

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

@ -764,14 +764,16 @@ namespace Xamarin.Forms.Build.Tasks
static bool CanConnectEvent(VariableDefinition parent, string localName)
{
return parent.VariableType.GetEvent(ed => ed.Name == localName) != null;
TypeReference _;
return parent.VariableType.GetEvent(ed => ed.Name == localName, out _) != null;
}
static IEnumerable<Instruction> ConnectEvent(VariableDefinition parent, string localName, INode valueNode, IXmlLineInfo iXmlLineInfo, ILContext context)
{
var elementType = parent.VariableType;
var module = context.Body.Method.Module;
var eventinfo = elementType.GetEvent(ed => ed.Name == localName);
TypeReference eventDeclaringTypeRef;
var eventinfo = elementType.GetEvent(ed => ed.Name == localName, out eventDeclaringTypeRef);
// IL_0007: ldloc.0
// IL_0008: ldarg.0
@ -810,7 +812,9 @@ namespace Xamarin.Forms.Build.Tasks
var ctor = module.ImportReference(eventinfo.EventType.Resolve().GetConstructors().First());
ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
yield return Instruction.Create(OpCodes.Newobj, module.ImportReference(ctor));
yield return Instruction.Create(OpCodes.Callvirt, module.ImportReference(eventinfo.AddMethod));
var adder = module.ImportReference(eventinfo.AddMethod);
adder = adder.ResolveGenericParameters(eventDeclaringTypeRef, module);
yield return Instruction.Create(OpCodes.Callvirt, module.ImportReference(adder));
}
static bool CanSetDynamicResource(FieldReference bpRef, INode valueNode, ILContext context)

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

@ -56,17 +56,42 @@ namespace Xamarin.Forms.Build.Tasks
return typeDef.BaseType.GetProperty(predicate, out declaringTypeRef);
}
public static EventDefinition GetEvent(this TypeReference typeRef, Func<EventDefinition, bool> predicate)
public static EventDefinition GetEvent(this TypeReference typeRef, Func<EventDefinition, bool> predicate,
out TypeReference declaringTypeRef)
{
declaringTypeRef = typeRef;
var typeDef = typeRef.Resolve();
var events = typeDef.Events.Where(predicate);
if (events.Any())
return events.Single();
if (events.Any()) {
var ev = events.Single();
return ev.ResolveGenericEvent(declaringTypeRef);
}
if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object")
return null;
return typeDef.BaseType.GetEvent(predicate);
return typeDef.BaseType.GetEvent(predicate, out declaringTypeRef);
}
//this resolves generic eventargs (https://bugzilla.xamarin.com/show_bug.cgi?id=57574)
static EventDefinition ResolveGenericEvent(this EventDefinition eventDef, TypeReference declaringTypeRef)
{
if (eventDef == null)
throw new ArgumentNullException(nameof(eventDef));
if (declaringTypeRef == null)
throw new ArgumentNullException(nameof(declaringTypeRef));
if (!eventDef.EventType.IsGenericInstance)
return eventDef;
if (eventDef.EventType.Resolve().FullName != "System.EventHandler`1")
return eventDef;
var git = eventDef.EventType as GenericInstanceType;
var ga = git.GenericArguments.First();
ga = ga.ResolveGenericParameters(declaringTypeRef);
git.GenericArguments[0] = ga;
eventDef.EventType = git;
return eventDef;
}
public static FieldDefinition GetField(this TypeReference typeRef, Func<FieldDefinition, bool> predicate,
out TypeReference declaringTypeRef)
{

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

@ -0,0 +1,9 @@
<?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.Bz57574">
<local:Bz57574Notificator x:Name="notificator"
x:TypeArguments="x:String"
Notified="OnNotified"/>
</ContentPage>

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

@ -0,0 +1,69 @@
using System;
using NUnit.Framework;
using Xamarin.Forms.Core.UnitTests;
namespace Xamarin.Forms.Xaml.UnitTests
{
public class Bz57574NotificationEventArgs<T> : EventArgs
{
public T Message { get; set; }
}
public class Bz57574Notificator<T> : View
{
public void Notify(T message)
{
Notified?.Invoke(this, new Bz57574NotificationEventArgs<T> { Message = message });
}
public event EventHandler<Bz57574NotificationEventArgs<T>> Notified;
}
public partial class Bz57574
{
public Bz57574()
{
notificator.Notified += OnNotified;
InitializeComponent();
}
public Bz57574(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}
string notification;
public void OnNotified(object sender, Bz57574NotificationEventArgs<string> args)
{
notification = args.Message;
}
[TestFixture]
class Tests
{
[SetUp]
public void Setup()
{
Device.PlatformServices = new MockPlatformServices();
}
[TearDown]
public void TearDown()
{
Device.PlatformServices = null;
Application.Current = null;
}
[TestCase(true)]
[TestCase(false)]
public void EventWithGenericEventHandlers(bool useCompiledXaml)
{
var layout = new Bz57574(useCompiledXaml);
Assume.That(layout.notification, Is.Null);
layout.notificator.Notify("Foo");
Assert.That(layout.notification, Is.EqualTo("Foo"));
}
}
}
}

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

@ -495,6 +495,9 @@
<DependentUpon>Bz53350.xaml</DependentUpon>
</Compile>
<Compile Include="XamlC\MethodDefinitionExtensionsTests.cs" />
<Compile Include="Issues\Bz57574.xaml.cs">
<DependentUpon>Bz57574.xaml</DependentUpon>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
@ -905,6 +908,9 @@
</EmbeddedResource>
<EmbeddedResource Include="Issues\Bz53350.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="Issues\Bz57574.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>