ソースを参照

Merge branch 'master' into pr-template

Max Katz 3 年 前
コミット
a6f1b7c0da

+ 43 - 7
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<IXamlILEmitter>
+        class AdderSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable<AdderSetter>
         {
             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<IXamlType> Parameters { get; }
+
             public void Emit(IXamlILEmitter emitter)
             {
                 var locals = new Stack<XamlLocalsPool.PooledLocal>();
@@ -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<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> 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();
         }
     }
 }

+ 20 - 6
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<IXamlILEmitter>
+            sealed class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
             {
                 private readonly IXamlMethod _method;
                 private readonly IXamlType _type;
                 public IXamlType TargetType { get; }
-                public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters();
+                public PropertySetterBinderParameters BinderParameters { get; }
                 public IReadOnlyList<IXamlType> 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();
             }
         }
     }

+ 118 - 34
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<IXamlILEmitter>
+        abstract class AvaloniaPropertyCustomSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable<AvaloniaPropertyCustomSetter>
         {
-            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<IXamlType> Parameters { get; set; }
-            public abstract void Emit(IXamlILEmitter codegen);
+
+            public abstract void Emit(IXamlILEmitter emitter);
+
+            public abstract void EmitWithArguments(
+                XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> 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<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> 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<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> 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<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> 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<IXamlILEmitter, XamlILNodeEmitResult> context,
+                IXamlILEmitter emitter,
+                IReadOnlyList<IXamlAstValueNode> 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)

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@@ -1 +1 @@
-Subproject commit a4e6be2d1407abec4f35fcb208848830ce513ead
+Subproject commit c1c0594ec2c35b08988183b1a5b3e34dfa19179d