Browse Source

Styles are now compiled

xamlil-debug-info
Nikita Tsukanov 8 years ago
parent
commit
73ababfff7
  1. 32
      samples/ControlCatalog/MainWindow.xaml
  2. 11
      src/Avalonia.Styling/Styling/Selectors.cs
  3. 3
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  4. 9
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  5. 2
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  6. 2
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
  7. 278
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  8. 118
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
  9. 2
      src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github

32
samples/ControlCatalog/MainWindow.xaml

@ -5,35 +5,5 @@
xmlns:local="clr-namespace:ControlCatalog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainWindow">
<!--<local:MainView/>-->
<ScrollViewer>
<StackPanel>
<pages:AutoCompleteBoxPage/>
<pages:BorderPage/>
<pages:ButtonPage/>
<pages:ButtonSpinnerPage/>
<pages:CalendarPage/>
<pages:CanvasPage/>
<pages:CarouselPage/>
<pages:CheckBoxPage/>
<pages:ContextMenuPage/>
<pages:DatePickerPage/>
<pages:DragAndDropPage/>
<pages:DropDownPage/>
<pages:ExpanderPage/>
<pages:ImagePage/>
<pages:LayoutTransformControlPage/>
<pages:ListBoxPage/>
<pages:MenuPage/>
<pages:NumericUpDownPage/>
<pages:ProgressBarPage/>
<pages:RadioButtonPage/>
<pages:SliderPage/>
<pages:TabControlPage/>
<pages:TextBoxPage/>
<pages:ToolTipPage/>
<pages:TreeViewPage/>
<pages:ViewboxPage/>
</StackPanel>
</ScrollViewer>
<local:MainView/>
</Window>

11
src/Avalonia.Styling/Styling/Selectors.cs

@ -104,6 +104,17 @@ namespace Avalonia.Styling
{
return new NotSelector(previous, argument(null));
}
/// <summary>
/// Returns a selector which inverts the results of selector argument.
/// </summary>
/// <param name="previous">The previous selector.</param>
/// <param name="argument">The selector to be not-ed.</param>
/// <returns>The selector.</returns>
public static Selector Not(this Selector previous, Selector argument)
{
return new NotSelector(previous, argument);
}
/// <summary>
/// Returns a selector which matches a type.

3
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -56,8 +56,10 @@
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlLanguage.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AddNameScopeRegistration.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlSetterTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\IgnoredDirectivesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\KnownPseudoMarkupExtensionsTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlSelectorTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\XNameTransformer.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlParentStackProvider.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
@ -67,6 +69,7 @@
<Compile Include="XamlIl\xamlil.github\src\XamlIl\**\*.cs" />
<Compile Include="XamlIl\xamlil.github\src\XamlIl.Cecil\**\*.cs" />
<Compile Remove="XamlIl\xamlil.github\**\obj\**\*.cs" />
<Compile Include="..\Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />

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

@ -23,7 +23,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
Transformers.Insert(Transformers.FindIndex(x => x is XamlIlXamlPropertyValueTransformer),
new KnownPseudoMarkupExtensionsTransformer());
Transformers.InsertRange(Transformers.FindIndex(x => x is XamlIlNewObjectTransformer),
new IXamlIlAstTransformer[]
{
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlSetterTransformer()
}
);
// After everything else
Transformers.Add(new AddNameScopeRegistration());

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

@ -57,6 +57,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
runtimeHelpers.FindMethod(m => m.Name == "CreateInnerServiceProviderV1"),
ShouldIgnoreMarkupExtensionCustomResultForProperty = (prop, customType) =>
{
if (prop.Name == "Value" && prop.Setter?.DeclaringType.FullName == "Avalonia.Styling.Setter")
return true;
if (customType.Equals(bindingType) &&
prop.CustomAttributes.Any(a => a.Type.Equals(assignBindingAttribute)))
return true;

2
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs

@ -31,7 +31,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
Value = pa.Value;
}
public override void VisitChildren(XamlIlAstVisitorDelegate visitor)
public override void VisitChildren(IXamlIlAstVisitor visitor)
=> Value = (IXamlIlAstValueNode)Value.Visit(visitor);
public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)

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

