diff --git a/Fluid/Accessors/PropertyInfoAccessor.cs b/Fluid/Accessors/PropertyInfoAccessor.cs index cf99450d..7eab3665 100644 --- a/Fluid/Accessors/PropertyInfoAccessor.cs +++ b/Fluid/Accessors/PropertyInfoAccessor.cs @@ -1,17 +1,18 @@ using System.Reflection; using System.Reflection.Emit; +using Fluid.Values; namespace Fluid.Accessors { public sealed class PropertyInfoAccessor : IMemberAccessor { - private readonly IInvoker _invoker; + private readonly Invoker _invoker; public PropertyInfoAccessor(PropertyInfo propertyInfo) { Delegate d; - if (!propertyInfo.DeclaringType.IsValueType) + if (!propertyInfo.DeclaringType?.IsValueType == true) { var delegateType = typeof(Func<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType); d = propertyInfo.GetGetMethod().CreateDelegate(delegateType); @@ -21,7 +22,7 @@ public PropertyInfoAccessor(PropertyInfo propertyInfo) // We can't create an open delegate on a struct (dotnet limitation?), so instead create custom delegates // https://sharplab.io/#v2:EYLgtghglgdgNAFxAJwK7wCYgNQB8ACATAAwCwAUEQIwX7EAE+VAdACLIQDusA5gNwUKANwjJ6ABwCSMAGYB7egF56CAJ7iApnJkAKAApzYCAJTMA4hoR7kczcjU6ARAA1HxgeRFiMhJROny5pYWCACylgAWchg6pgDCyBoQCBqsGgA2GjzJGjpqmto6+ACsADwGRnD0RgB8xu4UQA== // Instead we generate IL to access the backing field directly - + d = GetGetter(propertyInfo.DeclaringType, propertyInfo.Name); } @@ -30,58 +31,76 @@ public PropertyInfoAccessor(PropertyInfo propertyInfo) _invoker = null; } - var invokerType = typeof(Invoker<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType); - _invoker = Activator.CreateInstance(invokerType, [d]) as IInvoker; - } + Type invokerType; + if (propertyInfo.PropertyType == typeof(bool)) + { + invokerType = typeof(BooleanInvoker<>).MakeGenericType(propertyInfo.DeclaringType); + } + else if (propertyInfo.PropertyType == typeof(int)) + { + invokerType = typeof(Int32Invoker<>).MakeGenericType(propertyInfo.DeclaringType); + } + else + { + invokerType = typeof(Invoker<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType); + } - public object Get(object obj, string name, TemplateContext ctx) - { - return _invoker?.Invoke(obj); + _invoker = (Invoker) Activator.CreateInstance(invokerType, [d]); } + public object Get(object obj, string name, TemplateContext ctx) => _invoker.Invoke(obj); + private static Delegate GetGetter(Type declaringType, string fieldName) { - string[] names = [fieldName.ToLowerInvariant(), $"<{fieldName}>k__BackingField", "_" + fieldName.ToLowerInvariant()]; - - var field = names - .Select(n => declaringType.GetField(n, BindingFlags.Instance | BindingFlags.NonPublic)) - .FirstOrDefault(x => x != null); + string[] names = [fieldName.ToLowerInvariant(), $"<{fieldName}>k__BackingField", $"_{fieldName.ToLowerInvariant()}"]; - if (field == null) + foreach (var n in names) { - return null; - } + var field = declaringType.GetField(n, BindingFlags.Instance | BindingFlags.NonPublic); + if (field == null) + { + continue; + } - var parameterTypes = new[] { typeof(object), declaringType }; + var parameterTypes = new[] { typeof(object), declaringType }; - var method = new DynamicMethod(fieldName + "Get", field.FieldType, parameterTypes, typeof(PropertyInfoAccessor).Module, true); + var method = new DynamicMethod(fieldName + "Get", field.FieldType, parameterTypes, typeof(PropertyInfoAccessor).Module, true); - var emitter = method.GetILGenerator(); - emitter.Emit(OpCodes.Ldarg_1); - emitter.Emit(OpCodes.Ldfld, field); - emitter.Emit(OpCodes.Ret); + var emitter = method.GetILGenerator(); + emitter.Emit(OpCodes.Ldarg_1); + emitter.Emit(OpCodes.Ldfld, field); + emitter.Emit(OpCodes.Ret); - return method.CreateDelegate(typeof(Func<,>).MakeGenericType(declaringType, field.FieldType)); + return method.CreateDelegate(typeof(Func<,>).MakeGenericType(declaringType, field.FieldType)); + } + + return null; } - private interface IInvoker + private abstract class Invoker { - object Invoke(object target); + public abstract object Invoke(object target); } - private sealed class Invoker : IInvoker + private sealed class Invoker(Delegate d) : Invoker { - private readonly Func _d; + private readonly Func _d = (Func) d; - public Invoker(Delegate d) - { - _d = (Func)d; - } + public override object Invoke(object target) => _d((T) target); + } - public object Invoke(object target) - { - return _d((T)target); - } + private sealed class BooleanInvoker(Delegate d) : Invoker + { + private readonly Func _d = (Func) d; + + public override object Invoke(object target) => _d((T) target) ? BooleanValue.True : BooleanValue.False; + } + + private sealed class Int32Invoker(Delegate d) : Invoker + { + private readonly Func _d = (Func) d; + + public override object Invoke(object target) => NumberValue.Create(_d((T) target)); } } }