Browse Source

Special treatment for bindings

pull/2322/head
Nikita Tsukanov 7 years ago
parent
commit
1338448bdc
  1. 5
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  2. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  3. 31
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  4. 24
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs
  5. 109
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs
  6. 3
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs
  7. 42
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  8. 27
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs
  9. 31
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
  10. 2
      src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
  11. 2
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  12. 39
      tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs

5
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -55,18 +55,21 @@
<Compile Include="Templates\TemplateLoader.cs" />
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="XamlIl\AvaloniaXamlIlRuntimeCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlAvaloniaPropertyResolver.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlConstructorServiceProviderTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlDesignPropertiesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlMetadataRemover.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransformInstanceAttachedProperties.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlWellKnownTypes.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\ResolveAvaloniaPropertiesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlBindingPropertyAssignmentsTransformer.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" />
<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" />

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@ -28,7 +28,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public string ResourceKey { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
public override object ProvideValue(IServiceProvider serviceProvider) => ProvideTypedValue(serviceProvider);
public IBinding ProvideTypedValue(IServiceProvider serviceProvider)
{
var provideTarget = serviceProvider.GetService<IProvideValueTarget>();

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

@ -16,6 +16,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
private AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration) : base(configuration, true)
{
void InsertAfter<T>(params IXamlIlAstTransformer[] t)
=> Transformers.InsertRange(Transformers.FindIndex(x => x is T) + 1, t);
void InsertBefore<T>(params IXamlIlAstTransformer[] t)
=> Transformers.InsertRange(Transformers.FindIndex(x => x is T), t);
// Before everything else
Transformers.Insert(0, new XNameTransformer());
@ -25,21 +32,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
// Targeted
Transformers.Insert(Transformers.FindIndex(x => x is XamlIlPropertyReferenceResolver),
new AvaloniaXamlIlTransformInstanceAttachedProperties());
InsertBefore<XamlIlPropertyReferenceResolver>(new AvaloniaXamlIlTransformInstanceAttachedProperties());
InsertAfter<XamlIlPropertyReferenceResolver>(new AvaloniaXamlIlAvaloniaPropertyResolver());
Transformers.Insert(Transformers.FindIndex(x => x is XamlIlXamlPropertyValueTransformer),
new KnownPseudoMarkupExtensionsTransformer());
Transformers.InsertRange(Transformers.FindIndex(x => x is XamlIlNewObjectTransformer),
new IXamlIlAstTransformer[]
{
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer()
}
InsertBefore<XamlIlXamlPropertyValueTransformer>(new AvaloniaXamlIlBindingPropertyAssignmentsTransformer());
InsertBefore<XamlIlNewObjectTransformer>(
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer()
);
// After everything else
Transformers.Add(new AddNameScopeRegistration());

24
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs

@ -0,0 +1,24 @@
using System.Linq;
using XamlIl.Ast;
using XamlIl.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
public class AvaloniaXamlIlAvaloniaPropertyResolver : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (node is XamlIlAstClrPropertyReference prop)
{
var n = prop.Property.Name + "Property";
var field =
(prop.Property.Getter ?? prop.Property.Setter).DeclaringType.Fields
.FirstOrDefault(f => f.Name == n);
if (field != null)
prop.Property = new XamlIlAvaloniaProperty(prop.Property, field);
}
return node;
}
}
}

