From 9acb2a11ee4793568b01769d2ffc9160d8ef004c Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 2 Aug 2022 18:42:02 +0200 Subject: [PATCH] Custom setters implement Equals/GetHashCode and have optimized code gen --- ...aloniaXamlIlDeferredResourceTransformer.cs | 50 +++++- .../AvaloniaXamlIlSetterTransformer.cs | 26 ++- .../XamlIlAvaloniaPropertyHelper.cs | 152 ++++++++++++++---- .../Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- 4 files changed, 182 insertions(+), 48 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs index 662263e513..c29dd94886 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using XamlX.Ast; using XamlX.Emit; @@ -47,7 +48,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers return !node.Type.GetClrType().IsValueType; } - class AdderSetter : IXamlPropertySetter, IXamlEmitablePropertySetter + class AdderSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable { private readonly IXamlMethod _getter; private readonly IXamlMethod _adder; @@ -58,16 +59,22 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers _adder = adder; TargetType = getter.DeclaringType; Parameters = adder.ParametersWithThis().Skip(1).ToList(); + + bool allowNull = Parameters.Last().AcceptsNull(); + BinderParameters = new PropertySetterBinderParameters + { + AllowMultiple = true, + AllowXNull = allowNull, + AllowRuntimeNull = allowNull + }; } public IXamlType TargetType { get; } - public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters - { - AllowMultiple = true - }; + public PropertySetterBinderParameters BinderParameters { get; } public IReadOnlyList Parameters { get; } + public void Emit(IXamlILEmitter emitter) { var locals = new Stack(); @@ -80,11 +87,40 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } emitter.EmitCall(_getter); - while (locals.Count > 0) + while (locals.Count>0) using (var loc = locals.Pop()) emitter.Ldloc(loc.Local); emitter.EmitCall(_adder, true); } + + public void EmitWithArguments( + XamlEmitContextWithLocals context, + IXamlILEmitter emitter, + IReadOnlyList arguments) + { + emitter.EmitCall(_getter); + + for (var i = 0; i < arguments.Count; ++i) + context.Emit(arguments[i], emitter, Parameters[i]); + + emitter.EmitCall(_adder, true); + } + + public bool Equals(AdderSetter other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + + return _getter.Equals(other._getter) && _adder.Equals(other._adder); + } + + public override bool Equals(object obj) + => Equals(obj as AdderSetter); + + public override int GetHashCode() + => (_getter.GetHashCode() * 397) ^ _adder.GetHashCode(); } } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs index 6da95be1c1..ceaec972f6 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs @@ -75,17 +75,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { Getter = setterType.Methods.First(m => m.Name == "get_Value"); var method = setterType.Methods.First(m => m.Name == "set_Value"); - Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding)); - Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType)); - Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType)); + Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding, false)); + Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType, false)); + Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType, targetType.AcceptsNull())); } - class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter + sealed class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter { private readonly IXamlMethod _method; private readonly IXamlType _type; public IXamlType TargetType { get; } - public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters(); + public PropertySetterBinderParameters BinderParameters { get; } public IReadOnlyList Parameters { get; } public void Emit(IXamlILEmitter codegen) { @@ -94,13 +94,27 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers codegen.EmitCall(_method, true); } - public XamlIlDirectCallPropertySetter(IXamlMethod method, IXamlType type) + public XamlIlDirectCallPropertySetter(IXamlMethod method, IXamlType type, bool allowNull) { _method = method; _type = type; Parameters = new[] {type}; TargetType = method.ThisOrFirstParameter(); + BinderParameters = new PropertySetterBinderParameters + { + AllowXNull = allowNull, + AllowRuntimeNull = allowNull + }; } + + private bool Equals(XamlIlDirectCallPropertySetter other) + => Equals(_method, other._method) && Equals(_type, other._type); + + public override bool Equals(object obj) + => Equals(obj as XamlIlDirectCallPropertySetter); + + public override int GetHashCode() + => (_method.GetHashCode() * 397) ^ _type.GetHashCode(); } } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 5c7a80e680..6c9d510ba0 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -206,38 +206,64 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field)); } - abstract class AvaloniaPropertyCustomSetter : IXamlPropertySetter, IXamlEmitablePropertySetter + abstract class AvaloniaPropertyCustomSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable { - protected AvaloniaXamlIlWellKnownTypes Types; - protected IXamlField AvaloniaProperty; + protected readonly AvaloniaXamlIlWellKnownTypes Types; + protected readonly IXamlField AvaloniaProperty; - public AvaloniaPropertyCustomSetter(AvaloniaXamlIlWellKnownTypes types, + protected AvaloniaPropertyCustomSetter( + AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, - IXamlField avaloniaProperty) + IXamlField avaloniaProperty, + bool allowNull) { Types = types; AvaloniaProperty = avaloniaProperty; TargetType = declaringType; + BinderParameters = new PropertySetterBinderParameters + { + AllowXNull = allowNull, + AllowRuntimeNull = allowNull + }; } public IXamlType TargetType { get; } - public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters - { - AllowXNull = false - }; + public PropertySetterBinderParameters BinderParameters { get; } public IReadOnlyList Parameters { get; set; } - public abstract void Emit(IXamlILEmitter codegen); + + public abstract void Emit(IXamlILEmitter emitter); + + public abstract void EmitWithArguments( + XamlEmitContextWithLocals context, + IXamlILEmitter emitter, + IReadOnlyList arguments); + + public bool Equals(AvaloniaPropertyCustomSetter other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + + return GetType() == other.GetType() && AvaloniaProperty.Equals(other.AvaloniaProperty); + } + + public override bool Equals(object obj) + => Equals(obj as AvaloniaPropertyCustomSetter); + + public override int GetHashCode() + => AvaloniaProperty.GetHashCode(); } class BindingSetter : AvaloniaPropertyCustomSetter { public BindingSetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, - IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty) + IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty, false) { - Parameters = new[] {types.IBinding}; + Parameters = new[] { types.IBinding }; } public override void Emit(IXamlILEmitter emitter) @@ -246,10 +272,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions emitter .Stloc(bloc.Local) .Ldsfld(AvaloniaProperty) - .Ldloc(bloc.Local) - // TODO: provide anchor? - .Ldnull(); - emitter.EmitCall(Types.AvaloniaObjectBindMethod, true); + .Ldloc(bloc.Local); + EmitAnchorAndBind(emitter); + } + + public override void EmitWithArguments( + XamlEmitContextWithLocals context, + IXamlILEmitter emitter, + IReadOnlyList arguments) + { + emitter.Ldsfld(AvaloniaProperty); + context.Emit(arguments[0], emitter, Parameters[0]); + EmitAnchorAndBind(emitter); + } + + private void EmitAnchorAndBind(IXamlILEmitter emitter) + { + emitter + .Ldnull() // TODO: provide anchor? + .EmitCall(Types.AvaloniaObjectBindMethod, true); } } @@ -257,7 +298,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { public BindingWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, - IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty) + IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty, false) { Parameters = new[] { types.BindingPriority, types.IBinding }; } @@ -265,15 +306,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions public override void Emit(IXamlILEmitter emitter) { using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding)) - using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int)) emitter .Stloc(bloc.Local) - .Stloc(priorityLocal.Local) + .Pop() // ignore priority .Ldsfld(AvaloniaProperty) - .Ldloc(bloc.Local) - // TODO: provide anchor? - .Ldnull(); - emitter.EmitCall(Types.AvaloniaObjectBindMethod, true); + .Ldloc(bloc.Local); + EmitAnchorAndBind(emitter); + } + + public override void EmitWithArguments( + XamlEmitContextWithLocals context, + IXamlILEmitter emitter, + IReadOnlyList arguments) + { + emitter.Ldsfld(AvaloniaProperty); + context.Emit(arguments[1], emitter, Parameters[1]); + EmitAnchorAndBind(emitter); + } + + private void EmitAnchorAndBind(IXamlILEmitter emitter) + { + emitter + .Ldnull() // TODO: provide anchor? + .EmitCall(Types.AvaloniaObjectBindMethod, true); } } @@ -281,7 +336,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { public SetValueWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty, IXamlType propertyType) - : base(types, declaringType, avaloniaProperty) + : base(types, declaringType, avaloniaProperty, propertyType.AcceptsNull()) { Parameters = new[] { types.BindingPriority, propertyType }; } @@ -295,9 +350,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions - value */ - var method = Types.AvaloniaObjectSetStyledPropertyValue - .MakeGenericMethod(new[] { Parameters[1] }); - using (var valueLocal = emitter.LocalsPool.GetLocal(Parameters[1])) using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int)) emitter @@ -305,25 +357,57 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions .Stloc(priorityLocal.Local) .Ldsfld(AvaloniaProperty) .Ldloc(valueLocal.Local) - .Ldloc(priorityLocal.Local) - .EmitCall(method, true); + .Ldloc(priorityLocal.Local); + + EmitSetStyledPropertyValue(emitter); + } + + public override void EmitWithArguments( + XamlEmitContextWithLocals context, + IXamlILEmitter emitter, + IReadOnlyList arguments) + { + emitter.Ldsfld(AvaloniaProperty); + context.Emit(arguments[1], emitter, Parameters[1]); + context.Emit(arguments[0], emitter, Parameters[0]); + EmitSetStyledPropertyValue(emitter); + } + + private void EmitSetStyledPropertyValue(IXamlILEmitter emitter) + { + var method = Types.AvaloniaObjectSetStyledPropertyValue.MakeGenericMethod(new[] { Parameters[1] }); + emitter.EmitCall(method, true); } } class UnsetValueSetter : AvaloniaPropertyCustomSetter { public UnsetValueSetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty) - : base(types, declaringType, avaloniaProperty) + : base(types, declaringType, avaloniaProperty, false) { - Parameters = new[] {types.UnsetValueType}; + Parameters = new[] { types.UnsetValueType }; } public override void Emit(IXamlILEmitter codegen) { + codegen.Pop(); + EmitSetValue(codegen); + } + + public override void EmitWithArguments( + XamlEmitContextWithLocals context, + IXamlILEmitter emitter, + IReadOnlyList arguments) + { + EmitSetValue(emitter); + } + + private void EmitSetValue(IXamlILEmitter emitter) + { + // Ignore the instance and load one from the static field to avoid extra local variable var unsetValue = Types.AvaloniaProperty.Fields.First(f => f.Name == "UnsetValue"); - codegen - // Ignore the instance and load one from the static field to avoid extra local variable - .Pop() + + emitter .Ldsfld(AvaloniaProperty) .Ldsfld(unsetValue) .Ldc_I4(0) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index a4e6be2d14..c1c0594ec2 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit a4e6be2d1407abec4f35fcb208848830ce513ead +Subproject commit c1c0594ec2c35b08988183b1a5b3e34dfa19179d