Browse Source

Hook up target-typing for ICommand.

pull/7246/head
Jeremy Koritzinsky 4 years ago
parent
commit
a383c77650
No known key found for this signature in database GPG Key ID: 25A7D1C8126B7A16
  1. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  2. 70
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -19,10 +19,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlMethod AvaloniaObjectBindMethod { get; }
public IXamlMethod AvaloniaObjectSetValueMethod { get; }
public IXamlType IDisposable { get; }
public IXamlType ICommand { get; }
public XamlTypeWellKnownTypes XamlIlTypes { get; }
public XamlLanguageTypeMappings XamlIlMappings { get; }
public IXamlType Transitions { get; }
public IXamlType AssignBindingAttribute { get; }
public IXamlType DependsOnAttribute { get; }
public IXamlType UnsetValueType { get; }
public IXamlType StyledElement { get; }
public IXamlType IStyledElement { get; }
@ -96,8 +98,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority");
IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding");
IDisposable = cfg.TypeSystem.GetType("System.IDisposable");
IDisposable = cfg.TypeSystem.GetType("System.Windows.Input.ICommand");
Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions");
AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute");
AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject,
AvaloniaProperty,
IBinding, cfg.WellKnownTypes.Object);

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

@ -32,6 +32,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
selfType,
bindingPath.Path);
transformed = TransformForTargetTyping(transformed, context);
bindingResultType = transformed.BindingResultType;
binding.Arguments[0] = transformed;
}
@ -54,6 +56,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
selfType,
bindingPathNode.Path);
transformed = TransformForTargetTyping(transformed, context);
bindingResultType = transformed.BindingResultType;
bindingPathAssignment.Values[0] = transformed;
}
@ -66,7 +70,32 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return bindingResultType;
}
private static IXamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func<IXamlType> startTypeResolver, IXamlType selfType, IEnumerable<BindingExpressionGrammar.INode> bindingExpression)
private static XamlIlBindingPathNode TransformForTargetTyping(XamlIlBindingPathNode transformed, AstTransformationContext context)
{
if (context.ParentNodes().OfType<XamlPropertyAssignmentNode>().FirstOrDefault().Property.Getter.ReturnType == context.GetAvaloniaTypes().ICommand
&& transformed.Elements[transformed.Elements.Count - 1] is XamlIlClrMethodPathElementNode method)
{
IXamlMethod executeMethod = method.Method;
IXamlMethod canExecuteMethod = executeMethod.DeclaringType.FindMethod(new FindMethodMethodSignature($"Can{executeMethod.Name}", context.Configuration.WellKnownTypes.Boolean, context.Configuration.WellKnownTypes.Object));
List<string> dependsOnProperties = new();
if (canExecuteMethod is not null)
{
foreach (var attr in canExecuteMethod.CustomAttributes)
{
if (attr.Type == context.GetAvaloniaTypes().DependsOnAttribute)
{
dependsOnProperties.Add((string)attr.Parameters[0]);
}
}
}
transformed.Elements.RemoveAt(transformed.Elements.Count - 1);
transformed.Elements.Add(new XamlIlClrMethodAsCommandPathElementNode(context.GetAvaloniaTypes().ICommand, executeMethod, canExecuteMethod, dependsOnProperties));
}
return transformed;
}
private static XamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func<IXamlType> startTypeResolver, IXamlType selfType, IEnumerable<BindingExpressionGrammar.INode> bindingExpression)
{
List<IXamlIlBindingPathElementNode> transformNodes = new List<IXamlIlBindingPathElementNode>();
List<IXamlIlBindingPathElementNode> nodes = new List<IXamlIlBindingPathElementNode>();
@ -553,13 +582,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
class XamlIlClrMethodPathElementNode : IXamlIlBindingPathElementNode
{
private readonly IXamlMethod _method;
public XamlIlClrMethodPathElementNode(IXamlMethod method, IXamlType systemDelegateType)
{
_method = method;
Method = method;
Type = systemDelegateType;
}
public IXamlMethod Method { get; }
public IXamlType Type { get; }
@ -567,35 +596,35 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
IXamlTypeBuilder<IXamlILEmitter> newDelegateTypeBuilder = null;
IXamlType specificDelegateType;
if (_method.ReturnType == context.Configuration.WellKnownTypes.Void && _method.Parameters.Count == 0)
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)
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);
.GetType($"System.Action`{Method.Parameters.Count}")
.MakeGenericType(Method.Parameters);
}
else if (_method.Parameters.Count <= 16)
else if (Method.Parameters.Count <= 16)
{
List<IXamlType> genericParameters = new();
genericParameters.AddRange(_method.Parameters);
genericParameters.Add(_method.ReturnType);
genericParameters.AddRange(Method.Parameters);
genericParameters.Add(Method.ReturnType);
specificDelegateType = context.Configuration.TypeSystem
.GetType($"System.Func`{_method.Parameters.Count + 1}")
.GetType($"System.Func`{Method.Parameters.Count + 1}")
.MakeGenericType(genericParameters);
}
else
{
// In this case, we need to emit our own delegate type.
string delegateTypeName = context.Configuration.IdentifierGenerator.GenerateIdentifierPart();
specificDelegateType = newDelegateTypeBuilder = context.DefineDelegateSubType(delegateTypeName, _method.ReturnType, _method.Parameters);
specificDelegateType = newDelegateTypeBuilder = context.DefineDelegateSubType(delegateTypeName, Method.ReturnType, Method.Parameters);
}
codeGen
.Ldtoken(_method)
.Ldtoken(Method)
.Ldtoken(specificDelegateType)
.EmitCall(context.GetAvaloniaTypes()
.CompiledBindingPathBuilder.FindMethod(m => m.Name == "Method"));
@ -803,7 +832,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
{
private readonly List<IXamlIlBindingPathElementNode> _transformElements;
private readonly List<IXamlIlBindingPathElementNode> _elements;
public XamlIlBindingPathNode(IXamlLineInfo lineInfo,
IXamlType bindingPathType,
@ -812,16 +840,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
Type = new XamlAstClrTypeReference(lineInfo, bindingPathType, false);
_transformElements = transformElements;
_elements = elements;
Elements = elements;
}
public IXamlType BindingResultType
=> _transformElements.Count > 0
? _transformElements[0].Type
: _elements[_elements.Count - 1].Type;
: Elements[Elements.Count - 1].Type;
public IXamlAstTypeReference Type { get; }
public List<IXamlIlBindingPathElementNode> Elements { get; }
public XamlILNodeEmitResult Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
{
var types = context.GetAvaloniaTypes();
@ -832,7 +862,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
transform.Emit(context, codeGen);
}
foreach (var element in _elements)
foreach (var element in Elements)
{
element.Emit(context, codeGen);
}
@ -850,11 +880,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
_transformElements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
}
}
for (int i = 0; i < _elements.Count; i++)
for (int i = 0; i < Elements.Count; i++)
{
if (_elements[i] is IXamlAstNode ast)
if (Elements[i] is IXamlAstNode ast)
{
_elements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
Elements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
}
}
}

Loading…
Cancel
Save