瀏覽代碼

Added IPropertyInfo and use it in IProvideValueTarget when possible

Nikita Tsukanov 6 年之前
父節點
當前提交
c222070d84

+ 6 - 2
src/Avalonia.Base/AvaloniaProperty.cs

@@ -7,6 +7,7 @@ using System.Diagnostics;
 using System.Reactive.Subjects;
 using System.Reflection;
 using Avalonia.Data;
+using Avalonia.Data.Core;
 using Avalonia.Utilities;
 
 namespace Avalonia
@@ -14,7 +15,7 @@ namespace Avalonia
     /// <summary>
     /// Base class for avalonia properties.
     /// </summary>
-    public class AvaloniaProperty : IEquatable<AvaloniaProperty>
+    public class AvaloniaProperty : IEquatable<AvaloniaProperty>, IPropertyInfo
     {
         /// <summary>
         /// Represents an unset property value.
@@ -546,7 +547,10 @@ namespace Avalonia
             }
         }
 
-        
+        bool IPropertyInfo.CanGet => true;
+        bool IPropertyInfo.CanSet => true;
+        object IPropertyInfo.Get(object target) => ((AvaloniaObject)target).GetValue(this);
+        void IPropertyInfo.Set(object target, object value) => ((AvaloniaObject)target).SetValue(this, value);
     }
     /// <summary>
     /// Class representing the <see cref="AvaloniaProperty.UnsetValue"/>.

+ 70 - 0
src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace Avalonia.Data.Core
+{
+    public class ClrPropertyInfo : IPropertyInfo
+    {
+        private readonly Func<object, object> _getter;
+        private readonly Action<object, object> _setter;
+
+        public ClrPropertyInfo(string name, Func<object, object> getter, Action<object, object> setter)
+        {
+            _getter = getter;
+            _setter = setter;
+            Name = name;
+        }
+
+        public string Name { get; }
+        public object Get(object target)
+        {
+            if (_getter == null)
+                throw new NotSupportedException("Property " + Name + " doesn't have a getter");
+            return _getter(target);
+        }
+
+        public void Set(object target, object value)
+        {
+            if (_setter == null)
+                throw new NotSupportedException("Property " + Name + " doesn't have a setter");
+            _setter(target, value);
+        }
+
+        public bool CanSet => _setter != null;
+        public bool CanGet => _getter != null;
+    }
+
+    public class ReflectionClrPropertyInfo : ClrPropertyInfo
+    {
+        static Action<object, object> CreateSetter(PropertyInfo info)
+        {
+            if (info.SetMethod == null)
+                return null;
+            var target = Expression.Parameter(typeof(object), "target");
+            var value = Expression.Parameter(typeof(object), "value");
+            return Expression.Lambda<Action<object, object>>(
+                    Expression.Call(Expression.Convert(target, info.DeclaringType), info.SetMethod,
+                        Expression.Convert(value, info.SetMethod.GetParameters()[0].ParameterType)),
+                    target, value)
+                .Compile();
+        }
+        
+        static Func<object, object> CreateGetter(PropertyInfo info)
+        {
+            if (info.GetMethod == null)
+                return null;
+            var target = Expression.Parameter(typeof(object), "target");
+            return Expression.Lambda<Func<object, object>>(
+                    Expression.Convert(Expression.Call(Expression.Convert(target, info.DeclaringType), info.GetMethod),
+                        typeof(object)))
+                .Compile();
+        }
+
+        public ReflectionClrPropertyInfo(PropertyInfo info) : base(info.Name,
+            CreateGetter(info), CreateSetter(info))
+        {
+            
+        }
+    }
+}

+ 11 - 0
src/Avalonia.Base/Data/Core/IPropertyInfo.cs

@@ -0,0 +1,11 @@
+namespace Avalonia.Data.Core
+{
+    public interface IPropertyInfo
+    {
+        string Name { get; }
+        object Get(object target);
+        void Set(object target, object value);
+        bool CanSet { get; }
+        bool CanGet { get; }
+    }
+}

