Browse Source

Merge pull request #5088 from MarchingCube/compile-time-grid-definitions

Compile time grid definitions parsing
pull/5126/head
Jumar Macato 5 years ago
committed by GitHub
parent
commit
15968cb2c0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      src/Avalonia.Base/Collections/AvaloniaList.cs
  2. 60
      src/Avalonia.Controls/DefinitionList.cs
  3. 54
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs
  4. 171
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  5. 243
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  6. 10
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

18
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -63,6 +63,15 @@ namespace Avalonia.Collections
_inner = new List<T>();
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaList{T}"/>.
/// </summary>
/// <param name="capacity">Initial list capacity.</param>
public AvaloniaList(int capacity)
{
_inner = new List<T>(capacity);
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaList{T}"/> class.
/// </summary>
@ -175,6 +184,15 @@ namespace Avalonia.Collections
set { this[index] = (T)value; }
}
/// <summary>
/// Gets or sets the total number of elements the internal data structure can hold without resizing.
/// </summary>
public int Capacity
{
get => _inner.Capacity;
set => _inner.Capacity = value;
}
/// <summary>
/// Adds an item to the collection.
/// </summary>

60
src/Avalonia.Controls/DefinitionList.cs

@ -1,8 +1,9 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
#nullable enable
namespace Avalonia.Controls
{
public abstract class DefinitionList<T> : AvaloniaList<T> where T : DefinitionBase
@ -14,44 +15,65 @@ namespace Avalonia.Controls
}
internal bool IsDirty = true;
private Grid _parent;
private Grid? _parent;
internal Grid Parent
internal Grid? Parent
{
get => _parent;
set => SetParent(value);
}
private void SetParent(Grid value)
private void SetParent(Grid? value)
{
_parent = value;
foreach (var pair in this.Select((definitions, index) => (definitions, index)))
var idx = 0;
foreach (T definition in this)
{
pair.definitions.Parent = value;
pair.definitions.Index = pair.index;
definition.Parent = value;
definition.Index = idx++;
}
}
internal void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (var nI in this.Select((d, i) => (d, i)))
nI.d._parentIndex = nI.i;
var idx = 0;
foreach (var nD in e.NewItems?.Cast<DefinitionBase>()
?? Enumerable.Empty<DefinitionBase>())
foreach (T definition in this)
{
nD.Parent = this.Parent;
nD.OnEnterParentTree();
definition.Index = idx++;
}
UpdateDefinitionParent(e.NewItems, false);
UpdateDefinitionParent(e.OldItems, true);
IsDirty = true;
}
foreach (var oD in e.OldItems?.Cast<DefinitionBase>()
?? Enumerable.Empty<DefinitionBase>())
private void UpdateDefinitionParent(IList? items, bool wasRemoved)
{
if (items is null)
{
oD.OnExitParentTree();
return;
}
var count = items.Count;
IsDirty = true;
for (var i = 0; i < count; i++)
{
var definition = (DefinitionBase) items[i];
if (wasRemoved)
{
definition.OnExitParentTree();
}
else
{
definition.Parent = Parent;
definition.OnEnterParentTree();
}
}
}
}
}
}

54
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs

@ -0,0 +1,54 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlAvaloniaListConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly IXamlType _elementType;
private readonly IReadOnlyList<IXamlAstValueNode> _values;
private readonly IXamlConstructor _constructor;
private readonly IXamlMethod _listAddMethod;
private readonly IXamlMethod _listSetCapacityMethod;
public AvaloniaXamlIlAvaloniaListConstantAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, IXamlType listType, IXamlType elementType, IReadOnlyList<IXamlAstValueNode> values) : base(lineInfo)
{
_constructor = listType.GetConstructor();
_listAddMethod = listType.GetMethod(new FindMethodMethodSignature("Add", types.XamlIlTypes.Void, elementType));
_listSetCapacityMethod = listType.GetMethod(new FindMethodMethodSignature("set_Capacity", types.XamlIlTypes.Void, types.Int));
_elementType = elementType;
_values = values;
Type = new XamlAstClrTypeReference(lineInfo, listType, false);
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen.Newobj(_constructor);
codeGen
.Dup()
.Ldc_I4(_values.Count)
.EmitCall(_listSetCapacityMethod);
foreach (var value in _values)
{
codeGen.Dup();
context.Emit(value, codeGen, _elementType);
codeGen.EmitCall(_listAddMethod);
}
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
}

