src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformRoutedEvent.cs (104 lines of code) (raw):
using System.Collections.Generic;
using System.Linq;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
internal class AvaloniaXamlIlTransformRoutedEvent : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is XamlAstNamePropertyReference prop
&& prop.TargetType is XamlAstClrTypeReference targetRef
&& prop.DeclaringType is XamlAstClrTypeReference declaringRef)
{
var xkt = context.GetAvaloniaTypes();
var interactiveType = xkt.Interactivity.Interactive;
var routedEventType = xkt.Interactivity.RoutedEvent;
var AddHandlerT = xkt.Interactivity.AddHandlerT;
if (interactiveType.IsAssignableFrom(targetRef.Type))
{
var eventName = $"{prop.Name}Event";
if (declaringRef.Type.GetAllFields().FirstOrDefault(f => f.IsStatic && f.Name == eventName) is { } eventField)
{
if (routedEventType.IsAssignableFrom(eventField.FieldType))
{
var instance = new XamlAstClrProperty(prop
, prop.Name
, targetRef.Type
, null
);
instance.Setters.Add(new XamlDirectCallAddHandler(eventField,
targetRef.Type,
xkt.Interactivity.AddHandler,
xkt.Interactivity.RoutedEventHandler
)
);
if (eventField.FieldType.GenericArguments?.Count == 1)
{
var agrument = eventField.FieldType.GenericArguments[0];
if (!agrument.Equals(xkt.Interactivity.RoutedEventArgs))
{
instance.Setters.Add(new XamlDirectCallAddHandler(eventField,
targetRef.Type,
xkt.Interactivity.AddHandlerT.MakeGenericMethod([agrument]),
xkt.EventHandlerT.MakeGenericType(agrument)
)
);
}
}
return instance;
}
else
{
context.ReportDiagnostic(new XamlX.XamlDiagnostic(
AvaloniaXamlDiagnosticCodes.TransformError,
XamlX.XamlDiagnosticSeverity.Error,
$"Event definition {prop.Name} found, but its type {eventField.FieldType.GetFqn()} is not compatible with RoutedEvent.",
node));
}
}
}
}
return node;
}
private sealed class XamlDirectCallAddHandler : IXamlILOptimizedEmitablePropertySetter
{
private readonly IXamlField _eventField;
private readonly IXamlType _declaringType;
private readonly IXamlMethod _addMethod;
public XamlDirectCallAddHandler(IXamlField eventField,
IXamlType declaringType,
IXamlMethod addMethod,
IXamlType routedEventHandler
)
{
Parameters = [routedEventHandler];
_eventField = eventField;
_declaringType = declaringType;
_addMethod = addMethod;
}
public IXamlType TargetType => _declaringType;
public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters();
public IReadOnlyList<IXamlType> Parameters { get; }
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes => [];
public void Emit(IXamlILEmitter emitter)
=> emitter.EmitCall(_addMethod, true);
public void EmitWithArguments(XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> arguments)
{
using (var loc = emitter.LocalsPool.GetLocal(_declaringType))
emitter
.Ldloc(loc.Local);
emitter.Ldfld(_eventField);
for (var i = 0; i < arguments.Count; ++i)
context.Emit(arguments[i], emitter, Parameters[i]);
emitter.Ldc_I4(5);
emitter.Ldc_I4(0);
emitter.EmitCall(_addMethod, true);
}
}
}