109
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
public class AvaloniaXamlIlBindingPropertyAssignmentsTransformer : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (node is XamlIlAstXamlPropertyValueNode pv
&& pv.Property.GetClrProperty() is IXamlIlAvaloniaProperty ap
&& pv.Values.Count == 1)
{
var types = context.GetAvaloniaTypes();
var vn = pv.Values[0];
if(vn.Type.GetClrType().Name.Contains("TemplateBinding"))
Console.WriteLine();
// Special handling for markup extensions
if (vn.Type.IsMarkupExtension)
{
if (XamlIlTransformHelpers
.GetMarkupExtensionProvideValueAlternatives(context, vn.Type.GetClrType())
.Any(x => types.IBinding.IsAssignableFrom(x.ReturnType)))
{
if (XamlIlTransformHelpers.TryConvertMarkupExtension(context, pv.Values[0],
new AssignBindingProperty(types, ap), out var ext))
return ext;
}
}
else if (types.IBinding.IsAssignableFrom(vn.Type.GetClrType()))
{
pv.Property = new XamlIlAstClrPropertyReference(pv.Property, new AssignBindingProperty(types, ap));
}
}
return node;
}
class AssignBindingProperty : IXamlIlAvaloniaProperty
{
public AssignBindingProperty(
AvaloniaXamlIlWellKnownTypes types,
IXamlIlAvaloniaProperty property
)
{
PropertyType = types.IBinding;
AvaloniaProperty = property.AvaloniaProperty;
CustomAttributes = property.CustomAttributes;
Name = property.Name;
Setter = new SetterMethod(types, (property.Setter ?? property.Getter).DeclaringType,
AvaloniaProperty);
}
public bool Equals(IXamlIlProperty other) =>
other is AssignBindingProperty abp && abp.AvaloniaProperty.Equals(AvaloniaProperty);
public string Name { get; }
public IXamlIlType PropertyType { get; }
public IXamlIlMethod Setter { get; }
public IXamlIlMethod Getter { get; }
public IReadOnlyList<IXamlIlCustomAttribute> CustomAttributes { get; }
public IXamlIlField AvaloniaProperty { get; }
class SetterMethod : IXamlIlCustomEmitMethod
{
private readonly AvaloniaXamlIlWellKnownTypes _types;
private readonly IXamlIlField _avaloniaProperty;
public SetterMethod(AvaloniaXamlIlWellKnownTypes types,
IXamlIlType declaringType,
IXamlIlField avaloniaProperty)
{
_types = types;
_avaloniaProperty = avaloniaProperty;
Parameters = new[] {types.AvaloniaObject, types.IBinding};
ReturnType = types.XamlIlTypes.Void;
DeclaringType = declaringType;
Name = "Bind_" + avaloniaProperty.Name;
}
public bool Equals(IXamlIlMethod other) =>
other is SetterMethod sm && sm._avaloniaProperty.Equals(_avaloniaProperty);
public string Name { get; }
public bool IsPublic => true;
public bool IsStatic => true;
public IXamlIlType ReturnType { get; }
public IReadOnlyList<IXamlIlType> Parameters { get; }
public IXamlIlType DeclaringType { get; }
public void EmitCall(IXamlIlEmitter emitter)
{
using (var bloc = emitter.LocalsPool.GetLocal(_types.IBinding))
emitter
.Stloc(bloc.Local)
.Ldsfld(_avaloniaProperty)
.Ldloc(bloc.Local)
// TODO: provide anchor?
.Ldnull();
emitter.EmitCall(_types.AvaloniaObjectBindMethod, true);
}
}
}
}
}

3
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs

@ -71,7 +71,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return node;
}
class AvaloniaAttachedInstanceProperty : IXamlIlProperty
class AvaloniaAttachedInstanceProperty : IXamlIlAvaloniaProperty
{
private readonly XamlIlTransformerConfiguration _config;
private readonly IXamlIlType _declaringType;
@ -103,6 +103,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
Getter = new GetterMethod(this);
}
public IXamlIlField AvaloniaProperty => _field;
public bool Equals(IXamlIlProperty other) =>
other is AvaloniaAttachedInstanceProperty ap && ap._field.Equals(_field);

42
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -0,0 +1,42 @@
using XamlIl.Transform;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AvaloniaXamlIlWellKnownTypes
{
public IXamlIlType AvaloniaObject { get; }
public IXamlIlType IAvaloniaObject { get; }
public IXamlIlType AvaloniaObjectExtensions { get; }
public IXamlIlType AvaloniaProperty { get; }
public IXamlIlType IBinding { get; }
public IXamlIlMethod AvaloniaObjectBindMethod { get; }
public IXamlIlType IDisposable { get; }
public XamlIlTypeWellKnownTypes XamlIlTypes { get; }
public AvaloniaXamlIlWellKnownTypes(XamlIlAstTransformationContext ctx)
{
XamlIlTypes = ctx.Configuration.WellKnownTypes;
AvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObject");
IAvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.IAvaloniaObject");
AvaloniaObjectExtensions = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions");
AvaloniaProperty = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaProperty");
IBinding = ctx.Configuration.TypeSystem.GetType("Avalonia.Data.IBinding");
IDisposable = ctx.Configuration.TypeSystem.GetType("System.IDisposable");
AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject,
AvaloniaProperty,
IBinding, ctx.Configuration.WellKnownTypes.Object);
}
}
static class AvaloniaXamlIlWellKnownTypesExtensions
{
public static AvaloniaXamlIlWellKnownTypes GetAvaloniaTypes(this XamlIlAstTransformationContext ctx)
{
if (ctx.TryGetItem<AvaloniaXamlIlWellKnownTypes>(out var rv))
return rv;
ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx));
return rv;
}
}
}