@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Markup.Parsers;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.Transform.Transformers;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AvaloniaXamlIlSelectorTransformer : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (!(node is XamlIlAstXamlPropertyValueNode pn
&& pn.Property.GetClrProperty().PropertyType.FullName == "Avalonia.Styling.Selector"))
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))
return node;
var selectorType = pn.Property.GetClrProperty().PropertyType;
XamlIlSelectorNode Create(IEnumerable<SelectorGrammar.ISyntax> syntax,
Func<string, string, IXamlIlType> typeResolver)
{
XamlIlSelectorNode result = new XamlIlSelectorInitialNode(node, selectorType);
foreach (var i in syntax)
{
switch (i)
{
case SelectorGrammar.OfTypeSyntax ofType:
result = new XamlIlTypeSelector(result, typeResolver(ofType.Xmlns, ofType.TypeName), true);
break;
case SelectorGrammar.IsSyntax @is:
result = new XamlIlTypeSelector(result, typeResolver(@is.Xmlns, @is.TypeName), false);
break;
case SelectorGrammar.ClassSyntax @class:
result = new XamlIlStringSelector(result, XamlIlStringSelector.Type.Class, @class.Class);
break;
case SelectorGrammar.NameSyntax name:
result = new XamlIlStringSelector(result, XamlIlStringSelector.Type.Name, name.Name);
break;
case SelectorGrammar.PropertySyntax property:
{
var type = result?.TargetType;
if (type == null)
throw new XamlIlParseException("Property selectors must be applied to a type.", node);
var targetProperty =
type.GetAllProperties().FirstOrDefault(p => p.Name == property.Property);
if (targetProperty == null)
throw new XamlIlParseException($"Cannot find '{property.Property}' on '{type}", node);
if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context,
new XamlIlAstTextNode(node, property.Value, context.Configuration.WellKnownTypes.String),
targetProperty.PropertyType, out var typedValue))
throw new XamlIlParseException(
$"Cannot convert '{property.Value}' to '{targetProperty.PropertyType.GetFqn()}",
node);
result = new XamlIlPropertyEqualsSelector(result, targetProperty, typedValue);
break;
}
case SelectorGrammar.ChildSyntax child:
result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.Type.Child);
break;
case SelectorGrammar.DescendantSyntax descendant:
result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.Type.Descendant);
break;
case SelectorGrammar.TemplateSyntax template:
result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.Type.Template);
break;
case SelectorGrammar.NotSyntax not:
result = new XamlIlNotSelector(result, Create(not.Argument, typeResolver));
break;
default:
throw new XamlIlParseException($"Unsupported selector grammar '{i.GetType()}'.", node);
}
}
return result;
}
IEnumerable<SelectorGrammar.ISyntax> parsed;
try
{
parsed = SelectorGrammar.Parse(tn.Text);
}
catch (Exception e)
{
throw new XamlIlParseException("Unable to parse selector: " + e.Message, node);
}
var selector = Create(parsed, (p, n)
=> XamlIlTypeReferenceResolver.ResolveType(context, $"{p}:{n}", node, true));
pn.Values[0] = selector;
return node;
}
}
abstract class XamlIlSelectorNode : XamlIlAstNode, IXamlIlAstValueNode, IXamlIlAstEmitableNode
{
public XamlIlSelectorNode Previous { get; }
public abstract IXamlIlType TargetType { get; }
public XamlIlSelectorNode(XamlIlSelectorNode previous,
IXamlIlLineInfo info = null,
IXamlIlType selectorType = null) : base(info ?? previous)
{
Previous = previous;
Type = selectorType == null ? previous.Type : new XamlIlAstClrTypeReference(this, selectorType);
}
public IXamlIlAstTypeReference Type { get; }
public virtual XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
if (Previous != null)
context.Emit(Previous, codeGen, Type.GetClrType());
DoEmit(context, codeGen);
return XamlIlNodeEmitResult.Type(0, Type.GetClrType());
}
protected abstract void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen);
protected void EmitCall(XamlIlEmitContext context, IXamlIlEmitter codeGen, Func<IXamlIlMethod, bool> method)
{
var selectors = context.Configuration.TypeSystem.GetType("Avalonia.Styling.Selectors");
var found = selectors.FindMethod(m => m.IsStatic && m.Parameters.Count > 0 &&
m.Parameters[0].FullName == "Avalonia.Styling.Selector"
&& method(m));
codeGen.EmitCall(found);
}
}
class XamlIlSelectorInitialNode : XamlIlSelectorNode
{
public XamlIlSelectorInitialNode(IXamlIlLineInfo info,
IXamlIlType selectorType) : base(null, info, selectorType)
{
}
public override IXamlIlType TargetType => null;
protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen) => codeGen.Ldnull();
}
class XamlIlTypeSelector : XamlIlSelectorNode
{
public bool Concrete { get; }
public XamlIlTypeSelector(XamlIlSelectorNode previous, IXamlIlType type, bool concrete) : base(previous)
{
TargetType = type;
Concrete = concrete;
}
public override IXamlIlType TargetType { get; }
protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
var name = Concrete ? "OfType" : "Is";
codeGen.Ldtype(TargetType);
EmitCall(context, codeGen,
m => m.Name == name && m.Parameters.Count == 2 && m.Parameters[1].FullName == "System.Type");
}
}
class XamlIlStringSelector : XamlIlSelectorNode
{
public string String { get; set; }
public enum Type
{
Class,
Name
}
private Type _type;
public XamlIlStringSelector(XamlIlSelectorNode previous, Type type, string s) : base(previous)
{
_type = type;
String = s;
}
public override IXamlIlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
codeGen.Ldstr(String);
var name = _type.ToString();
EmitCall(context, codeGen,
m => m.Name == name && m.Parameters.Count == 2 && m.Parameters[1].FullName == "System.String");
}
}
class XamlIlCombinatorSelector : XamlIlSelectorNode
{
private readonly Type _type;
public enum Type
{
Child,
Descendant,
Template
}
public XamlIlCombinatorSelector(XamlIlSelectorNode previous, Type type) : base(previous)
{
_type = type;
}
public override IXamlIlType TargetType => null;
protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
var name = _type.ToString();
EmitCall(context, codeGen,
m => m.Name == name && m.Parameters.Count == 1);
}
}
class XamlIlNotSelector : XamlIlSelectorNode
{
public XamlIlSelectorNode Argument { get; }
public XamlIlNotSelector(XamlIlSelectorNode previous, XamlIlSelectorNode argument) : base(previous)
{
}
public override IXamlIlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
context.Emit(Argument, codeGen, Type.GetClrType());
EmitCall(context, codeGen,
m => m.Name == "Not" && m.Parameters.Count == 2 && m.Parameters[1].Equals(Type.GetClrType()));
}
}
class XamlIlPropertyEqualsSelector : XamlIlSelectorNode
{
public XamlIlPropertyEqualsSelector(XamlIlSelectorNode previous,
IXamlIlProperty property,
IXamlIlAstValueNode value)
: base(previous)
{
Property = property;
Value = value;
}
public IXamlIlProperty Property { get; set; }
public IXamlIlAstValueNode Value { get; set; }
public override IXamlIlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
if (!AvaloniaPropertyDescriptorEmitter.Emit(context, codeGen, Property))
throw new XamlIlLoadException(
$"{Property.Name} of {(Property.Setter ?? Property.Getter).DeclaringType.GetFqn()} doesn't seem to be an AvaloniaProperty",
this);
context.Emit(Value, codeGen, context.Configuration.WellKnownTypes.Object);
EmitCall(context, codeGen,
m => m.Name == "PropertyEquals"
&& m.Parameters.Count == 3
&& m.Parameters[1].FullName == "Avalonia.AvaloniaProperty"
&& m.Parameters[2].Equals(context.Configuration.WellKnownTypes.Object));
}
}
}

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

