12 changed files with 275 additions and 44 deletions
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -1 +1 @@ |
|||
Subproject commit e1ae06781318a65810adb83b87ba56ffeafaf50b |
|||
Subproject commit cf70f5da92e04e1d2d36da5451813bea2b8f54ba |
|||
@ -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…
Reference in new issue