+ 7 - 2
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@@ -49,13 +49,18 @@ namespace Avalonia.Build.Tasks
             if (avares.Resources.Count(CheckXamlName) == 0 && emres.Resources.Count(CheckXamlName) == 0)
                 // Nothing to do
                 return new CompileResult(true);
+
+            var clrPropertiesDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlHelpers",
+                TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
+            asm.MainModule.Types.Add(clrPropertiesDef);
             
             var xamlLanguage = AvaloniaXamlIlLanguage.Configure(typeSystem);
-            var compilerConfig = new XamlIlTransformerConfiguration(typeSystem,
+            var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
                 typeSystem.TargetAssembly,
                 xamlLanguage,
                 XamlIlXmlnsMappings.Resolve(typeSystem, xamlLanguage),
-                AvaloniaXamlIlLanguage.CustomValueConverter);
+                AvaloniaXamlIlLanguage.CustomValueConverter,
+                new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)));
 
 
             var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext", 

+ 2 - 0
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -38,6 +38,7 @@
         <Compile Include="Templates\TemplateContent.cs" />
         <Compile Include="Templates\TreeDataTemplate.cs" />
         <Compile Include="XamlIl\AvaloniaXamlIlRuntimeCompiler.cs" />
+        <Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompilerConfiguration.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaBindingExtensionHackTransformer.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlAvaloniaPropertyResolver.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlConstructorServiceProviderTransformer.cs" />
@@ -55,6 +56,7 @@
         <Compile Include="XamlIl\CompilerExtensions\Transformers\IgnoredDirectivesTransformer.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlSelectorTransformer.cs" />
         <Compile Include="XamlIl\CompilerExtensions\Transformers\XNameTransformer.cs" />
+        <Compile Include="XamlIl\CompilerExtensions\XamlIlClrPropertyInfoHelper.cs" />
         <Compile Include="XamlIl\Runtime\IAvaloniaXamlIlParentStackProvider.cs" />
         <Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
         <Compile Include="XamlIl\Runtime\XamlIlRuntimeHelpers.cs" />

+ 8 - 3
src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs

@@ -114,11 +114,15 @@ namespace Avalonia.Markup.Xaml.XamlIl
 
             InitializeSre();
             var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly);
+
+            var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri);
+            var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N"));
             
-            var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_sreTypeSystem, asm,
-                _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter),
+            var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm,
+                _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter,
+                new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder))), 
                 _sreContextType);
-            var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri);
+            
 
             IXamlIlType overrideType = null;
             if (rootInstance != null)
@@ -129,6 +133,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
             compiler.IsDesignMode = isDesignMode;
             compiler.ParseAndCompile(xaml, uri?.ToString(), null, _sreTypeSystem.CreateTypeBuilder(tb), overrideType);
             var created = tb.CreateTypeInfo();
+            clrPropertyBuilder.CreateTypeInfo();
 
             return LoadOrPopulate(created, rootInstance);
         }

+ 3 - 3
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@@ -16,7 +16,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
         private readonly IXamlIlType _contextType;
         private readonly AvaloniaXamlIlDesignPropertiesTransformer _designTransformer;
 
-        private AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration) : base(configuration, true)
+        private AvaloniaXamlIlCompiler(AvaloniaXamlIlCompilerConfiguration configuration) : base(configuration, true)
         {
             _configuration = configuration;
 
@@ -57,14 +57,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
 
         }
 
-        public AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration,
+        public AvaloniaXamlIlCompiler(AvaloniaXamlIlCompilerConfiguration configuration,
             IXamlIlTypeBuilder contextTypeBuilder) : this(configuration)
         {
             _contextType = CreateContextType(contextTypeBuilder);
         }
 
         
