csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
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
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;
|
|
}
|
|
}
|
|
}
|
|
|