@ -0,0 +1,118 @@
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.Transformers
{
public class AvaloniaXamlIlSetterTransformer : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (!(node is XamlIlAstObjectNode on
&& on.Type.GetClrType().FullName == "Avalonia.Styling.Setter"))
return node;
var parent = context.ParentNodes().OfType<XamlIlAstObjectNode>()
.FirstOrDefault(x => x.Type.GetClrType().FullName == "Avalonia.Styling.Style");
if (parent == null)
throw new XamlIlParseException(
"Avalonia.Styling.Setter is only valid inside Avalonia.Styling.Style", node);
var selectorProperty = parent.Children.OfType<XamlIlAstXamlPropertyValueNode>()
.FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector");
if (selectorProperty == null)
throw new XamlIlParseException(
"Can not find parent Style Selector", node);
var selector = selectorProperty.Values.FirstOrDefault() as XamlIlSelectorNode;
if (selector?.TargetType == null)
throw new XamlIlParseException(
"Can not resolve parent Style Selector type", node);
var property = @on.Children.OfType<XamlIlAstXamlPropertyValueNode>()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "Property");
if (property == null)
throw new XamlIlParseException("Setter without a property is not valid", node);
var propertyName = property.Values.OfType<XamlIlAstTextNode>().FirstOrDefault()?.Text;
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;
property.Values = new List<IXamlIlAstValueNode>
{
new XamlIlAvaloniaPropertyNode(property.Values[0], property.Property.GetClrProperty().PropertyType,
clrProperty)
};
var valueProperty = on.Children
.OfType<XamlIlAstXamlPropertyValueNode>().FirstOrDefault(p => p.Property.GetClrProperty().Name == "Value");
if (valueProperty?.Values?.Count == 1 && valueProperty.Values[0] is XamlIlAstTextNode)
{
if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, valueProperty.Values[0],
clrProperty.PropertyType, out var converted))
throw new XamlIlParseException(
$"Unable to convert property value to {clrProperty.PropertyType.GetFqn()}",
valueProperty.Values[0]);
valueProperty.Values = new List<IXamlIlAstValueNode>
{
new XamlIlAstRuntimeCastNode(converted, converted,
new XamlIlAstClrTypeReference(converted, context.Configuration.WellKnownTypes.Object))
};
}
return node;
}
}
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());
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github

@ -1 +1 @@
Subproject commit 0c66a9a0a5226378ac0b2c756a85129b94cf1de9
Subproject commit 04f47415b816692684603efda909257b0004e6b8
Loading…
Cancel
Save