27
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs

@ -1,27 +0,0 @@
using System.Collections.Generic;
using XamlIl.Ast;
using XamlIl.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class KnownPseudoMarkupExtensionsTransformer : IXamlIlAstTransformer
{
private static readonly List<string> s_knownPseudoExtensions = new List<string>
{
"Avalonia.Data.TemplateBinding",
"Avalonia.Data.MultiBinding",
"Avalonia.Data.Binding",
};
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (node is XamlIlAstXamlPropertyValueNode pn
&& pn.Values.Count == 1
&& s_knownPseudoExtensions.Contains(pn.Values[0].Type.GetClrType().FullName))
return new XamlIlMarkupExtensionNode(node, pn.Property.GetClrProperty(),
null, pn.Values[0], null);
else
return node;
}
}
}

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

@ -15,6 +15,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, IXamlIlProperty property)
{
if (property is IXamlIlAvaloniaProperty ap)
{
emitter.Ldsfld(ap.AvaloniaProperty);
return true;
}
var type = (property.Getter ?? property.Setter).DeclaringType;
var name = property.Name + "Property";
var found = type.Fields.FirstOrDefault(f => f.IsStatic && f.Name == name);
@ -75,4 +80,30 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return XamlIlNodeEmitResult.Type(0, Type.GetClrType());
}
}
interface IXamlIlAvaloniaProperty : IXamlIlProperty
{
IXamlIlField AvaloniaProperty { get; }
}
class XamlIlAvaloniaProperty : IXamlIlAvaloniaProperty
{
private readonly IXamlIlProperty _original;
public IXamlIlField AvaloniaProperty { get; }
public XamlIlAvaloniaProperty(IXamlIlProperty original, IXamlIlField field)
{
_original = original;
AvaloniaProperty = field;
}
public bool Equals(IXamlIlProperty other) =>
other is XamlIlAvaloniaProperty p && p.AvaloniaProperty.Equals(AvaloniaProperty);
public string Name => _original.Name;
public IXamlIlType PropertyType => _original.PropertyType;
public IXamlIlMethod Setter => _original.Setter;
public IXamlIlMethod Getter => _original.Getter;
public IReadOnlyList<IXamlIlCustomAttribute> CustomAttributes => _original.CustomAttributes;
}
}

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

@ -1 +1 @@
Subproject commit e1ae06781318a65810adb83b87ba56ffeafaf50b
Subproject commit cf70f5da92e04e1d2d36da5451813bea2b8f54ba

2
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@ -176,5 +176,7 @@ namespace Avalonia.Data
PublishValue();
}
}
public IBinding ProvideValue() => this;
}
}

39
tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs

@ -0,0 +1,39 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Media;
using JetBrains.Annotations;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests
{
public class XamlIlBugTests
{
[Fact]
public void Binding_Button_IsPressed_ShouldWork()
{
var parsed = (Button)AvaloniaXamlLoader.Parse(@"
<Button xmlns='https://github.com/avaloniaui' IsPressed='{Binding IsPressed, Mode=TwoWay}' />");
var ctx = new XamlIlBugTestsDataContext();
parsed.DataContext = ctx;
parsed.SetValue(Button.IsPressedProperty, true);
Assert.True(ctx.IsPressed);
}
}
public class XamlIlBugTestsDataContext : INotifyPropertyChanged
{
public bool IsPressed { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Loading…
Cancel
Save