From 56fc6218fff87b6b22c6b2ff3856972e891c7a01 Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Mon, 24 Jul 2017 16:28:19 -0400 Subject: [PATCH] [XamlC] resolve generic EventArgs (#1020) --- .../SetPropertiesVisitor.cs | 10 ++- .../TypeReferenceExtensions.cs | 33 +++++++-- .../Issues/Bz57574.xaml | 9 +++ .../Issues/Bz57574.xaml.cs | 69 +++++++++++++++++++ .../Xamarin.Forms.Xaml.UnitTests.csproj | 6 ++ 5 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml.cs diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs index f7da7689a..873d3436b 100644 --- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs @@ -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 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) diff --git a/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs b/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs index d40b5be4b..f8a489b5e 100644 --- a/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs +++ b/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs @@ -56,17 +56,42 @@ namespace Xamarin.Forms.Build.Tasks return typeDef.BaseType.GetProperty(predicate, out declaringTypeRef); } - public static EventDefinition GetEvent(this TypeReference typeRef, Func predicate) + public static EventDefinition GetEvent(this TypeReference typeRef, Func 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 predicate, out TypeReference declaringTypeRef) { diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml new file mode 100644 index 000000000..98b9aab0d --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml.cs new file mode 100644 index 000000000..85269c47c --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz57574.xaml.cs @@ -0,0 +1,69 @@ +using System; +using NUnit.Framework; +using Xamarin.Forms.Core.UnitTests; + +namespace Xamarin.Forms.Xaml.UnitTests +{ + public class Bz57574NotificationEventArgs : EventArgs + { + public T Message { get; set; } + } + + public class Bz57574Notificator : View + { + public void Notify(T message) + { + Notified?.Invoke(this, new Bz57574NotificationEventArgs { Message = message }); + } + + public event EventHandler> 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 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")); + } + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj index b168a7a49..7f2895c25 100644 --- a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj +++ b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj @@ -495,6 +495,9 @@ Bz53350.xaml + + Bz57574.xaml + @@ -905,6 +908,9 @@ MSBuild:UpdateDesignTimeXaml + + + MSBuild:UpdateDesignTimeXaml