Browse Source

Resolve all AvaloniaProperty values at compile time

xamlil-debug-info
Nikita Tsukanov 7 years ago
parent
commit
af57196f9b
  1. 4
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  2. 26
      src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
  3. 21
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaPropertyDescriptorEmitter.cs
  4. 4
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  5. 40
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  6. 65
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
  7. 17
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs
  8. 23
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  9. 53
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
  10. 79
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

4
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -52,7 +52,9 @@
<Compile Include="Templates\TemplateLoader.cs" />
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="XamlIl\AvaloniaXamlIlRuntimeCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaPropertyDescriptorEmitter.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlMetadataRemover.cs" />
<Compile Include="XamlIl\CompilerExtensions\XamlIlAvaloniaPropertyHelper.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlLanguage.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AddNameScopeRegistration.cs" />

26
src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs

@ -114,7 +114,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
InitializeSre();
var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly);
var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_sreTypeSystem, asm,
_sreMappings, _sreXmlns, CustomValueConverter));
_sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter));
var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri);
IXamlIlType overrideType = null;
@ -214,7 +214,8 @@ namespace Avalonia.Markup.Xaml.XamlIl
var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_cecilTypeSystem,
localAssembly == null ? null : _cecilTypeSystem.FindAssembly(localAssembly.GetName().Name),
_cecilMappings, XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings), CustomValueConverter));
_cecilMappings, XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings),
AvaloniaXamlIlLanguage.CustomValueConverter));
compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType);
var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll");
using(var f = File.Create(asmPath))
@ -224,26 +225,5 @@ namespace Avalonia.Markup.Xaml.XamlIl
_cecilGeneratedCache[safeUri] = loaded;
return LoadOrPopulate(loaded, rootInstance);
}
private static bool CustomValueConverter(XamlIlAstTransformationContext context,
IXamlIlAstValueNode node, IXamlIlType type, out IXamlIlAstValueNode result)
{
if (type.FullName == "System.TimeSpan"
&& node is XamlIlAstTextNode tn
&& !tn.Text.Contains(":"))
{
var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture);
result = new XamlIlStaticOrTargetedReturnMethodCallNode(tn,
type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double),
new[]
{
new XamlIlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
});
return true;
}
result = null;
return false;
}
}
}

21
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaPropertyDescriptorEmitter.cs

@ -1,21 +0,0 @@
using System.Linq;
using XamlIl.Transform;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
public class AvaloniaPropertyDescriptorEmitter
{
public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, IXamlIlProperty property)
{
var type = (property.Getter ?? property.Setter).DeclaringType;
var name = property.Name + "Property";
var found = type.Fields.FirstOrDefault(f => f.IsStatic && f.Name == name);
if (found == null)
return false;
emitter.Ldsfld(found);
return true;
}
}
}

4
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -28,12 +28,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new IXamlIlAstTransformer[]
{
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlSetterTransformer()
new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
}
);
// After everything else
Transformers.Add(new AddNameScopeRegistration());
Transformers.Add(new AvaloniaXamlIlMetadataRemover());
}

40
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@ -1,5 +1,9 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.TypeSystem;
@ -64,7 +68,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return true;
return false;
},
ProvideValueTargetPropertyEmitter = AvaloniaPropertyDescriptorEmitter.Emit,
ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.Emit,
};
rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv);
return rv;
@ -92,7 +96,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
=> AddType(typeSystem.GetType(type), typeSystem.GetType(conv));
Add("Avalonia.AvaloniaProperty","Avalonia.Markup.Xaml.Converters.AvaloniaPropertyTypeConverter");
//Add("Avalonia.AvaloniaProperty","Avalonia.Markup.Xaml.Converters.AvaloniaPropertyTypeConverter");
Add("Avalonia.Media.Imaging.IBitmap","Avalonia.Markup.Xaml.Converters.BitmapTypeConverter");
var ilist = typeSystem.GetType("System.Collections.Generic.IList`1");
AddType(ilist.MakeGenericType(typeSystem.GetType("Avalonia.Point")),
@ -152,5 +156,37 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return null;
}
}
public static bool CustomValueConverter(XamlIlAstTransformationContext context,
IXamlIlAstValueNode node, IXamlIlType type, out IXamlIlAstValueNode result)
{
if (type.FullName == "System.TimeSpan"
&& node is XamlIlAstTextNode tn
&& !tn.Text.Contains(":"))
{
var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture);
result = new XamlIlStaticOrTargetedReturnMethodCallNode(tn,
type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double),
new[]
{
new XamlIlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
});
return true;
}
if (type.FullName == "Avalonia.AvaloniaProperty")
{
var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
if (scope == null)
throw new XamlIlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
if (!(node is XamlIlAstTextNode text))
throw new XamlIlLoadException("Property should be a text node", node);
result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text);
return true;
}
result = null;
return false;
}
}
}