-        public AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration,
+        public AvaloniaXamlIlCompiler(AvaloniaXamlIlCompilerConfiguration configuration,
             IXamlIlType contextType) : this(configuration)
         {
             _contextType = contextType;

+ 21 - 0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs

@@ -0,0 +1,21 @@
+using XamlIl.Transform;
+using XamlIl.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
+{
+    class AvaloniaXamlIlCompilerConfiguration : XamlIlTransformerConfiguration
+    {
+        public XamlIlClrPropertyInfoEmitter ClrPropertyEmitter { get; }
+
+        public AvaloniaXamlIlCompilerConfiguration(IXamlIlTypeSystem typeSystem, 
+            IXamlIlAssembly defaultAssembly, 
+            XamlIlLanguageTypeMappings typeMappings,
+            XamlIlXmlnsMappings xmlnsMappings,
+            XamlIlValueConverter customValueConverter,
+            XamlIlClrPropertyInfoEmitter clrPropertyEmitter) : base(typeSystem, defaultAssembly, typeMappings, xmlnsMappings, customValueConverter)
+        {
+            ClrPropertyEmitter = clrPropertyEmitter;
+            AddExtra(ClrPropertyEmitter);
+        }
+    }
+}

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@@ -50,7 +50,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 },
                 InnerServiceProviderFactoryMethod =
                     runtimeHelpers.FindMethod(m => m.Name == "CreateInnerServiceProviderV1"),
-                ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.Emit,
+                ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.EmitProvideValueTarget,
             };
             rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv);
             return rv;

+ 27 - 15
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@@ -19,26 +19,30 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
         public IXamlIlType Transitions { get; }
         public IXamlIlType AssignBindingAttribute { get; }
         public IXamlIlType UnsetValueType { get; }
+        public IXamlIlType IPropertyInfo { get; }
+        public IXamlIlType ClrPropertyInfo { get; }
         
