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 AvaloniaObjectBindMethod { get; }
public IXamlMethod AvaloniaObjectSetValueMethod { get; } public IXamlMethod AvaloniaObjectSetValueMethod { get; }
public IXamlType IDisposable { get; } public IXamlType IDisposable { get; }
public IXamlType ICommand { get; }
public XamlTypeWellKnownTypes XamlIlTypes { get; } public XamlTypeWellKnownTypes XamlIlTypes { get; }
public XamlLanguageTypeMappings XamlIlMappings { get; } public XamlLanguageTypeMappings XamlIlMappings { get; }
public IXamlType Transitions { get; } public IXamlType Transitions { get; }
public IXamlType AssignBindingAttribute { get; } public IXamlType AssignBindingAttribute { get; }
public IXamlType DependsOnAttribute { get; }
public IXamlType UnsetValueType { get; } public IXamlType UnsetValueType { get; }
public IXamlType StyledElement { get; } public IXamlType StyledElement { get; }
public IXamlType IStyledElement { get; } public IXamlType IStyledElement { get; }
@ -96,8 +98,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority"); BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority");
IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding"); IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding");
IDisposable = cfg.TypeSystem.GetType("System.IDisposable"); IDisposable = cfg.TypeSystem.GetType("System.IDisposable");
IDisposable = cfg.TypeSystem.GetType("System.Windows.Input.ICommand");
Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions"); Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions");
AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute"); AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute");
AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject, AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject,
AvaloniaProperty, AvaloniaProperty,
IBinding, cfg.WellKnownTypes.Object); 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, selfType,
bindingPath.Path); bindingPath.Path);
transformed = TransformForTargetTyping(transformed, context);
bindingResultType = transformed.BindingResultType; bindingResultType = transformed.BindingResultType;
binding.Arguments[0] = transformed; binding.Arguments[0] = transformed;
} }
@ -54,6 +56,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
selfType, selfType,
bindingPathNode.Path); bindingPathNode.Path);
transformed = TransformForTargetTyping(transformed, context);
bindingResultType = transformed.BindingResultType; bindingResultType = transformed.BindingResultType;
bindingPathAssignment.Values[0] = transformed; bindingPathAssignment.Values[0] = transformed;
} }
@ -66,7 +70,32 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return bindingResultType; 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> transformNodes = new List<IXamlIlBindingPathElementNode>();
List<IXamlIlBindingPathElementNode> nodes = new List<IXamlIlBindingPathElementNode>(); List<IXamlIlBindingPathElementNode> nodes = new List<IXamlIlBindingPathElementNode>();
@ -553,13 +582,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
class XamlIlClrMethodPathElementNode : IXamlIlBindingPathElementNode class XamlIlClrMethodPathElementNode : IXamlIlBindingPathElementNode
{ {
private readonly IXamlMethod _method;
public XamlIlClrMethodPathElementNode(IXamlMethod method, IXamlType systemDelegateType) public XamlIlClrMethodPathElementNode(IXamlMethod method, IXamlType systemDelegateType)
{ {
_method = method; Method = method;
Type = systemDelegateType; Type = systemDelegateType;
} }
public IXamlMethod Method { get; }
public IXamlType Type { get; } public IXamlType Type { get; }
@ -567,35 +596,35 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{ {
IXamlTypeBuilder<IXamlILEmitter> newDelegateTypeBuilder = null; IXamlTypeBuilder<IXamlILEmitter> newDelegateTypeBuilder = null;
IXamlType specificDelegateType; 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 specificDelegateType = context.Configuration.TypeSystem
.GetType("System.Action"); .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 specificDelegateType = context.Configuration.TypeSystem
.GetType($"System.Action`{_method.Parameters.Count}") .GetType($"System.Action`{Method.Parameters.Count}")
.MakeGenericType(_method.Parameters); .MakeGenericType(Method.Parameters);
} }
else if (_method.Parameters.Count <= 16) else if (Method.Parameters.Count <= 16)
{ {
List<IXamlType> genericParameters = new(); List<IXamlType> genericParameters = new();
genericParameters.AddRange(_method.Parameters); genericParameters.AddRange(Method.Parameters);
genericParameters.Add(_method.ReturnType); genericParameters.Add(Method.ReturnType);
specificDelegateType = context.Configuration.TypeSystem specificDelegateType = context.Configuration.TypeSystem
.GetType($"System.Func`{_method.Parameters.Count + 1}") .GetType($"System.Func`{Method.Parameters.Count + 1}")
.MakeGenericType(genericParameters); .MakeGenericType(genericParameters);
} }
else else
{ {
// In this case, we need to emit our own delegate type. // In this case, we need to emit our own delegate type.
string delegateTypeName = context.Configuration.IdentifierGenerator.GenerateIdentifierPart(); 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 codeGen
.Ldtoken(_method) .Ldtoken(Method)
.Ldtoken(specificDelegateType) .Ldtoken(specificDelegateType)
.EmitCall(context.GetAvaloniaTypes() .EmitCall(context.GetAvaloniaTypes()
.CompiledBindingPathBuilder.FindMethod(m => m.Name == "Method")); .CompiledBindingPathBuilder.FindMethod(m => m.Name == "Method"));
@ -803,7 +832,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult> class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
{ {
private readonly List<IXamlIlBindingPathElementNode> _transformElements; private readonly List<IXamlIlBindingPathElementNode> _transformElements;
private readonly List<IXamlIlBindingPathElementNode> _elements;
public XamlIlBindingPathNode(IXamlLineInfo lineInfo, public XamlIlBindingPathNode(IXamlLineInfo lineInfo,
IXamlType bindingPathType, IXamlType bindingPathType,
@ -812,16 +840,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{ {
Type = new XamlAstClrTypeReference(lineInfo, bindingPathType, false); Type = new XamlAstClrTypeReference(lineInfo, bindingPathType, false);
_transformElements = transformElements; _transformElements = transformElements;
_elements = elements; Elements = elements;
} }
public IXamlType BindingResultType public IXamlType BindingResultType
=> _transformElements.Count > 0 => _transformElements.Count > 0
? _transformElements[0].Type ? _transformElements[0].Type
: _elements[_elements.Count - 1].Type; : Elements[Elements.Count - 1].Type;
public IXamlAstTypeReference Type { get; } public IXamlAstTypeReference Type { get; }
public List<IXamlIlBindingPathElementNode> Elements { get; }
public XamlILNodeEmitResult Emit(XamlIlEmitContext context, IXamlILEmitter codeGen) public XamlILNodeEmitResult Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
{ {
var types = context.GetAvaloniaTypes(); var types = context.GetAvaloniaTypes();
@ -832,7 +862,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
transform.Emit(context, codeGen); transform.Emit(context, codeGen);
} }
foreach (var element in _elements) foreach (var element in Elements)
{ {
element.Emit(context, codeGen); element.Emit(context, codeGen);
} }
@ -850,11 +880,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
_transformElements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor); _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