65
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs

@ -0,0 +1,65 @@
using System.Linq;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (!(node is XamlIlAstObjectNode on
&& on.Type.GetClrType().FullName == "Avalonia.Markup.Xaml.Templates.ControlTemplate"))
return node;
var tt = on.Children.OfType<XamlIlAstXamlPropertyValueNode>().FirstOrDefault(ch =>
ch.Property.GetClrProperty().Name == "TargetType");
if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlTargetTypeMetadataNode)
// Deja vu. I've just been in this place before
return node;
IXamlIlAstTypeReference targetType;
if ((tt?.Values.FirstOrDefault() is XamlIlTypeExtensionNode tn))
{
targetType = tn.Type;
}
else
{
var parentScope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>()
.FirstOrDefault();
if (parentScope?.Type == AvaloniaXamlIlTargetTypeMetadataNode.ScopeType.Style)
targetType = parentScope.TargetType;
else
targetType = new XamlIlAstClrTypeReference(node,
context.Configuration.TypeSystem.GetType("Avalonia.Controls.Control"));
}
return new AvaloniaXamlIlTargetTypeMetadataNode(on, targetType,
AvaloniaXamlIlTargetTypeMetadataNode.ScopeType.ControlTemplate);
}
}
class AvaloniaXamlIlTargetTypeMetadataNode : XamlIlValueWithSideEffectNodeBase
{
public IXamlIlAstTypeReference TargetType { get; set; }
public ScopeType Type { get; }
public enum ScopeType
{
Style,
ControlTemplate
}
public AvaloniaXamlIlTargetTypeMetadataNode(IXamlIlAstValueNode value, IXamlIlAstTypeReference targetType,
ScopeType type)
: base(value, value)
{
TargetType = targetType;
Type = type;
}
}
}

17
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs

@ -0,0 +1,17 @@
using System.Linq;
using XamlIl.Ast;
using XamlIl.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
public class AvaloniaXamlIlMetadataRemover : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (node is AvaloniaXamlIlTargetTypeMetadataNode md)
return md.Value;
return node;
}
}
}

23
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs

@ -15,14 +15,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (!(node is XamlIlAstXamlPropertyValueNode pn
&& pn.Property.GetClrProperty().PropertyType.FullName == "Avalonia.Styling.Selector"))
if (!(node is XamlIlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Style"))
return node;
var pn = on.Children.OfType<XamlIlAstXamlPropertyValueNode>()
.FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector");
if (pn == null)
return node;
if (pn.Values.Count != 1)
throw new XamlIlParseException("Selector property should should have exactly one value", node);
if (!(pn.Values[0] is XamlIlAstTextNode tn))
if (pn.Values[0] is XamlIlSelectorNode)
//Deja vu. I've just been in this place before
return node;
if (!(pn.Values[0] is XamlIlAstTextNode tn))
throw new XamlIlParseException("Selector property should be a text node", node);
var selectorType = pn.Property.GetClrProperty().PropertyType;
@ -104,7 +114,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var selector = Create(parsed, (p, n)
=> XamlIlTypeReferenceResolver.ResolveType(context, $"{p}:{n}", node, true));
pn.Values[0] = selector;
return node;
return new AvaloniaXamlIlTargetTypeMetadataNode(on,
new XamlIlAstClrTypeReference(selector, selector.TargetType),
AvaloniaXamlIlTargetTypeMetadataNode.ScopeType.Style);
}
}
@ -263,7 +276,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public override IXamlIlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
if (!AvaloniaPropertyDescriptorEmitter.Emit(context, codeGen, Property))
if (!XamlIlAvaloniaPropertyHelper.Emit(context, codeGen, Property))
throw new XamlIlLoadException(
$"{Property.Name} of {(Property.Setter ?? Property.Getter).DeclaringType.GetFqn()} doesn't seem to be an AvaloniaProperty",
this);

53
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs

