diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs index 1974dfe3bc..c847bf54bc 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/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 GetAllDefinedMethods(IXamlType type) + { + foreach (var t in TraverseTypeHierarchy(type)) + { + foreach (var m in t.Methods) + { + yield return m; + } + } + } + static IEnumerable 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 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; diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs index 3e468d8c8e..31c9535e5f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs +++ b/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; }