From 0576baf5b7309f85446b681b588f11d89478bea8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 19 Dec 2021 17:10:02 -0600 Subject: [PATCH] Add tests for full-fidelity method->delegate conversion in compiled bindings. --- .../XamlCompilerTaskExecutor.cs | 2 + .../AvaloniaXamlIlLanguageParseIntrinsics.cs | 2 +- .../AvaloniaXamlIlSelectorTransformer.cs | 2 +- .../XamlIlBindingPathHelper.cs | 17 ++++++-- .../Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- .../CompiledBindingExtensionTests.cs | 43 +++++++++++++++++++ 6 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 593d79471e..d2f7102b48 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -255,6 +255,8 @@ namespace Avalonia.Build.Tasks true), (closureName, closureBaseType) => populateBuilder.DefineSubType(closureBaseType, closureName, false), + (closureName, returnType, parameterTypes) => + populateBuilder.CreateDelegateSubType(closureName, false, returnType, parameterTypes), res.Uri, res ); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index 4592b9c8b4..b622971d38 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -201,7 +201,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions if (type.Equals(types.Classes)) { var classes = text.Split(' '); - var classNodes = classes.Select(c => new XamlAstTextNode(node, c, types.XamlIlTypes.String)).ToArray(); + var classNodes = classes.Select(c => new XamlAstTextNode(node, c, type: types.XamlIlTypes.String)).ToArray(); result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, types.Classes, types.XamlIlTypes.String, classNodes); return true; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs index dfabd66d17..652d9628ac 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs @@ -76,7 +76,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers throw new XamlParseException($"Cannot find '{property.Property}' on '{type}", node); if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, - new XamlAstTextNode(node, property.Value, context.Configuration.WellKnownTypes.String), + new XamlAstTextNode(node, property.Value, type: context.Configuration.WellKnownTypes.String), targetProperty.PropertyType, out var typedValue)) throw new XamlParseException( $"Cannot convert '{property.Value}' to '{targetProperty.PropertyType.GetFqn()}", diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs index c847bf54bc..f35d45e1e6 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs @@ -556,26 +556,33 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen) { + IXamlTypeBuilder newDelegateTypeBuilder = null; IXamlType specificDelegateType; - if (_method.ReturnType == context.Configuration.WellKnownTypes.Void && _method.Parameters.Count <= 8) + if (_method.ReturnType == context.Configuration.WellKnownTypes.Void && _method.Parameters.Count == 0) + { + specificDelegateType = context.Configuration.TypeSystem + .GetType("System.Action"); + } + else if (_method.ReturnType == context.Configuration.WellKnownTypes.Void && _method.Parameters.Count <= 16) { specificDelegateType = context.Configuration.TypeSystem .GetType($"System.Action`{_method.Parameters.Count}") .MakeGenericType(_method.Parameters); } - else if (_method.Parameters.Count <= 7) + else if (_method.Parameters.Count <= 16) { List genericParameters = new(); genericParameters.AddRange(_method.Parameters); genericParameters.Add(_method.ReturnType); specificDelegateType = context.Configuration.TypeSystem - .GetType($"System.Func`{_method.Parameters.Count}") + .GetType($"System.Func`{_method.Parameters.Count + 1}") .MakeGenericType(genericParameters); } else { // In this case, we need to emit our own delegate type. - specificDelegateType = null; + string delegateTypeName = context.Configuration.IdentifierGenerator.GenerateIdentifierPart(); + specificDelegateType = newDelegateTypeBuilder = context.DefineDelegateSubType(delegateTypeName, _method.ReturnType, _method.Parameters); } codeGen @@ -583,6 +590,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions .Ldtoken(specificDelegateType) .EmitCall(context.GetAvaloniaTypes() .CompiledBindingPathBuilder.FindMethod(m => m.Name == "Method")); + + newDelegateTypeBuilder?.CreateType(); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index 8e20d65eb5..daaac590e0 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit 8e20d65eb5f1efbae08e49b18f39bfdce32df7b3 +Subproject commit daaac590e078967b78045f74c38ef046d00d8582 diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 8a61458030..f18955c0b0 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -1042,6 +1042,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void SupportsMethodBindingAsDelegate() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + window.DataContext = new MethodDataContext(); + + Assert.IsAssignableFrom(typeof(Action), window.FindControl("action").Content); + Assert.IsAssignableFrom(typeof(Func), window.FindControl("func").Content); + Assert.IsAssignableFrom(typeof(Action), window.FindControl("action16").Content); + Assert.IsAssignableFrom(typeof(Func), window.FindControl("func16").Content); + Assert.True(typeof(Delegate).IsAssignableFrom(window.FindControl("customvoid").Content.GetType())); + Assert.True(typeof(Delegate).IsAssignableFrom(window.FindControl("customint").Content.GetType())); + } + } + void Throws(string type, Action cb) { try @@ -1131,4 +1162,16 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } } + + public class MethodDataContext + { + public void Action() { } + + public int Func() => 1; + + public void Action16(int i, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15, int i16) { } + public int Func16(int i, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15, int i16) => i; + public void CustomDelegateTypeVoid(int i, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) { } + public int CustomDelegateTypeInt(int i, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) => i; + } }