-        public AvaloniaXamlIlWellKnownTypes(XamlIlAstTransformationContext ctx)
+        public AvaloniaXamlIlWellKnownTypes(XamlIlTransformerConfiguration cfg)
         {
-            XamlIlTypes = ctx.Configuration.WellKnownTypes;
-            AvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObject");
-            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");
-            Transitions = ctx.Configuration.TypeSystem.GetType("Avalonia.Animation.Transitions");
-            AssignBindingAttribute = ctx.Configuration.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
+            XamlIlTypes = cfg.WellKnownTypes;
+            AvaloniaObject = cfg.TypeSystem.GetType("Avalonia.AvaloniaObject");
+            IAvaloniaObject = cfg.TypeSystem.GetType("Avalonia.IAvaloniaObject");
+            AvaloniaObjectExtensions = cfg.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions");
+            AvaloniaProperty = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty");
+            AvaloniaPropertyT = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty`1");
+            BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority");
+            IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding");
+            IDisposable = cfg.TypeSystem.GetType("System.IDisposable");
+            Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions");
+            AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
             AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject,
                 AvaloniaProperty,
-                IBinding, ctx.Configuration.WellKnownTypes.Object);
-            UnsetValueType = ctx.Configuration.TypeSystem.GetType("Avalonia.UnsetValueType");
+                IBinding, cfg.WellKnownTypes.Object);
+            UnsetValueType = cfg.TypeSystem.GetType("Avalonia.UnsetValueType");
             AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", XamlIlTypes.Void,
                 false, AvaloniaProperty, XamlIlTypes.Object, BindingPriority);
+            IPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.IPropertyInfo");
+            ClrPropertyInfo = cfg.TypeSystem.GetType("Avalonia.Data.Core.ClrPropertyInfo");
         }
     }
 
@@ -48,7 +52,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
         {
             if (ctx.TryGetItem<AvaloniaXamlIlWellKnownTypes>(out var rv))
                 return rv;
-            ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx));
+            ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx.Configuration));
+            return rv;
+        }
+        
+        public static AvaloniaXamlIlWellKnownTypes GetAvaloniaTypes(this XamlIlEmitContext ctx)
+        {
+            if (ctx.TryGetItem<AvaloniaXamlIlWellKnownTypes>(out var rv))
+                return rv;
+            ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx.Configuration));
             return rv;
         }
     }

+ 14 - 0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

@@ -15,6 +15,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
 {
     class XamlIlAvaloniaPropertyHelper
     {
+        public static bool EmitProvideValueTarget(XamlIlEmitContext context, IXamlIlEmitter emitter,
+            XamlIlAstClrProperty property)
+        {
+            if (Emit(context, emitter, property))
+                return true;
+            var foundClr = property.DeclaringType.Properties.FirstOrDefault(p => p.Name == property.Name);
+            if (foundClr == null)
+                return false;
+            context
+                .Configuration.GetExtra<XamlIlClrPropertyInfoEmitter>()
+                .Emit(context, emitter, foundClr);
+            return true;
+        }
+        
         public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, XamlIlAstClrProperty property)
         {
             if (property is IXamlIlAvaloniaProperty ap)

+ 147 - 0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs

@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using XamlIl.Ast;
+using XamlIl.Transform;
+using XamlIl.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
+{
+    class XamlIlClrPropertyInfoHelper
+    {
+        
+        class ClrPropertyInfoNode : XamlIlAstNode, IXamlIlAstValueNode
+        {
+            public ClrPropertyInfoNode(AvaloniaXamlIlWellKnownTypes types, IXamlIlLineInfo lineInfo) :
+                base(lineInfo)
+            {
+                Type = new XamlIlAstClrTypeReference(this, types.XamlIlTypes.Object, false);
+            }
+
+            public IXamlIlAstTypeReference Type { get; }
+        }
+    }
+
+    class XamlIlClrPropertyInfoEmitter
+    {
+        private readonly IXamlIlTypeBuilder _builder;
+
+        private Dictionary<string, List<(IXamlIlProperty prop, IXamlIlMethod get)>> _fields
+            = new Dictionary<string, List<(IXamlIlProperty prop, IXamlIlMethod get)>>();
+        
+        public XamlIlClrPropertyInfoEmitter(IXamlIlTypeBuilder builder)
+        {
+            _builder = builder;
+        }
+
+        static string GetKey(IXamlIlProperty property) 
+            => property.Getter.DeclaringType.GetFullName() + "." + property.Name;
+
+        public IXamlIlType Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen, IXamlIlProperty property)
+        {
+            var types = context.GetAvaloniaTypes();
+            IXamlIlMethod Get()
+            {
+                var key = GetKey(property);
+                if (!_fields.TryGetValue(key, out var lst))
+                    _fields[key] = lst = new List<(IXamlIlProperty prop, IXamlIlMethod get)>();
+
+                foreach (var cached in lst)
+                {
+                    if (
+                        ((cached.prop.Getter == null && property.Getter == null) ||
+                         cached.prop.Getter?.Equals(property.Getter) == true) &&
+                        ((cached.prop.Setter == null && property.Setter == null) ||
+                         cached.prop.Setter?.Equals(property.Setter) == true)
+                    )
+                        return cached.get;
+                }
+
+                var name = lst.Count == 0 ? key : key + "_" + Guid.NewGuid().ToString("N");
+                
+                var field = _builder.DefineField(types.IPropertyInfo, name + "!Field", false, true);
+
+                var getter = property.Getter == null ?
+                    null :
+                    _builder.DefineMethod(types.XamlIlTypes.Object,
+                        new[] {types.XamlIlTypes.Object}, name + "!Getter", false, true, false);
+                if (getter != null)
+                {
+                    getter.Generator
+                        .Ldarg_0()
+                        .Castclass(property.Getter.DeclaringType)
+                        .EmitCall(property.Getter);
+                    if (property.Getter.ReturnType.IsValueType)
+                        getter.Generator.Box(property.Getter.ReturnType);
+                    getter.Generator.Ret();
+                }
+
+                var setter = property.Setter == null ?
+                    null :
+                    _builder.DefineMethod(types.XamlIlTypes.Void,
+                        new[] {types.XamlIlTypes.Object, types.XamlIlTypes.Object},
+                        name + "!Setter", false, true, false);
+                if (setter != null)
+                {
+                    setter.Generator
+                        .Ldarg_0()
+                        .Castclass(property.Setter.DeclaringType)
+                        .Ldarg(1);
+                    if (property.Setter.Parameters[0].IsValueType)
+                        setter.Generator.Unbox_Any(property.Setter.Parameters[0]);
+                    else
+                        setter.Generator.Castclass(property.Setter.Parameters[0]);
+                    setter.Generator
+                        .EmitCall(property.Setter, true)
+                        .Ret();
+                }
+
+                var get = _builder.DefineMethod(types.IPropertyInfo, Array.Empty<IXamlIlType>(),
+                    name + "!Property", true, true, false);
+
+
+                var ctor = types.ClrPropertyInfo.Constructors.First(c =>
+                    c.Parameters.Count == 3 && c.IsStatic == false);
+                
+                var cacheMiss = get.Generator.DefineLabel();
+                get.Generator
+                    .Ldsfld(field)
+                    .Brfalse(cacheMiss)
+                    .Ldsfld(field)
+                    .Ret()
+                    .MarkLabel(cacheMiss)
+                    .Ldstr(property.Name);
+
+                void EmitFunc(IXamlIlEmitter emitter, IXamlIlMethod method, IXamlIlType del)
+                {
+                    if (method == null)
+                        emitter.Ldnull();
+                    else
+                    {
+                        emitter
+                            .Ldnull()
+                            .Ldftn(method)
+                            .Newobj(del.Constructors.First(c =>
+                                c.Parameters.Count == 2 &&
+                                c.Parameters[0].Equals(context.Configuration.WellKnownTypes.Object)));
+                    }
+                }
+
+                EmitFunc(get.Generator, getter, ctor.Parameters[1]);
+                EmitFunc(get.Generator, setter, ctor.Parameters[2]);
+                get.Generator
+                    .Newobj(ctor)
+                    .Stsfld(field)
+                    .Ldsfld(field)
+                    .Ret();
+
+                lst.Add((property, get));
+                return get;
+            }
+
+            codeGen.EmitCall(Get());
+            return types.IPropertyInfo;
+        }
+    }
+}

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

@@ -1 +1 @@
-Subproject commit 610cda30c69e32e83c8235060606480904c937bc
+Subproject commit 3ea474b672ff63629300c36a9644caac2b74a1f2

+ 30 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

@@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
 using Avalonia.Controls;
 using Avalonia.Controls.Presenters;
 using Avalonia.Data.Converters;
+using Avalonia.Data.Core;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Media;
@@ -234,6 +235,17 @@ namespace Avalonia.Markup.Xaml.UnitTests
                 Assert.Equal(100, XamlIlBugTestsStaticClassWithAttachedProperty.GetTestInt(tb));
             }
         }
+
+        [Fact]
+        public void Provide_Value_Target_Should_Provide_Clr_Property_Info()
+        {
+            var parsed = AvaloniaXamlLoader.Parse<XamlIlClassWithClrPropertyWithValue>(@"
+<XamlIlClassWithClrPropertyWithValue 
+    xmlns='clr-namespace:Avalonia.Markup.Xaml.UnitTests'
+    Count='{XamlIlCheckClrPropertyInfo ExpectedPropertyName=Count}'
+/>", typeof(XamlIlClassWithClrPropertyWithValue).Assembly);
+            Assert.Equal(6, parsed.Count);
+        }
     }
     
     public class XamlIlBugTestsEventHandlerCodeBehind : Window
@@ -314,4 +326,22 @@ namespace Avalonia.Markup.Xaml.UnitTests
             return (int)control.GetValue(TestIntProperty);
         }
     }
+
+    public class XamlIlCheckClrPropertyInfoExtension
+    {
+        public string ExpectedPropertyName { get; set; }
+
+        public object ProvideValue(IServiceProvider prov)
+        {
+            var pvt = prov.GetService<IProvideValueTarget>();
+            var info = (ClrPropertyInfo)pvt.TargetProperty;
+            var v = (int)info.Get(pvt.TargetObject);
+            return v + 1;
+        }
+    }
+
+    public class XamlIlClassWithClrPropertyWithValue
+    {
+        public int Count { get; set; }= 5;
+    }
 }