171
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@ -177,182 +177,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
var text = textNode.Text;
var types = context.GetAvaloniaTypes();
if (type.FullName == "System.TimeSpan")
if (AvaloniaXamlIlLanguageParseIntrinsics.TryConvert(context, node, text, type, types, out result))
{
var tsText = text.Trim();
if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan))
{
// // shorthand seconds format (ie. "0.25")
if (!tsText.Contains(":") && double.TryParse(tsText,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out var seconds))
timeSpan = TimeSpan.FromSeconds(seconds);
else
throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.FindMethod("FromTicks", type, false, types.Long),
new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) });
return true;
}
if (type.Equals(types.FontFamily))
{
result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
return true;
}
if (type.Equals(types.Thickness))
{
try
{
var thickness = Thickness.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor,
new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node);
}
}
if (type.Equals(types.Point))
{
try
{
var point = Point.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor,
new[] { point.X, point.Y });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node);
}
}
if (type.Equals(types.Vector))
{
try
{
var vector = Vector.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor,
new[] { vector.X, vector.Y });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node);
}
}
if (type.Equals(types.Size))
{
try
{
var size = Size.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor,
new[] { size.Width, size.Height });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node);
}
}
if (type.Equals(types.Matrix))
{
try
{
var matrix = Matrix.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor,
new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node);
}
}
if (type.Equals(types.CornerRadius))
{
try
{
var cornerRadius = CornerRadius.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor,
new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node);
}
}
if (type.Equals(types.Color))
{
if (!Color.TryParse(text, out Color color))
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.GetMethod(
new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }),
new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) });
return true;
}
if (type.Equals(types.GridLength))
{
try
{
var gridLength = GridLength.Parse(text);
result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
}
}
if (type.Equals(types.Cursor))
{
if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode))
{
var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false);
result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List<IXamlAstValueNode> { enumConstantNode });
return true;
}
}
if (type.FullName == "Avalonia.AvaloniaProperty")
{
var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();

243
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs

@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using Avalonia.Media;
using XamlX.Ast;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
class AvaloniaXamlIlLanguageParseIntrinsics
{
public static bool TryConvert(AstTransformationContext context, IXamlAstValueNode node, string text, IXamlType type, AvaloniaXamlIlWellKnownTypes types, out IXamlAstValueNode result)
{
if (type.FullName == "System.TimeSpan")
{
var tsText = text.Trim();
if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan))
{
// // shorthand seconds format (ie. "0.25")
if (!tsText.Contains(":") && double.TryParse(tsText,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out var seconds))
timeSpan = TimeSpan.FromSeconds(seconds);
else
throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.FindMethod("FromTicks", type, false, types.Long),
new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) });
return true;
}
if (type.Equals(types.FontFamily))
{
result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
return true;
}
if (type.Equals(types.Thickness))
{
try
{
var thickness = Thickness.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor,
new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node);
}
}
if (type.Equals(types.Point))
{
try
{
var point = Point.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor,
new[] { point.X, point.Y });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node);
}
}
if (type.Equals(types.Vector))
{
try
{
var vector = Vector.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor,
new[] { vector.X, vector.Y });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node);
}
}
if (type.Equals(types.Size))
{
try
{
var size = Size.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor,
new[] { size.Width, size.Height });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node);
}
}
if (type.Equals(types.Matrix))
{
try
{
var matrix = Matrix.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor,
new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node);
}
}
if (type.Equals(types.CornerRadius))
{
try
{
var cornerRadius = CornerRadius.Parse(text);
result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor,
new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft });
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node);
}
}
if (type.Equals(types.Color))
{
if (!Color.TryParse(text, out Color color))
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.GetMethod(
new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }),
new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) });
return true;
}
if (type.Equals(types.GridLength))
{
try
{
var gridLength = GridLength.Parse(text);
result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
}
}
if (type.Equals(types.Cursor))
{
if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode))
{
var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false);
result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List<IXamlAstValueNode> { enumConstantNode });
return true;
}
}
if (type.Equals(types.ColumnDefinitions))
{
return ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result);
}
if (type.Equals(types.RowDefinitions))
{
return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result);
}
result = null;
return false;
}
private static bool ConvertDefinitionList(
IXamlAstValueNode node,
string text,
AvaloniaXamlIlWellKnownTypes types,
IXamlType listType,
IXamlType elementType,
string errorDisplayName,
out IXamlAstValueNode result)
{
try
{
var lengths = GridLength.ParseLengths(text);
var definitionTypeRef = new XamlAstClrTypeReference(node, elementType, false);
var definitionConstructorGridLength = elementType.GetConstructor(new List<IXamlType> {types.GridLength});
IXamlAstValueNode CreateDefinitionNode(GridLength length)
{
var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, length);
return new XamlAstNewClrObjectNode(node, definitionTypeRef,
definitionConstructorGridLength, new List<IXamlAstValueNode> {lengthNode});
}
var definitionNodes =
new List<IXamlAstValueNode>(lengths.Select(CreateDefinitionNode));
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, listType, elementType, definitionNodes);
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a {errorDisplayName}", node);
}
}
}
}

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

@ -50,6 +50,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType RelativeSource { get; }
public IXamlType UInt { get; }
public IXamlType Int { get; }
public IXamlType Long { get; }
public IXamlType Uri { get; }
public IXamlType FontFamily { get; }
@ -72,6 +73,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType StandardCursorType { get; }
public IXamlType Cursor { get; }
public IXamlConstructor CursorTypeConstructor { get; }
public IXamlType RowDefinition { get; }
public IXamlType RowDefinitions { get; }
public IXamlType ColumnDefinition { get; }
public IXamlType ColumnDefinitions { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -130,6 +135,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension");
RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource");
UInt = cfg.TypeSystem.GetType("System.UInt32");
Int = cfg.TypeSystem.GetType("System.Int32");
Long = cfg.TypeSystem.GetType("System.Int64");
Uri = cfg.TypeSystem.GetType("System.Uri");
FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily");
@ -156,6 +162,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
StandardCursorType = cfg.TypeSystem.GetType("Avalonia.Input.StandardCursorType");
Cursor = cfg.TypeSystem.GetType("Avalonia.Input.Cursor");
CursorTypeConstructor = Cursor.GetConstructor(new List<IXamlType> { StandardCursorType });
ColumnDefinition = cfg.TypeSystem.GetType("Avalonia.Controls.ColumnDefinition");
ColumnDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.ColumnDefinitions");
RowDefinition = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinition");
RowDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinitions");
}
}

Loading…
Cancel
Save