mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-02 13:14:29 -04:00
Adding inner rewrite for expression rewriter
This commit is contained in:
parent
8d10b44b1d
commit
c42805e415
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@ -18,6 +20,14 @@ namespace Kyoo
|
|||||||
|
|
||||||
public class ExpressionRewrite : ExpressionVisitor
|
public class ExpressionRewrite : ExpressionVisitor
|
||||||
{
|
{
|
||||||
|
private string _inner;
|
||||||
|
private readonly List<(string inner, ParameterExpression param, ParameterExpression newParam)> _innerRewrites;
|
||||||
|
|
||||||
|
private ExpressionRewrite()
|
||||||
|
{
|
||||||
|
_innerRewrites = new List<(string, ParameterExpression, ParameterExpression)>();
|
||||||
|
}
|
||||||
|
|
||||||
public static Expression Rewrite(Expression expression)
|
public static Expression Rewrite(Expression expression)
|
||||||
{
|
{
|
||||||
return new ExpressionRewrite().Visit(expression);
|
return new ExpressionRewrite().Visit(expression);
|
||||||
@ -30,7 +40,18 @@ namespace Kyoo
|
|||||||
|
|
||||||
protected override Expression VisitMember(MemberExpression node)
|
protected override Expression VisitMember(MemberExpression node)
|
||||||
{
|
{
|
||||||
ExpressionRewriteAttribute attr = node.Member.GetCustomAttribute<ExpressionRewriteAttribute>();
|
(string inner, _, ParameterExpression p) = _innerRewrites.FirstOrDefault(x => x.param == node.Expression);
|
||||||
|
if (inner != null)
|
||||||
|
{
|
||||||
|
Expression param = p;
|
||||||
|
foreach (string accessor in inner.Split('.'))
|
||||||
|
param = Expression.Property(param, accessor);
|
||||||
|
node = Expression.Property(param, node.Member.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't use node.Member directly because we want to support attribute override
|
||||||
|
MemberInfo member = node.Expression.Type.GetProperty(node.Member.Name) ?? node.Member;
|
||||||
|
ExpressionRewriteAttribute attr = member!.GetCustomAttribute<ExpressionRewriteAttribute>();
|
||||||
if (attr == null)
|
if (attr == null)
|
||||||
return base.VisitMember(node);
|
return base.VisitMember(node);
|
||||||
|
|
||||||
@ -38,9 +59,62 @@ namespace Kyoo
|
|||||||
foreach (string child in attr.Link.Split('.'))
|
foreach (string child in attr.Link.Split('.'))
|
||||||
property = Expression.Property(property, child);
|
property = Expression.Property(property, child);
|
||||||
|
|
||||||
if (property is MemberExpression member)
|
if (property is MemberExpression expr)
|
||||||
Visit(member.Expression);
|
Visit(expr.Expression);
|
||||||
|
_inner = attr.Inner;
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Expression VisitLambda<T>(Expression<T> node)
|
||||||
|
{
|
||||||
|
(_, ParameterExpression oldParam, ParameterExpression param) = _innerRewrites
|
||||||
|
.FirstOrDefault(x => node.Parameters.Any(y => y == x.param));
|
||||||
|
if (param == null)
|
||||||
|
return base.VisitLambda(node);
|
||||||
|
|
||||||
|
ParameterExpression[] newParams = node.Parameters.Where(x => x != oldParam).Append(param).ToArray();
|
||||||
|
return Expression.Lambda(Visit(node.Body)!, newParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Expression VisitMethodCall(MethodCallExpression node)
|
||||||
|
{
|
||||||
|
int count = node.Arguments.Count;
|
||||||
|
if (node.Object != null)
|
||||||
|
count++;
|
||||||
|
if (count != 2)
|
||||||
|
return base.VisitMethodCall(node);
|
||||||
|
|
||||||
|
Expression instance = node.Object ?? node.Arguments.First();
|
||||||
|
Expression argument = node.Object != null
|
||||||
|
? node.Arguments.First()
|
||||||
|
: node.Arguments[1];
|
||||||
|
|
||||||
|
Type oldType = instance.Type;
|
||||||
|
instance = Visit(instance);
|
||||||
|
if (instance!.Type == oldType)
|
||||||
|
return base.VisitMethodCall(node);
|
||||||
|
|
||||||
|
if (_inner != null && argument is LambdaExpression lambda)
|
||||||
|
{
|
||||||
|
// TODO this type handler will usually work with IEnumerable & others but won't work with everything.
|
||||||
|
Type type = oldType.GetGenericArguments().First();
|
||||||
|
ParameterExpression oldParam = lambda.Parameters.FirstOrDefault(x => x.Type == type);
|
||||||
|
if (oldParam != null)
|
||||||
|
{
|
||||||
|
Type newType = instance.Type.GetGenericArguments().First();
|
||||||
|
ParameterExpression newParam = Expression.Parameter(newType, oldParam.Name);
|
||||||
|
_innerRewrites.Add((_inner, oldParam, newParam));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argument = Visit(argument);
|
||||||
|
|
||||||
|
// TODO this method handler may not work for some methods (ex: method taking a Fun<> method won't have good generic arguments)
|
||||||
|
MethodInfo method = node.Method.IsGenericMethod
|
||||||
|
? node.Method.GetGenericMethodDefinition().MakeGenericMethod(instance.Type.GetGenericArguments())
|
||||||
|
: node.Method;
|
||||||
|
return node.Object != null
|
||||||
|
? Expression.Call(instance, method!, argument)
|
||||||
|
: Expression.Call(null, method!, instance, argument!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -309,12 +309,14 @@ namespace Kyoo
|
|||||||
public static Expression<T> Convert<T>([CanBeNull] this Expression expr)
|
public static Expression<T> Convert<T>([CanBeNull] this Expression expr)
|
||||||
where T : Delegate
|
where T : Delegate
|
||||||
{
|
{
|
||||||
return expr switch
|
Expression<T> e = expr switch
|
||||||
{
|
{
|
||||||
null => null,
|
null => null,
|
||||||
LambdaExpression lambda => new ExpressionConverter<T>(lambda).VisitAndConvert(),
|
LambdaExpression lambda => new ExpressionConverter<T>(lambda).VisitAndConvert(),
|
||||||
_ => throw new ArgumentException("Can't convert a non lambda.")
|
_ => throw new ArgumentException("Can't convert a non lambda.")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return ExpressionRewrite.Rewrite<T>(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ExpressionConverter<TTo> : ExpressionVisitor
|
private class ExpressionConverter<TTo> : ExpressionVisitor
|
||||||
|
@ -24,9 +24,14 @@ namespace Kyoo.CommonApi
|
|||||||
Expression<Func<T, bool>> defaultWhere = null)
|
Expression<Func<T, bool>> defaultWhere = null)
|
||||||
{
|
{
|
||||||
if (where == null || where.Count == 0)
|
if (where == null || where.Count == 0)
|
||||||
return defaultWhere;
|
{
|
||||||
|
if (defaultWhere == null)
|
||||||
|
return null;
|
||||||
|
Expression body = ExpressionRewrite.Rewrite(defaultWhere.Body);
|
||||||
|
return Expression.Lambda<Func<T, bool>>(body, defaultWhere.Parameters.First());
|
||||||
|
}
|
||||||
|
|
||||||
ParameterExpression param = Expression.Parameter(typeof(T));
|
ParameterExpression param = defaultWhere?.Parameters.First() ?? Expression.Parameter(typeof(T));
|
||||||
Expression expression = defaultWhere?.Body;
|
Expression expression = defaultWhere?.Body;
|
||||||
|
|
||||||
foreach ((string key, string desired) in where)
|
foreach ((string key, string desired) in where)
|
||||||
|
@ -9,6 +9,7 @@ namespace Kyoo.Models
|
|||||||
{
|
{
|
||||||
[JsonIgnore] [NotMergable] public virtual IEnumerable<GenreLink> Links { get; set; }
|
[JsonIgnore] [NotMergable] public virtual IEnumerable<GenreLink> Links { get; set; }
|
||||||
|
|
||||||
|
[ExpressionRewrite(nameof(Links), nameof(GenreLink.Genre))]
|
||||||
[JsonIgnore] [NotMergable] public override IEnumerable<Show> Shows
|
[JsonIgnore] [NotMergable] public override IEnumerable<Show> Shows
|
||||||
{
|
{
|
||||||
get => Links?.Select(x => x.Show);
|
get => Links?.Select(x => x.Show);
|
||||||
|
@ -11,6 +11,7 @@ namespace Kyoo.Models
|
|||||||
public class ShowDE : Show
|
public class ShowDE : Show
|
||||||
{
|
{
|
||||||
[JsonIgnore] [NotMergable] public virtual IEnumerable<GenreLink> GenreLinks { get; set; }
|
[JsonIgnore] [NotMergable] public virtual IEnumerable<GenreLink> GenreLinks { get; set; }
|
||||||
|
[ExpressionRewrite(nameof(GenreLinks), nameof(GenreLink.Genre))]
|
||||||
public override IEnumerable<Genre> Genres
|
public override IEnumerable<Genre> Genres
|
||||||
{
|
{
|
||||||
get => GenreLinks?.Select(x => x.Genre);
|
get => GenreLinks?.Select(x => x.Genre);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user