@ -43,36 +43,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (propertyName == null)
throw new XamlIlParseException("Setter.Property must be a string", node);
var selectorTypeReference = new XamlIlAstClrTypeReference(selector, selector.TargetType);
XamlIlAstNamePropertyReference forgedReference;
var parser = new PropertyParser();
var parsedPropertyName = parser.Parse(new CharacterReader(propertyName.AsSpan()));
if(parsedPropertyName.owner == null)
forgedReference = new XamlIlAstNamePropertyReference(property.Values[0], selectorTypeReference,
propertyName, selectorTypeReference);
else
{
var xmlOwner = parsedPropertyName.ns;
if (xmlOwner != null)
xmlOwner += ":";
xmlOwner += parsedPropertyName.owner;
var t = XamlIlTypeReferenceResolver.ResolveType(context, xmlOwner, property.Values[0], true);
var tref = new XamlIlAstClrTypeReference(property.Values[0], t);
forgedReference = new XamlIlAstNamePropertyReference(property.Values[0],
tref, parsedPropertyName.name, tref);
}
var clrProperty =
((XamlIlAstClrPropertyReference)new XamlIlPropertyReferenceResolver().Transform(context,
forgedReference)).Property;
var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName,
new XamlIlAstClrTypeReference(selector, selector.TargetType), property.Values[0]);
property.Values = new List<IXamlIlAstValueNode>
{
new XamlIlAvaloniaPropertyNode(property.Values[0], property.Property.GetClrProperty().PropertyType,
clrProperty)
avaloniaPropertyNode
};
var valueProperty = on.Children
@ -80,9 +56,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (valueProperty?.Values?.Count == 1 && valueProperty.Values[0] is XamlIlAstTextNode)
{
if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, valueProperty.Values[0],
clrProperty.PropertyType, out var converted))
avaloniaPropertyNode.Property.PropertyType, out var converted))
throw new XamlIlParseException(
$"Unable to convert property value to {clrProperty.PropertyType.GetFqn()}",
$"Unable to convert property value to {avaloniaPropertyNode.Property.PropertyType.GetFqn()}",
valueProperty.Values[0]);
valueProperty.Values = new List<IXamlIlAstValueNode>
@ -96,23 +72,4 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
}
class XamlIlAvaloniaPropertyNode : XamlIlAstNode, IXamlIlAstValueNode, IXamlIlAstEmitableNode
{
public XamlIlAvaloniaPropertyNode(IXamlIlLineInfo lineInfo, IXamlIlType type, IXamlIlProperty property) : base(lineInfo)
{
Type = new XamlIlAstClrTypeReference(this, type);
Property = property;
}
public IXamlIlProperty Property { get; set; }
public IXamlIlAstTypeReference Type { get; }
public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
if (!AvaloniaPropertyDescriptorEmitter.Emit(context, codeGen, Property))
throw new XamlIlLoadException(Property.Name + " is not an AvaloniaProperty", this);
return XamlIlNodeEmitResult.Type(0, Type.GetClrType());
}
}
}

79
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Markup.Xaml.Parsers;
using Avalonia.Utilities;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.Transform.Transformers;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
class XamlIlAvaloniaPropertyHelper
{
public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, IXamlIlProperty property)
{
var type = (property.Getter ?? property.Setter).DeclaringType;
var name = property.Name + "Property";
var found = type.Fields.FirstOrDefault(f => f.IsStatic && f.Name == name);
if (found == null)
return false;
emitter.Ldsfld(found);
return true;
}
public static XamlIlAvaloniaPropertyNode CreateNode(XamlIlAstTransformationContext context,
string propertyName, IXamlIlAstTypeReference selectorTypeReference, IXamlIlLineInfo lineInfo)
{
XamlIlAstNamePropertyReference forgedReference;
var parser = new PropertyParser();
var parsedPropertyName = parser.Parse(new CharacterReader(propertyName.AsSpan()));
if(parsedPropertyName.owner == null)
forgedReference = new XamlIlAstNamePropertyReference(lineInfo, selectorTypeReference,
propertyName, selectorTypeReference);
else
{
var xmlOwner = parsedPropertyName.ns;
if (xmlOwner != null)
xmlOwner += ":";
xmlOwner += parsedPropertyName.owner;
var t = XamlIlTypeReferenceResolver.ResolveType(context, xmlOwner, lineInfo, true);
var tref = new XamlIlAstClrTypeReference(lineInfo, t);
forgedReference = new XamlIlAstNamePropertyReference(lineInfo,
tref, parsedPropertyName.name, tref);
}
var clrProperty =
((XamlIlAstClrPropertyReference)new XamlIlPropertyReferenceResolver().Transform(context,
forgedReference)).Property;
return new XamlIlAvaloniaPropertyNode(lineInfo,
context.Configuration.TypeSystem.GetType("Avalonia.AvaloniaProperty"),
clrProperty);
}
}
class XamlIlAvaloniaPropertyNode : XamlIlAstNode, IXamlIlAstValueNode, IXamlIlAstEmitableNode
{
public XamlIlAvaloniaPropertyNode(IXamlIlLineInfo lineInfo, IXamlIlType type, IXamlIlProperty property) : base(lineInfo)
{
Type = new XamlIlAstClrTypeReference(this, type);
Property = property;
}
public IXamlIlProperty Property { get; }
public IXamlIlAstTypeReference Type { get; }
public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
if (!XamlIlAvaloniaPropertyHelper.Emit(context, codeGen, Property))
throw new XamlIlLoadException(Property.Name + " is not an AvaloniaProperty", this);
return XamlIlNodeEmitResult.Type(0, Type.GetClrType());
}
}
}
Loading…
Cancel
Save