From 86a48cf7d583539744c7f82611e5b02f4e636fae Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 11 Jun 2019 19:08:25 +0300 Subject: [PATCH 1/4] Fixed #2561 --- src/Avalonia.Build.Tasks/Program.cs | 11 ++++- .../XamlIlAvaloniaPropertyHelper.cs | 2 +- .../Xaml/XamlIlTests.cs | 42 +++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Build.Tasks/Program.cs b/src/Avalonia.Build.Tasks/Program.cs index c2d0950264..d356b15408 100644 --- a/src/Avalonia.Build.Tasks/Program.cs +++ b/src/Avalonia.Build.Tasks/Program.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.IO; +using System.Linq; using Microsoft.Build.Framework; namespace Avalonia.Build.Tasks @@ -11,8 +12,14 @@ namespace Avalonia.Build.Tasks { if (args.Length != 3) { - Console.Error.WriteLine("input references output"); - return 1; + if (args.Length == 1) + args = new[] {"original.dll", "references", "out.dll"} + .Select(x => Path.Combine(args[0], x)).ToArray(); + else + { + Console.Error.WriteLine("input references output"); + return 1; + } } return new CompileAvaloniaXamlTask() diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 452bb05132..34a79a4cdc 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -64,7 +64,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions var tref = XamlIlTypeReferenceResolver.ResolveType(context, xmlOwner, false, lineInfo, true); forgedReference = new XamlIlAstNamePropertyReference(lineInfo, - tref, parsedPropertyName.name, tref); + tref, parsedPropertyName.name, selectorTypeReference); } var clrProperty = diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index 5e346e5289..b2cc44291d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -207,6 +207,33 @@ namespace Avalonia.Markup.Xaml.UnitTests xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'/>", typeof(XamlIlTests).Assembly); Assert.Equal(Design.GetDataContext(loaded), SomeStaticProperty); } + + [Fact] + public void Attached_Properties_From_Static_Types_Should_Work_In_Style_Setters_Bug_2561() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + + var parsed = (Window)AvaloniaXamlLoader.Parse(@" + + + + + + + +"); + var tb = ((TextBox)parsed.Content); + parsed.Show(); + tb.ApplyTemplate(); + Assert.Equal(100, XamlIlBugTestsStaticClassWithAttachedProperty.GetTestInt(tb)); + } + } } public class XamlIlBugTestsEventHandlerCodeBehind : Window @@ -272,4 +299,19 @@ namespace Avalonia.Markup.Xaml.UnitTests { } + public static class XamlIlBugTestsStaticClassWithAttachedProperty + { + public static readonly AvaloniaProperty TestIntProperty = AvaloniaProperty + .RegisterAttached("TestInt", typeof(XamlIlBugTestsStaticClassWithAttachedProperty)); + + public static void SetTestInt(Control control, int value) + { + control.SetValue(TestIntProperty, value); + } + + public static int GetTestInt(Control control) + { + return (int)control.GetValue(TestIntProperty); + } + } } From 143b6a3476537e27ed61424258d11947f94c219c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 11 Jun 2019 21:10:41 +0300 Subject: [PATCH 2/4] Explicitly ignore Setter's property type for animations --- .../AvaloniaXamlIlLanguage.cs | 2 +- .../AvaloniaXamlIlSetterTransformer.cs | 27 ++++++-- .../AvaloniaXamlIlWellKnownTypes.cs | 2 + .../XamlIlAvaloniaPropertyHelper.cs | 66 ++++++++++++++++++- 4 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 581dbcdac7..d5cc93ef66 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -165,7 +165,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlIlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node); if (!(node is XamlIlAstTextNode text)) throw new XamlIlLoadException("Property should be a text node", node); - result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text); + result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text, false); return true; } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs index 0e7ea34bb7..d548d3a188 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs @@ -16,8 +16,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (!(node is XamlIlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Setter")) return node; - var parent = context.ParentNodes().OfType() - .FirstOrDefault(x => x.Type.GetClrType().FullName == "Avalonia.Styling.Style"); + + // This is a hack required to get complex animations (which are also a hack) to work + var inAnimation = false; + + XamlIlAstObjectNode parent = null; + + foreach (var p in context.ParentNodes().OfType()) + { + if (p.Type.GetClrType().FullName == "Avalonia.Styling.Style") + { + parent = p; + break; + } + + if (p.Type.GetClrType().FullName == "Avalonia.Animation.Animation") + inAnimation = true; + } + if (parent == null) throw new XamlIlParseException( "Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node); @@ -43,7 +59,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName, - new XamlIlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0]); + new XamlIlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0], + // Hack to allow passing any property to an animation + inAnimation); property.Values = new List { avaloniaPropertyNode @@ -53,8 +71,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers .OfType().FirstOrDefault(p => p.Property.GetClrProperty().Name == "Value"); if (valueProperty?.Values?.Count == 1 && valueProperty.Values[0] is XamlIlAstTextNode) { - var propType = avaloniaPropertyNode.Property.Getter?.ReturnType - ?? avaloniaPropertyNode.Property.Setters.First().Parameters[0]; + var propType = avaloniaPropertyNode.AvaloniaPropertyType; if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, valueProperty.Values[0], propType, out var converted)) throw new XamlIlParseException( diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index b57c26c241..c054e57380 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -10,6 +10,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlIlType BindingPriority { get; } public IXamlIlType AvaloniaObjectExtensions { get; } public IXamlIlType AvaloniaProperty { get; } + public IXamlIlType AvaloniaPropertyT { get; } public IXamlIlType IBinding { get; } public IXamlIlMethod AvaloniaObjectBindMethod { get; } public IXamlIlMethod AvaloniaObjectSetValueMethod { get; } @@ -26,6 +27,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers IAvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.IAvaloniaObject"); AvaloniaObjectExtensions = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions"); AvaloniaProperty = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaProperty"); + AvaloniaPropertyT = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaProperty`1"); BindingPriority = ctx.Configuration.TypeSystem.GetType("Avalonia.Data.BindingPriority"); IBinding = ctx.Configuration.TypeSystem.GetType("Avalonia.Data.IBinding"); IDisposable = ctx.Configuration.TypeSystem.GetType("System.IDisposable"); diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 34a79a4cdc..bf3f8958b3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -44,8 +44,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return true; } - public static XamlIlAvaloniaPropertyNode CreateNode(XamlIlAstTransformationContext context, - string propertyName, IXamlIlAstTypeReference selectorTypeReference, IXamlIlLineInfo lineInfo) + public static IXamlIlAvaloniaPropertyNode CreateNode(XamlIlAstTransformationContext context, + string propertyName, IXamlIlAstTypeReference selectorTypeReference, IXamlIlLineInfo lineInfo, + bool ignoreAttachedTargetType) { XamlIlAstNamePropertyReference forgedReference; @@ -63,6 +64,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions xmlOwner += parsedPropertyName.owner; var tref = XamlIlTypeReferenceResolver.ResolveType(context, xmlOwner, false, lineInfo, true); + + if (ignoreAttachedTargetType) + { + var propertyFieldName = parsedPropertyName.name + "Property"; + var found = tref.Type.GetAllFields() + .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == propertyFieldName); + if (found == null) + throw new XamlIlParseException( + $"Unable to find {propertyFieldName} field on type {tref.Type.GetFullName()}", lineInfo); + return new XamlIlAvaloniaPropertyFieldNode(context.GetAvaloniaTypes(), lineInfo, found); + } + forgedReference = new XamlIlAstNamePropertyReference(lineInfo, tref, parsedPropertyName.name, selectorTypeReference); } @@ -75,13 +88,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions clrProperty); } } + + interface IXamlIlAvaloniaPropertyNode : IXamlIlAstValueNode + { + IXamlIlType AvaloniaPropertyType { get; } + } - class XamlIlAvaloniaPropertyNode : XamlIlAstNode, IXamlIlAstValueNode, IXamlIlAstEmitableNode + class XamlIlAvaloniaPropertyNode : XamlIlAstNode, IXamlIlAstValueNode, IXamlIlAstEmitableNode, IXamlIlAvaloniaPropertyNode { public XamlIlAvaloniaPropertyNode(IXamlIlLineInfo lineInfo, IXamlIlType type, XamlIlAstClrProperty property) : base(lineInfo) { Type = new XamlIlAstClrTypeReference(this, type, false); Property = property; + AvaloniaPropertyType = Property.Getter?.ReturnType + ?? Property.Setters.First().Parameters[0]; } public XamlIlAstClrProperty Property { get; } @@ -93,6 +113,46 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlIlLoadException(Property.Name + " is not an AvaloniaProperty", this); return XamlIlNodeEmitResult.Type(0, Type.GetClrType()); } + + public IXamlIlType AvaloniaPropertyType { get; } + } + + class XamlIlAvaloniaPropertyFieldNode : XamlIlAstNode, IXamlIlAstValueNode, IXamlIlAstEmitableNode, IXamlIlAvaloniaPropertyNode + { + private readonly IXamlIlField _field; + + public XamlIlAvaloniaPropertyFieldNode(AvaloniaXamlIlWellKnownTypes types, + IXamlIlLineInfo lineInfo, IXamlIlField field) : base(lineInfo) + { + _field = field; + var avaloniaPropertyType = field.FieldType; + while (avaloniaPropertyType != null) + { + if (avaloniaPropertyType.GenericTypeDefinition?.Equals(types.AvaloniaPropertyT) == true) + { + AvaloniaPropertyType = avaloniaPropertyType.GenericArguments[0]; + return; + } + + avaloniaPropertyType = avaloniaPropertyType.BaseType; + } + + throw new XamlIlParseException( + $"{field.Name}'s type {field.FieldType} doesn't inherit from AvaloniaProperty, make sure to use typed properties", + lineInfo); + + } + + + + public IXamlIlAstTypeReference Type => new XamlIlAstClrTypeReference(this, _field.FieldType, false); + public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen) + { + codeGen.Ldsfld(_field); + return XamlIlNodeEmitResult.Type(0, _field.FieldType); + } + + public IXamlIlType AvaloniaPropertyType { get; } } interface IXamlIlAvaloniaProperty From 7a99e3118e8d2d027fe9d34197e09153b0c9fdaa Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 11 Jun 2019 21:27:57 +0300 Subject: [PATCH 3/4] Always ignore target type for fully-qualified avalonia properties --- .../AvaloniaXamlIlLanguage.cs | 2 +- .../AvaloniaXamlIlSetterTransformer.cs | 24 +++--------------- .../XamlIlAvaloniaPropertyHelper.cs | 25 +++++++------------ 3 files changed, 14 insertions(+), 37 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs index d5cc93ef66..581dbcdac7 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -165,7 +165,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlIlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node); if (!(node is XamlIlAstTextNode text)) throw new XamlIlLoadException("Property should be a text node", node); - result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text, false); + result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text); return true; } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs index d548d3a188..629e2562d3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs @@ -16,24 +16,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (!(node is XamlIlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Setter")) return node; - - // This is a hack required to get complex animations (which are also a hack) to work - var inAnimation = false; - - XamlIlAstObjectNode parent = null; - - foreach (var p in context.ParentNodes().OfType()) - { - if (p.Type.GetClrType().FullName == "Avalonia.Styling.Style") - { - parent = p; - break; - } - - if (p.Type.GetClrType().FullName == "Avalonia.Animation.Animation") - inAnimation = true; - } + var parent = context.ParentNodes().OfType() + .FirstOrDefault(p => p.Type.GetClrType().FullName == "Avalonia.Styling.Style"); + if (parent == null) throw new XamlIlParseException( "Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node); @@ -59,9 +45,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName, - new XamlIlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0], - // Hack to allow passing any property to an animation - inAnimation); + new XamlIlAstClrTypeReference(selector, selector.TargetType, false), property.Values[0]); property.Values = new List { avaloniaPropertyNode diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index bf3f8958b3..6fc83cb5a5 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -45,8 +45,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } public static IXamlIlAvaloniaPropertyNode CreateNode(XamlIlAstTransformationContext context, - string propertyName, IXamlIlAstTypeReference selectorTypeReference, IXamlIlLineInfo lineInfo, - bool ignoreAttachedTargetType) + string propertyName, IXamlIlAstTypeReference selectorTypeReference, IXamlIlLineInfo lineInfo) { XamlIlAstNamePropertyReference forgedReference; @@ -64,20 +63,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions xmlOwner += parsedPropertyName.owner; var tref = XamlIlTypeReferenceResolver.ResolveType(context, xmlOwner, false, lineInfo, true); - - if (ignoreAttachedTargetType) - { - var propertyFieldName = parsedPropertyName.name + "Property"; - var found = tref.Type.GetAllFields() - .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == propertyFieldName); - if (found == null) - throw new XamlIlParseException( - $"Unable to find {propertyFieldName} field on type {tref.Type.GetFullName()}", lineInfo); - return new XamlIlAvaloniaPropertyFieldNode(context.GetAvaloniaTypes(), lineInfo, found); - } - - forgedReference = new XamlIlAstNamePropertyReference(lineInfo, - tref, parsedPropertyName.name, selectorTypeReference); + + var propertyFieldName = parsedPropertyName.name + "Property"; + var found = tref.Type.GetAllFields() + .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == propertyFieldName); + if (found == null) + throw new XamlIlParseException( + $"Unable to find {propertyFieldName} field on type {tref.Type.GetFullName()}", lineInfo); + return new XamlIlAvaloniaPropertyFieldNode(context.GetAvaloniaTypes(), lineInfo, found); } var clrProperty = From 57e061366ed353fef31f9b429d06b4f08f1196a0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 11 Jun 2019 22:00:01 +0300 Subject: [PATCH 4/4] Use AvaloniaProperty in test class --- tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index b2cc44291d..5398e76f63 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -301,7 +301,7 @@ namespace Avalonia.Markup.Xaml.UnitTests public static class XamlIlBugTestsStaticClassWithAttachedProperty { - public static readonly AvaloniaProperty TestIntProperty = AvaloniaProperty + public static readonly AvaloniaProperty TestIntProperty = AvaloniaProperty .RegisterAttached("TestInt", typeof(XamlIlBugTestsStaticClassWithAttachedProperty)); public static void SetTestInt(Control control, int value)