Mercurial > pub > bltoolkit
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 } |