A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

134 lines
6.1 KiB

using System.Collections.Generic;
using System.Linq;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AvaloniaXamlIlSetterTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (!(node is XamlAstObjectNode on
&& on.Type.GetClrType().FullName == "Avalonia.Styling.Setter"))
return node;
IXamlType targetType = null;
IXamlLineInfo lineInfo = null;
var styleParent = context.ParentNodes()
.OfType<AvaloniaXamlIlTargetTypeMetadataNode>()
.FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
if (styleParent != null)
{
targetType = styleParent.TargetType.GetClrType()
?? throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType. If setter is not part of the style, you can set x:SetterTargetType directive on its parent.", node);
lineInfo = on;
}
if (targetType == null)
{
throw new XamlParseException("Could not determine target type of Setter", node);
}
IXamlType propType = null;
var property = @on.Children.OfType<XamlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "Property");
if (property != null)
{
var propertyName = property.Values.OfType<XamlAstTextNode>().FirstOrDefault()?.Text;
if (propertyName == null)
throw new XamlParseException("Setter.Property must be a string", node);
var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName,
new XamlAstClrTypeReference(lineInfo, targetType, false), property.Values[0]);
property.Values = new List<IXamlAstValueNode> {avaloniaPropertyNode};
propType = avaloniaPropertyNode.AvaloniaPropertyType;
}
else
{
var propertyPath = on.Children.OfType<XamlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "PropertyPath");
if (propertyPath == null)
throw new XamlX.XamlParseException("Setter without a property or property path is not valid", node);
if (propertyPath.Values[0] is IXamlIlPropertyPathNode ppn
&& ppn.PropertyType != null)
propType = ppn.PropertyType;
else
throw new XamlX.XamlParseException("Unable to get the property path property type", node);
}
var valueProperty = on.Children
.OfType<XamlAstXamlPropertyValueNode>().FirstOrDefault(p => p.Property.GetClrProperty().Name == "Value");
if (valueProperty?.Values?.Count == 1 && valueProperty.Values[0] is XamlAstTextNode)
{
if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, valueProperty.Values[0],
propType, out var converted))
throw new XamlParseException(
$"Unable to convert property value to {propType.GetFqn()}",
valueProperty.Values[0]);
valueProperty.Property = new SetterValueProperty(valueProperty.Property,
on.Type.GetClrType(), propType, context.GetAvaloniaTypes());
}
return node;
}
class SetterValueProperty : XamlAstClrProperty
{
public SetterValueProperty(IXamlLineInfo line, IXamlType setterType, IXamlType targetType,
AvaloniaXamlIlWellKnownTypes types)
: base(line, "Value", setterType, null)
{
Getter = setterType.Methods.First(m => m.Name == "get_Value");
var method = setterType.Methods.First(m => m.Name == "set_Value");
Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding, false));
Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType, false));
Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType, targetType.AcceptsNull()));
}
sealed class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
{
private readonly IXamlMethod _method;
private readonly IXamlType _type;
public IXamlType TargetType { get; }
public PropertySetterBinderParameters BinderParameters { get; }
public IReadOnlyList<IXamlType> Parameters { get; }
public void Emit(IXamlILEmitter codegen)
{
if (_type.IsValueType)
codegen.Box(_type);
codegen.EmitCall(_method, true);
}
public XamlIlDirectCallPropertySetter(IXamlMethod method, IXamlType type, bool allowNull)
{
_method = method;
_type = type;
Parameters = new[] {type};
TargetType = method.ThisOrFirstParameter();
BinderParameters = new PropertySetterBinderParameters
{
AllowXNull = allowNull,
AllowRuntimeNull = allowNull
};
}
private bool Equals(XamlIlDirectCallPropertySetter other)
=> Equals(_method, other._method) && Equals(_type, other._type);
public override bool Equals(object obj)
=> Equals(obj as XamlIlDirectCallPropertySetter);
public override int GetHashCode()
=> (_method.GetHashCode() * 397) ^ _type.GetHashCode();
}
}
}
}