A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

158 lines
6.1 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX.Ast;
using XamlX.Transform;
using XamlX.TypeSystem;
using XamlX.IL;
using XamlX.Emit;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
class XamlIlClrPropertyInfoEmitter
{
private readonly IXamlTypeBuilder<IXamlILEmitter> _builder;
private Dictionary<string, List<(IXamlProperty prop, IXamlMethod get)>> _fields
= new Dictionary<string, List<(IXamlProperty prop, IXamlMethod get)>>();
public XamlIlClrPropertyInfoEmitter(IXamlTypeBuilder<IXamlILEmitter> builder)
{
_builder = builder;
}
static string GetKey(IXamlProperty property, string indexerArgumentsKey)
{
var baseKey = property.Getter.DeclaringType.GetFullName() + "." + property.Name;
if (indexerArgumentsKey is null)
{
return baseKey;
}
return baseKey + $"[{indexerArgumentsKey}]";
}
public IXamlType Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen, IXamlProperty property, IEnumerable<IXamlAstValueNode> indexerArguments = null, string indexerArgumentsKey = null)
{
indexerArguments = indexerArguments ?? Enumerable.Empty<IXamlAstValueNode>();
var types = context.GetAvaloniaTypes();
IXamlMethod Get()
{
var key = GetKey(property, indexerArgumentsKey);
if (!_fields.TryGetValue(key, out var lst))
_fields[key] = lst = new List<(IXamlProperty prop, IXamlMethod 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);
void Load(IXamlMethod m, IXamlILEmitter cg)
{
cg
.Ldarg_0();
if (m.DeclaringType.IsValueType)
cg.Unbox(m.DeclaringType);
else
cg.Castclass(m.DeclaringType);
foreach (var indexerArg in indexerArguments)
{
context.Emit(indexerArg, cg, indexerArg.Type.GetClrType());
}
}
var getter = property.Getter == null ?
null :
_builder.DefineMethod(types.XamlIlTypes.Object,
new[] {types.XamlIlTypes.Object}, name + "!Getter", false, true, false);
if (getter != null)
{
Load(property.Getter, getter.Generator);
getter.Generator.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)
{
Load(property.Setter, setter.Generator);
setter.Generator.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<IXamlType>(),
name + "!Property", true, true, false);
var ctor = types.ClrPropertyInfo.Constructors.First(c =>
c.Parameters.Count == 4 && 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, IXamlMethod method, IXamlType 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
.Ldtype(property.PropertyType)
.Newobj(ctor)
.Stsfld(field)
.Ldsfld(field)
.Ret();
lst.Add((property, get));
return get;
}
codeGen.EmitCall(Get());
return types.IPropertyInfo;
}
}
}