7 changed files with 212 additions and 2 deletions
@ -0,0 +1,201 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using XamlIl; |
|||
using XamlIl.Ast; |
|||
using XamlIl.Transform; |
|||
using XamlIl.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
public class AvaloniaXamlIlTransformInstanceAttachedProperties : IXamlIlAstTransformer |
|||
{ |
|||
|
|||
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) |
|||
{ |
|||
if (node is XamlIlAstNamePropertyReference prop |
|||
&& prop.TargetType is XamlIlAstClrTypeReference targetRef |
|||
&& prop.DeclaringType is XamlIlAstClrTypeReference declaringRef) |
|||
{ |
|||
// Target and declared type aren't assignable but both inherit from AvaloniaObject
|
|||
var avaloniaObject = context.Configuration.TypeSystem.FindType("Avalonia.AvaloniaObject"); |
|||
if (avaloniaObject.IsAssignableFrom(targetRef.Type) |
|||
&& avaloniaObject.IsAssignableFrom(declaringRef.Type) |
|||
&& !targetRef.Type.IsAssignableFrom(declaringRef.Type)) |
|||
{ |
|||
// Instance property
|
|||
var clrProp = declaringRef.Type.GetAllProperties().FirstOrDefault(p => p.Name == prop.Name); |
|||
if (clrProp != null |
|||
&& (clrProp.Getter?.IsStatic == false || clrProp.Setter?.IsStatic == false)) |
|||
{ |
|||
var declaringType = (clrProp.Getter ?? clrProp.Setter)?.DeclaringType; |
|||
var avaloniaPropertyFieldName = prop.Name + "Property"; |
|||
var avaloniaPropertyField = declaringType.Fields.FirstOrDefault(f => f.IsStatic && f.Name == avaloniaPropertyFieldName); |
|||
if (avaloniaPropertyField != null) |
|||
{ |
|||
var avaloniaPropertyType = avaloniaPropertyField.FieldType; |
|||
while (avaloniaPropertyType != null |
|||
&& !(avaloniaPropertyType.Namespace == "Avalonia" |
|||
&& (avaloniaPropertyType.Name == "AvaloniaProperty" |
|||
|| avaloniaPropertyType.Name == "AvaloniaProperty`1" |
|||
))) |
|||
{ |
|||
// Attached properties are handled by vanilla XamlIl
|
|||
if (avaloniaPropertyType.Name.StartsWith("AttachedProperty")) |
|||
return node; |
|||
|
|||
avaloniaPropertyType = avaloniaPropertyType.BaseType; |
|||
} |
|||
|
|||
if (avaloniaPropertyType == null) |
|||
return node; |
|||
|
|||
if (avaloniaPropertyType.GenericArguments?.Count > 1) |
|||
return node; |
|||
|
|||
var propertyType = avaloniaPropertyType.GenericArguments?.Count == 1 ? |
|||
avaloniaPropertyType.GenericArguments[0] : |
|||
context.Configuration.WellKnownTypes.Object; |
|||
|
|||
return new XamlIlAstClrPropertyReference(prop, |
|||
new AvaloniaAttachedInstanceProperty(prop.Name, context.Configuration, |
|||
declaringType, propertyType, avaloniaPropertyType, avaloniaObject, |
|||
avaloniaPropertyField)); |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
|
|||
return node; |
|||
} |
|||
|
|||
class AvaloniaAttachedInstanceProperty : IXamlIlProperty |
|||
{ |
|||
private readonly XamlIlTransformerConfiguration _config; |
|||
private readonly IXamlIlType _declaringType; |
|||
private readonly IXamlIlType _avaloniaPropertyType; |
|||
private readonly IXamlIlType _avaloniaObject; |
|||
private readonly IXamlIlField _field; |
|||
|
|||
public AvaloniaAttachedInstanceProperty(string name, |
|||
XamlIlTransformerConfiguration config, |
|||
IXamlIlType declaringType, |
|||
IXamlIlType type, |
|||
IXamlIlType avaloniaPropertyType, |
|||
IXamlIlType avaloniaObject, |
|||
IXamlIlField field) |
|||
{ |
|||
_config = config; |
|||
_declaringType = declaringType; |
|||
_avaloniaPropertyType = avaloniaPropertyType; |
|||
|
|||
// XamlIl doesn't support generic methods yet
|
|||
if (_avaloniaPropertyType.GenericArguments?.Count > 0) |
|||
_avaloniaPropertyType = _avaloniaPropertyType.BaseType; |
|||
|
|||
_avaloniaObject = avaloniaObject; |
|||
_field = field; |
|||
Name = name; |
|||
PropertyType = type; |
|||
Setter = new SetterMethod(this); |
|||
Getter = new GetterMethod(this); |
|||
} |
|||
|
|||
public bool Equals(IXamlIlProperty other) => |
|||
other is AvaloniaAttachedInstanceProperty ap && ap._field.Equals(_field); |
|||
|
|||
public string Name { get; } |
|||
public IXamlIlType PropertyType { get; } |
|||
public IXamlIlMethod Setter { get; } |
|||
public IXamlIlMethod Getter { get; } |
|||
public IReadOnlyList<IXamlIlCustomAttribute> CustomAttributes { get; } = new List<IXamlIlCustomAttribute>(); |
|||
|
|||
class Method |
|||
{ |
|||
public AvaloniaAttachedInstanceProperty Parent { get; } |
|||
public bool IsPublic => true; |
|||
public bool IsStatic => true; |
|||
public string Name { get; protected set; } |
|||
public IXamlIlType DeclaringType { get; } |
|||
public Method(AvaloniaAttachedInstanceProperty parent) |
|||
{ |
|||
Parent = parent; |
|||
DeclaringType = parent._declaringType; |
|||
} |
|||
|
|||
public bool Equals(IXamlIlMethod other) => |
|||
other is Method m && m.Name == Name && m.DeclaringType.Equals(DeclaringType); |
|||
} |
|||
|
|||
class SetterMethod : Method, IXamlIlCustomEmitMethod |
|||
{ |
|||
public SetterMethod(AvaloniaAttachedInstanceProperty parent) : base(parent) |
|||
{ |
|||
Name = "AvaloniaObject:SetValue_" + Parent.Name; |
|||
Parameters = new[] {Parent._avaloniaObject, Parent.PropertyType}; |
|||
} |
|||
|
|||
public IXamlIlType ReturnType => Parent._config.WellKnownTypes.Void; |
|||
public IReadOnlyList<IXamlIlType> Parameters { get; } |
|||
|
|||
public void EmitCall(IXamlIlEmitter emitter) |
|||
{ |
|||
var so = Parent._config.WellKnownTypes.Object; |
|||
var method = Parent._avaloniaObject |
|||
.FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "SetValue" |
|||
&& |
|||
m.Parameters.Count == 3 |
|||
&& m.Parameters[0].Equals(Parent._avaloniaPropertyType) |
|||
&& m.Parameters[1].Equals(so) |
|||
&& m.Parameters[2].IsEnum |
|||
); |
|||
if (method == null) |
|||
throw new XamlIlTypeSystemException( |
|||
"Unable to find SetValue(AvaloniaProperty, object, BindingPriority) on AvaloniaObject"); |
|||
var loc = emitter.DefineLocal(Parent.PropertyType); |
|||
emitter |
|||
.Stloc(loc) |
|||
.Ldsfld(Parent._field) |
|||
.Ldloc(loc); |
|||
if(Parent.PropertyType.IsValueType) |
|||
emitter.Box(Parent.PropertyType); |
|||
emitter |
|||
.Ldc_I4(0) |
|||
.EmitCall(method); |
|||
|
|||
} |
|||
} |
|||
|
|||
class GetterMethod : Method, IXamlIlCustomEmitMethod |
|||
{ |
|||
public GetterMethod(AvaloniaAttachedInstanceProperty parent) : base(parent) |
|||
{ |
|||
Name = "AvaloniaObject:GetValue_" + Parent.Name; |
|||
Parameters = new[] {parent._avaloniaObject}; |
|||
} |
|||
|
|||
public IXamlIlType ReturnType => Parent.PropertyType; |
|||
public IReadOnlyList<IXamlIlType> Parameters { get; } |
|||
public void EmitCall(IXamlIlEmitter emitter) |
|||
{ |
|||
var method = Parent._avaloniaObject |
|||
.FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "GetValue" |
|||
&& |
|||
m.Parameters.Count == 1 |
|||
&& m.Parameters[0].Equals(Parent._avaloniaPropertyType)); |
|||
if (method == null) |
|||
throw new XamlIlTypeSystemException( |
|||
"Unable to find T GetValue<T>(AvaloniaProperty<T>) on AvaloniaObject"); |
|||
emitter |
|||
.Ldsfld(Parent._field) |
|||
.EmitCall(method); |
|||
if (Parent.PropertyType.IsValueType) |
|||
emitter.Unbox_Any(Parent.PropertyType); |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1 +1 @@ |
|||
Subproject commit 1b3fda73e4cece31a4fec22ce146640ed5f2fd4c |
|||
Subproject commit 5070101d67673da49f955055b7890e7a53a77397 |
|||
Loading…
Reference in new issue