comparison Source/Aspects/Builders/OverloadAspectBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
1 using System;
2 using System.Collections.Generic;
3 using System.Reflection;
4 using System.Reflection.Emit;
5
6 using BLToolkit.Properties;
7 using BLToolkit.Reflection;
8 using BLToolkit.Reflection.Emit;
9 using BLToolkit.TypeBuilder;
10 using BLToolkit.TypeBuilder.Builders;
11
12 namespace BLToolkit.Aspects.Builders
13 {
14 public class OverloadAspectBuilder: AbstractTypeBuilderBase
15 {
16 private readonly string _overloadedMethodName;
17 private readonly Type[] _parameterTypes;
18
19 public OverloadAspectBuilder(string overloadedMethodName, Type[] parameterTypes)
20 {
21 _overloadedMethodName = overloadedMethodName;
22 _parameterTypes = parameterTypes;
23 }
24
25 public override int GetPriority(BuildContext context)
26 {
27 return TypeBuilderConsts.Priority.OverloadAspect;
28 }
29
30 public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder)
31 {
32 if (context.IsBuildStep)
33 return false;
34
35 AbstractTypeBuilderList list = new AbstractTypeBuilderList(2);
36
37 list.Add(this);
38 list.Add(typeBuilder);
39
40 BuildStep step = context.Step;
41
42 try
43 {
44 context.Step = BuildStep.Build;
45
46 return typeBuilder.IsApplied(context, list);
47 }
48 finally
49 {
50 context.Step = step;
51 }
52 }
53
54 public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders)
55 {
56 if (context == null) throw new ArgumentNullException("context");
57
58 return context.IsBuildStep && context.BuildElement == BuildElement.AbstractMethod;
59 }
60
61 protected override void BuildAbstractMethod()
62 {
63 MethodInfo currentMethod = Context.CurrentMethod;
64 string methodName = _overloadedMethodName ?? currentMethod.Name;
65 MethodInfo overloadedMethod = GetOverloadedMethod(methodName);
66
67 if (overloadedMethod == null)
68 {
69 throw new TypeBuilderException(string.Format(
70 Resources.OverloadAspectBuilder_NoOverloadedMethod,
71 Context.Type.FullName, methodName));
72 }
73
74 EmitHelper emit = Context.MethodBuilder.Emitter;
75 List<ParameterInfo> parameters = new List<ParameterInfo>(currentMethod.GetParameters());
76
77 if (!overloadedMethod.IsStatic)
78 emit.ldarg_0.end();
79
80 foreach (ParameterInfo param in overloadedMethod.GetParameters())
81 {
82 ParameterInfo currentMethodParameter = null;
83 foreach (ParameterInfo p in parameters)
84 {
85 if (p.Name != param.Name)
86 continue;
87
88 currentMethodParameter = p;
89 parameters.Remove(p);
90 break;
91 }
92
93 if (currentMethodParameter != null)
94 {
95 emit.ldarg(currentMethodParameter);
96 }
97 else
98 {
99 Type type = param.ParameterType;
100 bool isRef = false;
101
102 if (type.IsByRef)
103 {
104 type = type.GetElementType();
105 isRef = true;
106 }
107
108 if (type.IsValueType && !type.IsPrimitive)
109 {
110 LocalBuilder localBuilder = emit.DeclareLocal(type);
111
112 emit
113 .ldloca (localBuilder)
114 .initobj (type)
115 ;
116
117 if (isRef)
118 emit.ldloca (localBuilder);
119 else
120 emit.ldloc (localBuilder);
121
122 }
123 else
124 {
125 if ((param.Attributes & ParameterAttributes.HasDefault) == 0 ||
126 !emit.LoadWellKnownValue(param.DefaultValue))
127 {
128 emit.LoadInitValue(type);
129 }
130
131 if (isRef)
132 {
133 LocalBuilder localBuilder = emit.DeclareLocal(type);
134
135 emit
136 .stloc (localBuilder)
137 .ldloca (localBuilder)
138 ;
139 }
140 }
141 }
142 }
143
144 // Finally, call the method we override.
145 //
146 if (overloadedMethod.IsStatic || overloadedMethod.IsFinal)
147 emit.call (overloadedMethod);
148 else
149 emit.callvirt (overloadedMethod);
150
151 if (currentMethod.ReturnType != typeof(void))
152 emit.stloc(Context.ReturnValue);
153 }
154
155 private MethodInfo GetOverloadedMethod(string methodName)
156 {
157 MethodInfo currentMethod = Context.CurrentMethod;
158 MethodInfo bestMatch = null;
159 int bestMatchParametersCount = -1;
160 ParameterInfo[] currentMethodParameters = currentMethod.GetParameters();
161
162 if (_parameterTypes != null)
163 {
164 bestMatch = Context.Type.GetMethod(methodName, _parameterTypes);
165 return bestMatch != null && MatchParameters(currentMethodParameters, bestMatch.GetParameters()) >= 0? bestMatch: null;
166 }
167
168 const BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Instance| BindingFlags.Public | BindingFlags.NonPublic;
169
170 foreach (MethodInfo m in Context.Type.GetMethods(bindingFlags))
171 {
172 if (m.IsPrivate || m.Name != methodName || m.IsGenericMethod != currentMethod.IsGenericMethod)
173 continue;
174
175 if (!TypeHelper.CompareParameterTypes(m.ReturnType, currentMethod.ReturnType))
176 continue;
177
178 if (m.IsDefined(typeof(OverloadAttribute), true))
179 continue;
180
181 ParameterInfo[] overloadedMethodParameters = m.GetParameters();
182 if (overloadedMethodParameters.Length <= bestMatchParametersCount)
183 continue;
184
185 int matchedParameters = MatchParameters(overloadedMethodParameters, currentMethodParameters);
186 if (matchedParameters <= bestMatchParametersCount)
187 continue;
188
189 bestMatchParametersCount = matchedParameters;
190 bestMatch = m;
191 }
192
193 return bestMatch;
194 }
195
196 private static int MatchParameters(ParameterInfo[] parametersToMatch, ParameterInfo[] existingParameters)
197 {
198 int matchedParameters = 0;
199 List<ParameterInfo> existingParametersList = new List<ParameterInfo>(existingParameters);
200 foreach (ParameterInfo param in parametersToMatch)
201 {
202 foreach (ParameterInfo existing in existingParametersList)
203 {
204 if (existing.Name != param.Name)
205 continue;
206
207 if (!TypeHelper.CompareParameterTypes(param.ParameterType, existing.ParameterType))
208 return -1;
209
210 ++matchedParameters;
211 existingParametersList.Remove(existing);
212 break;
213 }
214 }
215
216 return matchedParameters;
217 }
218 }
219 }