-
Notifications
You must be signed in to change notification settings - Fork 108
/
BaseUniversalWrapperAspect.cs
101 lines (82 loc) · 4.34 KB
/
BaseUniversalWrapperAspect.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Aspects.Universal.Attributes;
using Aspects.Universal.Events;
namespace Aspects.Universal.Aspects
{
public abstract class BaseUniversalWrapperAspect
{
private delegate object Method(object[] args);
private delegate object Wrapper(Func<object[], object> target, object[] args);
private delegate object Handler(Func<object[], object> next, object[] args, AspectEventArgs eventArgs);
private static readonly ConcurrentDictionary<(MethodBase, Type), Lazy<Handler>> _delegateCache = new ConcurrentDictionary<(MethodBase, Type), Lazy<Handler>>();
private static readonly MethodInfo _asyncGenericHandler =
typeof(BaseUniversalWrapperAttribute).GetMethod(nameof(BaseUniversalWrapperAttribute.WrapAsync), BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly MethodInfo _syncGenericHandler =
typeof(BaseUniversalWrapperAttribute).GetMethod(nameof(BaseUniversalWrapperAttribute.WrapSync), BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly Type _voidTaskResult = Type.GetType("System.Threading.Tasks.VoidTaskResult");
protected object BaseHandle(
object instance,
Type type,
MethodBase method,
Func<object[], object> target,
string name,
object[] args,
Type returnType,
Attribute[] triggers)
{
var eventArgs = new AspectEventArgs
{
Instance = instance,
Type = type,
Method = method,
Name = name,
Args = args,
ReturnType = returnType,
Triggers = triggers
};
var wrappers = triggers.OfType<BaseUniversalWrapperAttribute>().ToArray();
var handler = GetMethodHandler(method, returnType, wrappers);
return handler(target, args, eventArgs);
}
private Handler CreateMethodHandler(Type returnType, IReadOnlyList<BaseUniversalWrapperAttribute> wrappers)
{
var targetParam = Expression.Parameter(typeof(Func<object[], object>), "orig");
var eventArgsParam = Expression.Parameter(typeof(AspectEventArgs), "event");
MethodInfo wrapperMethod;
if (typeof(Task).IsAssignableFrom(returnType))
{
var taskType = returnType.IsConstructedGenericType ? returnType.GenericTypeArguments[0] : _voidTaskResult;
returnType = typeof(Task<>).MakeGenericType(new[] { taskType });
wrapperMethod = _asyncGenericHandler.MakeGenericMethod(new[] { taskType });
}
else
{
if (returnType == typeof(void))
returnType = typeof(object);
wrapperMethod = _syncGenericHandler.MakeGenericMethod(new[] { returnType });
}
var converArgs = Expression.Parameter(typeof(object[]), "args");
var next = Expression.Lambda(Expression.Convert(Expression.Invoke(targetParam, converArgs), returnType), converArgs);
foreach (var wrapper in wrappers)
{
var argsParam = Expression.Parameter(typeof(object[]), "args");
next = Expression.Lambda(Expression.Call(Expression.Constant(wrapper), wrapperMethod, next, argsParam, eventArgsParam), argsParam);
}
var orig_args = Expression.Parameter(typeof(object[]), "orig_args");
var handler = Expression.Lambda<Handler>(Expression.Convert(Expression.Invoke(next, orig_args), typeof(object)), targetParam, orig_args, eventArgsParam);
var handlerCompiled = handler.Compile();
return handlerCompiled;
}
private Handler GetMethodHandler(MethodBase method, Type returnType, IReadOnlyList<BaseUniversalWrapperAttribute> wrappers)
{
var lazyHandler = _delegateCache.GetOrAdd((method, returnType), _ => new Lazy<Handler>(() => CreateMethodHandler(returnType, wrappers)));
return lazyHandler.Value;
}
}
}