14 changed files with 348 additions and 27 deletions
@ -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)) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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 @@ |
|||
Subproject commit 610cda30c69e32e83c8235060606480904c937bc |
|||
Subproject commit 3ea474b672ff63629300c36a9644caac2b74a1f2 |
|||
Loading…
Reference in new issue