From a2c7f67c418fe93308ba4b4250de3e71a8cd79f9 Mon Sep 17 00:00:00 2001 From: Dmitry Zhelnin Date: Sat, 28 Jan 2023 15:45:08 +0300 Subject: [PATCH 1/2] throw XamlParseException if there are duplicate property setters in style or control theme --- .../AvaloniaXamlIlCompiler.cs | 1 + .../AvaloniaXamlIlDuplicateSettersChecker.cs | 44 +++++++++++++++++++ .../Xaml/XamlIlTests.cs | 41 +++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index aaaee39b0d..197815f9a0 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -49,6 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions InsertBefore( new AvaloniaXamlIlControlThemeTransformer(), new AvaloniaXamlIlSelectorTransformer(), + new AvaloniaXamlIlDuplicateSettersChecker(), new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(), new AvaloniaXamlIlBindingPathParser(), new AvaloniaXamlIlPropertyPathTransformer(), diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs new file mode 100644 index 0000000000..4f92fde3f0 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; +using XamlX; +using XamlX.Ast; +using XamlX.Transform; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; + +class AvaloniaXamlIlDuplicateSettersChecker : IXamlAstTransformer +{ + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + if (node is not XamlAstObjectNode objectNode) + { + return node; + } + + var fullName = objectNode.Type.GetClrType().FullName; + if (fullName is not ("Avalonia.Styling.Style" or "Avalonia.Styling.ControlTheme")) + { + return node; + } + + var properties = objectNode.Children + .OfType() + .Where(n => n.Type.GetClrType().Name == "Setter") + .SelectMany(setter => + setter.Children.OfType() + .Where(c => c.Property.GetClrProperty().Name == "Property")) + .Select(p => p.Values[0]) + .OfType() + .Select(x => x.Text); + var index = new HashSet(); + foreach (var property in properties) + { + if (!index.Add(property)) + { + throw new XamlParseException($"Duplicate setter encountered for property '{property}'", node); + } + } + + return node; + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index f42f787117..be2cae8ec4 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -334,6 +334,47 @@ namespace Avalonia.Markup.Xaml.UnitTests var parsed = (Button)AvaloniaRuntimeXamlLoader.Load(document); Assert.Equal(Colors.Blue, ((ISolidColorBrush)parsed.Background!).Color); } + + [Fact] + public void Style_Parser_Throws_For_Duplicate_Setter() + { + var xaml = @" + + + + + +"; + AssertThrows(() => AvaloniaRuntimeXamlLoader.Load(xaml, typeof(XamlIlTests).Assembly, designMode: true), + e => e.Message.StartsWith("Duplicate setter encountered for property 'Height'")); + } + + [Fact] + public void Control_Theme_Parser_Throws_For_Duplicate_Setter() + { + var xaml = @" + + + + + + + + + + +"; + AssertThrows(() => AvaloniaRuntimeXamlLoader.Load(xaml, typeof(XamlIlTests).Assembly, designMode: true), + e => e.Message.StartsWith("Duplicate setter encountered for property 'Height'")); + } } public class XamlIlBugTestsEventHandlerCodeBehind : Window From 0b726e1eff92cfbf37ed57cda1f42d7951cfce54 Mon Sep 17 00:00:00 2001 From: Dmitry Zhelnin Date: Mon, 30 Jan 2023 11:10:53 +0300 Subject: [PATCH 2/2] add Style and ControlTheme to WellKnownTypes --- .../Transformers/AvaloniaXamlIlControlThemeTransformer.cs | 3 ++- .../Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs | 5 +++-- .../Transformers/AvaloniaXamlIlSelectorTransformer.cs | 3 ++- .../Transformers/AvaloniaXamlIlWellKnownTypes.cs | 4 ++++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs index 1338dc7248..51fe58d1c9 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs @@ -11,7 +11,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { - if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.ControlTheme")) + if (node is not XamlAstObjectNode on || + !context.GetAvaloniaTypes().ControlTheme.IsAssignableFrom(on.Type.GetClrType())) return node; // Check if we've already transformed this node. diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs index 4f92fde3f0..4ab9594cd8 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs @@ -15,8 +15,9 @@ class AvaloniaXamlIlDuplicateSettersChecker : IXamlAstTransformer return node; } - var fullName = objectNode.Type.GetClrType().FullName; - if (fullName is not ("Avalonia.Styling.Style" or "Avalonia.Styling.ControlTheme")) + var nodeType = objectNode.Type.GetClrType(); + if (!context.GetAvaloniaTypes().Style.IsAssignableFrom(nodeType) && + !context.GetAvaloniaTypes().ControlTheme.IsAssignableFrom(nodeType)) { return node; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs index 078e23bc02..edddc5424a 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs @@ -19,7 +19,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { - if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Style")) + if (node is not XamlAstObjectNode on || + !context.GetAvaloniaTypes().Style.IsAssignableFrom(on.Type.GetClrType())) return node; var pn = on.Children.OfType() diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 0aa3dda693..c55e116cb3 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -111,6 +111,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlMethod ResourceDictionaryDeferredAdd { get; } public IXamlType UriKind { get; } public IXamlConstructor UriConstructor { get; } + public IXamlType Style { get; } + public IXamlType ControlTheme { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -248,6 +250,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers XamlIlTypes.Object)); UriKind = cfg.TypeSystem.GetType("System.UriKind"); UriConstructor = Uri.GetConstructor(new List() { cfg.WellKnownTypes.String, UriKind }); + Style = cfg.TypeSystem.GetType("Avalonia.Styling.Style"); + ControlTheme = cfg.TypeSystem.GetType("Avalonia.Styling.ControlTheme"); } }