Mercurial > pub > bltoolkit
comparison Source/Aspects/Builders/InterceptorAspectBuilder.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.Reflection; | |
3 using System.Reflection.Emit; | |
4 | |
5 namespace BLToolkit.Aspects.Builders | |
6 { | |
7 using Reflection; | |
8 using TypeBuilder.Builders; | |
9 | |
10 public class InterceptorAspectBuilder : AbstractTypeBuilderBase | |
11 { | |
12 public InterceptorAspectBuilder( | |
13 Type interceptorType, InterceptType interceptType, string configString, int priority, bool localInterceptor) | |
14 { | |
15 _interceptorType = interceptorType; | |
16 _interceptType = interceptType; | |
17 _configString = configString; | |
18 _priority = priority; | |
19 _localInterceptor = localInterceptor; | |
20 } | |
21 | |
22 private readonly Type _interceptorType; | |
23 private readonly InterceptType _interceptType; | |
24 private readonly string _configString; | |
25 private readonly int _priority; | |
26 private readonly bool _localInterceptor; | |
27 | |
28 private FieldBuilder _interceptorField; | |
29 private LocalBuilder _infoField; | |
30 | |
31 public override int GetPriority(BuildContext context) | |
32 { | |
33 return _priority; | |
34 } | |
35 | |
36 public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders) | |
37 { | |
38 if (_interceptorType == null && _interceptType == 0) | |
39 return false; | |
40 | |
41 foreach (var builder in builders) | |
42 { | |
43 var interceptor = builder as InterceptorAspectBuilder; | |
44 | |
45 if (interceptor != null) | |
46 { | |
47 if (interceptor._interceptorType == null && interceptor._interceptType == 0) | |
48 return false; | |
49 | |
50 if (builder == this) | |
51 break; | |
52 } | |
53 } | |
54 | |
55 if (context.IsMethodOrProperty) switch (context.Step) | |
56 { | |
57 case BuildStep.Begin: return true; | |
58 case BuildStep.Before: return (_interceptType & InterceptType.BeforeCall) != 0; | |
59 case BuildStep.After: return (_interceptType & InterceptType.AfterCall) != 0; | |
60 case BuildStep.Catch: return (_interceptType & InterceptType.OnCatch) != 0; | |
61 case BuildStep.Finally: return (_interceptType & InterceptType.OnFinally) != 0; | |
62 case BuildStep.End: return true; | |
63 } | |
64 | |
65 return false; | |
66 } | |
67 | |
68 public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder) | |
69 { | |
70 var builder = typeBuilder as InterceptorAspectBuilder; | |
71 | |
72 return builder == null || _interceptorType != builder._interceptorType; | |
73 } | |
74 | |
75 public override void Build(BuildContext context) | |
76 { | |
77 if (context.Step == BuildStep.Begin || context.Step == BuildStep.End) | |
78 { | |
79 base.Build(context); | |
80 return; | |
81 } | |
82 | |
83 Context = context; | |
84 | |
85 var emit = Context.MethodBuilder.Emitter; | |
86 | |
87 // Push ref & out parameters. | |
88 // | |
89 var parameters = Context.CurrentMethod.GetParameters(); | |
90 | |
91 for (var i = 0; i < parameters.Length; i++) | |
92 { | |
93 var p = parameters[i]; | |
94 | |
95 if (!p.ParameterType.IsByRef) | |
96 continue; | |
97 | |
98 emit | |
99 .ldloc (_infoField) | |
100 .callvirt (typeof(InterceptCallInfo).GetProperty("ParameterValues").GetGetMethod()) | |
101 .ldc_i4 (i) | |
102 .ldargEx (p, true) | |
103 .stelem_ref | |
104 .end() | |
105 ; | |
106 } | |
107 | |
108 // Push return value. | |
109 // | |
110 if (Context.ReturnValue != null) | |
111 { | |
112 emit | |
113 .ldloc (_infoField) | |
114 .ldloc (Context.ReturnValue) | |
115 .boxIfValueType (Context.CurrentMethod.ReturnType) | |
116 .callvirt (typeof(InterceptCallInfo).GetProperty("ReturnValue").GetSetMethod()) | |
117 ; | |
118 } | |
119 | |
120 // Set Exception. | |
121 // | |
122 if (Context.Step == BuildStep.Catch) | |
123 { | |
124 emit | |
125 .ldloc(_infoField) | |
126 .ldloc(Context.Exception) | |
127 .callvirt(typeof(InterceptCallInfo).GetProperty("Exception").GetSetMethod()) | |
128 ; | |
129 } | |
130 | |
131 // Set intercept result. | |
132 // | |
133 emit | |
134 .ldloc (_infoField) | |
135 .ldc_i4 ((int)InterceptResult.Continue) | |
136 .callvirt (typeof(InterceptCallInfo).GetProperty("InterceptResult").GetSetMethod()) | |
137 ; | |
138 | |
139 // Set intercept type. | |
140 // | |
141 InterceptType interceptType; | |
142 | |
143 switch (Context.Step) | |
144 { | |
145 case BuildStep.Before: interceptType = InterceptType.BeforeCall; break; | |
146 case BuildStep.After: interceptType = InterceptType.AfterCall; break; | |
147 case BuildStep.Catch: interceptType = InterceptType.OnCatch; break; | |
148 case BuildStep.Finally: interceptType = InterceptType.OnFinally; break; | |
149 default: | |
150 throw new InvalidOperationException(); | |
151 } | |
152 | |
153 emit | |
154 .ldloc (_infoField) | |
155 .ldc_i4 ((int)interceptType) | |
156 .callvirt (typeof(InterceptCallInfo).GetProperty("InterceptType").GetSetMethod()) | |
157 | |
158 // Call interceptor. | |
159 // | |
160 .LoadField(_interceptorField) | |
161 .ldloc (_infoField) | |
162 .callvirt (typeof(IInterceptor), "Intercept", typeof(InterceptCallInfo)) | |
163 ; | |
164 | |
165 // Pop return value. | |
166 // | |
167 if (Context.ReturnValue != null) | |
168 { | |
169 emit | |
170 .ldloc (_infoField) | |
171 .callvirt (typeof(InterceptCallInfo).GetProperty("ReturnValue").GetGetMethod()) | |
172 .CastFromObject (Context.CurrentMethod.ReturnType) | |
173 .stloc (Context.ReturnValue) | |
174 ; | |
175 } | |
176 | |
177 // Pop ref & out parameters. | |
178 // | |
179 for (var i = 0; i < parameters.Length; i++) | |
180 { | |
181 var p = parameters[i]; | |
182 | |
183 if (!p.ParameterType.IsByRef) | |
184 continue; | |
185 | |
186 var type = p.ParameterType.GetElementType(); | |
187 | |
188 emit | |
189 .ldarg (p) | |
190 .ldloc (_infoField) | |
191 .callvirt (typeof(InterceptCallInfo).GetProperty("ParameterValues").GetGetMethod()) | |
192 .ldc_i4 (i) | |
193 .ldelem_ref | |
194 .CastFromObject (type) | |
195 .stind (type) | |
196 ; | |
197 } | |
198 | |
199 // Check InterceptResult | |
200 emit | |
201 .ldloc (_infoField) | |
202 .callvirt (typeof(InterceptCallInfo).GetProperty("InterceptResult").GetGetMethod()) | |
203 .ldc_i4 ((int)InterceptResult.Return) | |
204 .beq (Context.ReturnLabel) | |
205 ; | |
206 } | |
207 | |
208 private static int _methodCounter; | |
209 | |
210 private LocalBuilder GetInfoField() | |
211 { | |
212 var field = Context.GetItem<LocalBuilder>("$BLToolkit.InfoField"); | |
213 | |
214 if (field == null) | |
215 { | |
216 _methodCounter++; | |
217 | |
218 // Create MethodInfo field. | |
219 // | |
220 var methodInfo = Context.CreatePrivateStaticField( | |
221 "_methodInfo$" + Context.CurrentMethod.Name + _methodCounter, typeof(CallMethodInfo)); | |
222 | |
223 var emit = Context.MethodBuilder.Emitter; | |
224 | |
225 var checkMethodInfo = emit.DefineLabel(); | |
226 | |
227 emit | |
228 .LoadField (methodInfo) | |
229 .brtrue_s (checkMethodInfo) | |
230 .call (typeof(MethodBase), "GetCurrentMethod") | |
231 .castclass (typeof(MethodInfo)) | |
232 .newobj (TypeHelper.GetConstructor(typeof(CallMethodInfo), typeof(MethodInfo))) | |
233 .stsfld (methodInfo) | |
234 .MarkLabel (checkMethodInfo) | |
235 ; | |
236 | |
237 // Create & initialize the field. | |
238 // | |
239 field = emit.DeclareLocal(typeof(InterceptCallInfo)); | |
240 | |
241 emit | |
242 .newobj (TypeHelper.GetDefaultConstructor(typeof(InterceptCallInfo))) | |
243 .dup | |
244 .ldarg_0 | |
245 .callvirt (typeof(InterceptCallInfo).GetProperty("Object").GetSetMethod()) | |
246 | |
247 .dup | |
248 .LoadField(methodInfo) | |
249 .callvirt (typeof(InterceptCallInfo).GetProperty("CallMethodInfo").GetSetMethod()) | |
250 ; | |
251 | |
252 var parameters = Context.CurrentMethod.GetParameters(); | |
253 | |
254 for (var i = 0; i < parameters.Length; i++) | |
255 { | |
256 var p = parameters[i]; | |
257 | |
258 if (p.ParameterType.IsByRef) | |
259 continue; | |
260 | |
261 emit | |
262 .dup | |
263 .callvirt (typeof(InterceptCallInfo).GetProperty("ParameterValues").GetGetMethod()) | |
264 .ldc_i4 (i) | |
265 .ldargEx (p, true) | |
266 .stelem_ref | |
267 .end() | |
268 ; | |
269 } | |
270 | |
271 emit.stloc(field); | |
272 | |
273 Context.Items.Add("$BLToolkit.MethodInfo", methodInfo); | |
274 Context.Items.Add("$BLToolkit.InfoField", field); | |
275 } | |
276 | |
277 return field; | |
278 } | |
279 | |
280 private FieldBuilder GetInterceptorField() | |
281 { | |
282 var fieldName = "_interceptor$" + _interceptorType.FullName + "$_" + Context.CurrentMethod.Name + _methodCounter; | |
283 var field = Context.GetField(fieldName); | |
284 | |
285 if (field == null) | |
286 { | |
287 // Create MethodInfo field. | |
288 // | |
289 field = _localInterceptor? | |
290 Context.CreatePrivateField (fieldName, typeof(IInterceptor)): | |
291 Context.CreatePrivateStaticField(fieldName, typeof(IInterceptor)); | |
292 | |
293 var emit = Context.MethodBuilder.Emitter; | |
294 | |
295 var checkInterceptor = emit.DefineLabel(); | |
296 var methodInfo = Context.GetItem<FieldBuilder>("$BLToolkit.MethodInfo"); | |
297 | |
298 emit | |
299 .LoadField (field) | |
300 .brtrue_s (checkInterceptor) | |
301 ; | |
302 | |
303 if (!field.IsStatic) | |
304 emit.ldarg_0.end(); | |
305 | |
306 emit | |
307 .newobj (TypeHelper.GetDefaultConstructor(_interceptorType)) | |
308 .castclass (typeof(IInterceptor)) | |
309 ; | |
310 | |
311 if (field.IsStatic) | |
312 emit.stsfld(field); | |
313 else | |
314 emit.stfld(field); | |
315 | |
316 emit | |
317 .LoadField (field) | |
318 .LoadField (methodInfo) | |
319 .ldstrEx (_configString ?? "") | |
320 .callvirt (typeof(IInterceptor), "Init", typeof(CallMethodInfo), typeof(string)) | |
321 | |
322 .MarkLabel (checkInterceptor) | |
323 ; | |
324 } | |
325 | |
326 return field; | |
327 } | |
328 | |
329 protected override void BeginMethodBuild() | |
330 { | |
331 _infoField = GetInfoField(); | |
332 _interceptorField = GetInterceptorField(); | |
333 } | |
334 | |
335 protected override void EndMethodBuild() | |
336 { | |
337 Context.Items.Remove("$BLToolkit.MethodInfo"); | |
338 Context.Items.Remove("$BLToolkit.InfoField"); | |
339 } | |
340 } | |
341 } |