Browse Source

Implement method->delegate mapping in the XAML compiler for well-known delegate types

pull/7246/head
Jeremy Koritzinsky 4 years ago
parent
commit
12adf764c0
No known key found for this signature in database GPG Key ID: 25A7D1C8126B7A16
  1. 71
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
  2. 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

71
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs

@ -119,16 +119,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyFieldMaybe,
XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyFieldMaybe, context.GetAvaloniaTypes(), lineInfo)));
}
else
else if (GetAllDefinedProperties(targetType).FirstOrDefault(p => p.Name == propName.PropertyName) is IXamlProperty clrProperty)
{
var clrProperty = GetAllDefinedProperties(targetType).FirstOrDefault(p => p.Name == propName.PropertyName);
if (clrProperty is null)
{
throw new XamlX.XamlParseException($"Unable to resolve property of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
}
nodes.Add(new XamlIlClrPropertyPathElementNode(clrProperty));
}
else if (GetAllDefinedMethods(targetType).FirstOrDefault(m => m.Name == propName.PropertyName) is IXamlMethod method)
{
nodes.Add(new XamlIlClrMethodPathElementNode(method, context.Configuration.WellKnownTypes.Delegate));
}
else
{
throw new XamlX.XamlParseException($"Unable to resolve property or method of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
}
break;
case BindingExpressionGrammar.IndexerNode indexer:
{
@ -275,6 +277,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
static IEnumerable<IXamlMethod> GetAllDefinedMethods(IXamlType type)
{
foreach (var t in TraverseTypeHierarchy(type))
{
foreach (var m in t.Methods)
{
yield return m;
}
}
}
static IEnumerable<IXamlType> TraverseTypeHierarchy(IXamlType type)
{
if (type.IsInterface)
@ -529,6 +542,50 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public IXamlType Type => _property.Getter?.ReturnType ?? _property.Setter?.Parameters[0];
}
class XamlIlClrMethodPathElementNode : IXamlIlBindingPathElementNode
{
private readonly IXamlMethod _method;
public XamlIlClrMethodPathElementNode(IXamlMethod method, IXamlType systemDelegateType)
{
_method = method;
Type = systemDelegateType;
}
public IXamlType Type { get; }
public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
{
IXamlType specificDelegateType;
if (_method.ReturnType == context.Configuration.WellKnownTypes.Void && _method.Parameters.Count <= 8)
{
specificDelegateType = context.Configuration.TypeSystem
.GetType($"System.Action`{_method.Parameters.Count}")
.MakeGenericType(_method.Parameters);
}
else if (_method.Parameters.Count <= 7)
{
List<IXamlType> genericParameters = new();
genericParameters.AddRange(_method.Parameters);
genericParameters.Add(_method.ReturnType);
specificDelegateType = context.Configuration.TypeSystem
.GetType($"System.Func`{_method.Parameters.Count}")
.MakeGenericType(genericParameters);
}
else
{
// In this case, we need to emit our own delegate type.
specificDelegateType = null;
}
codeGen
.Ldtoken(_method)
.Ldtoken(specificDelegateType)
.EmitCall(context.GetAvaloniaTypes()
.CompiledBindingPathBuilder.FindMethod(m => m.Name == "Method"));
}
}
class XamlIlClrIndexerPathElementNode : IXamlIlBindingPathElementNode
{
private readonly IXamlProperty _property;

6
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -96,7 +96,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
return this;
}
public CompiledBindingPathBuilder Method(RuntimeMethodHandle handle, Type delegateType)
public CompiledBindingPathBuilder Method(RuntimeMethodHandle handle, RuntimeTypeHandle delegateType)
{
_elements.Add(new MethodAsDelegateElement(handle, delegateType));
return this;
@ -190,10 +190,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
internal class MethodAsDelegateElement : ICompiledBindingPathElement
{
public MethodAsDelegateElement(RuntimeMethodHandle method, Type delegateType)
public MethodAsDelegateElement(RuntimeMethodHandle method, RuntimeTypeHandle delegateType)
{
Method = (MethodInfo)MethodBase.GetMethodFromHandle(method);
DelegateType = delegateType;
DelegateType = Type.GetTypeFromHandle(delegateType);
}
public MethodInfo Method { get; }

Loading…
Cancel
Save