Mercurial > pub > bltoolkit
comparison Source/Aspects/Builders/AsyncAspectBuilder.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.Threading; | |
4 | |
5 namespace BLToolkit.Aspects.Builders | |
6 { | |
7 using Properties; | |
8 using TypeBuilder; | |
9 using TypeBuilder.Builders; | |
10 | |
11 /// <summary> | |
12 /// This aspect simplifies asynchronous operations. | |
13 /// </summary> | |
14 public class AsyncAspectBuilder : AbstractTypeBuilderBase | |
15 { | |
16 private readonly string _targetMethodName; | |
17 private readonly Type[] _parameterTypes; | |
18 | |
19 public AsyncAspectBuilder(string targetMethodName, Type[] parameterTypes) | |
20 { | |
21 _targetMethodName = targetMethodName; | |
22 _parameterTypes = parameterTypes; | |
23 } | |
24 | |
25 public override int GetPriority(BuildContext context) | |
26 { | |
27 return TypeBuilderConsts.Priority.AsyncAspect; | |
28 } | |
29 | |
30 public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder) | |
31 { | |
32 if (context.IsBuildStep) | |
33 return false; | |
34 | |
35 var list = new AbstractTypeBuilderList(2) { this, typeBuilder }; | |
36 var step = context.Step; | |
37 | |
38 try | |
39 { | |
40 context.Step = BuildStep.Build; | |
41 | |
42 return typeBuilder.IsApplied(context, list); | |
43 } | |
44 finally | |
45 { | |
46 context.Step = step; | |
47 } | |
48 } | |
49 | |
50 public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders) | |
51 { | |
52 if (context == null) throw new ArgumentNullException("context"); | |
53 | |
54 return context.IsBuildStep && context.BuildElement == BuildElement.AbstractMethod; | |
55 } | |
56 | |
57 protected override void BuildAbstractMethod() | |
58 { | |
59 var mi = Context.CurrentMethod; | |
60 | |
61 if (mi.ReturnType == typeof(IAsyncResult)) | |
62 BuildBeginMethod(); | |
63 else | |
64 { | |
65 var parameters = mi.GetParameters(); | |
66 | |
67 if (parameters.Length == 1 && parameters[0].ParameterType == typeof(IAsyncResult)) | |
68 BuildEndMethod(); | |
69 else | |
70 throw new TypeBuilderException(string.Format("Method '{0}.{1}' is not a 'Begin' nor an 'End' method.", mi.DeclaringType.FullName, mi.Name)); | |
71 } | |
72 } | |
73 | |
74 private void BuildBeginMethod() | |
75 { | |
76 var mi = Context.CurrentMethod; | |
77 var method = GetTargetMethod(Context, "Begin"); | |
78 var delegateType = EnsureDelegateType(Context, method); | |
79 var emit = Context.MethodBuilder.Emitter; | |
80 var type = typeof(InternalAsyncResult); | |
81 var arLocal = emit.DeclareLocal(type); | |
82 var dLocal = emit.DeclareLocal(delegateType); | |
83 | |
84 emit | |
85 .newobj (type) | |
86 .dup | |
87 .dup | |
88 .dup | |
89 .stloc (arLocal) | |
90 .ldarg_0 | |
91 .ldftn (method) | |
92 .newobj (delegateType, typeof(object), typeof(IntPtr)) | |
93 .stloc (dLocal) | |
94 .ldloc (dLocal) | |
95 .stfld (type.GetField("Delegate")) | |
96 .ldloc (dLocal) | |
97 ; | |
98 | |
99 var callbackIndex = -1; | |
100 var parameters = mi.GetParameters(); | |
101 | |
102 for (var i = 0; i < parameters.Length; ++i) | |
103 { | |
104 if (parameters[i].ParameterType == typeof(AsyncCallback)) | |
105 { | |
106 callbackIndex = i; | |
107 emit | |
108 .ldloc (arLocal) | |
109 .dup | |
110 .ldarg (parameters[i]) | |
111 .stfld (type.GetField("AsyncCallback")) | |
112 .ldftn (type.GetMethod("CallBack")) | |
113 .newobj (typeof(AsyncCallback), typeof(object), typeof(IntPtr)) | |
114 ; | |
115 } | |
116 else | |
117 emit.ldarg(parameters[i]); | |
118 } | |
119 | |
120 if (callbackIndex < 0) | |
121 { | |
122 // Callback omitted | |
123 // | |
124 emit | |
125 .ldnull | |
126 .ldnull | |
127 .end(); | |
128 } | |
129 else if (callbackIndex == parameters.Length - 1) | |
130 { | |
131 // State omitted | |
132 // | |
133 emit | |
134 .ldnull | |
135 .end(); | |
136 } | |
137 | |
138 emit | |
139 .callvirt (delegateType.GetMethod("BeginInvoke")) | |
140 .stfld (type.GetField("InnerResult")) | |
141 .stloc (Context.ReturnValue) | |
142 ; | |
143 } | |
144 | |
145 private void BuildEndMethod() | |
146 { | |
147 var method = GetTargetMethod(Context, "End"); | |
148 var delegateType = EnsureDelegateType(Context, method); | |
149 var emit = Context.MethodBuilder.Emitter; | |
150 var type = typeof(InternalAsyncResult); | |
151 var arLocal = emit.DeclareLocal(type); | |
152 | |
153 emit | |
154 .ldarg_1 | |
155 .castclass (type) | |
156 .dup | |
157 .stloc (arLocal) | |
158 .ldfld (type.GetField("Delegate")) | |
159 .castclass (delegateType) | |
160 .ldloc (arLocal) | |
161 .ldfld (type.GetField("InnerResult")) | |
162 .callvirt (delegateType, "EndInvoke", typeof(IAsyncResult)); | |
163 | |
164 if (Context.ReturnValue != null) | |
165 emit.stloc(Context.ReturnValue); | |
166 } | |
167 | |
168 private MethodInfo GetTargetMethod(BuildContext context, string prefix) | |
169 { | |
170 var targetMethodName = _targetMethodName; | |
171 | |
172 if (targetMethodName == null) | |
173 { | |
174 var mi = context.CurrentMethod; | |
175 var name = mi.Name; | |
176 | |
177 if (name.StartsWith(prefix)) | |
178 targetMethodName = name.Substring(prefix.Length); | |
179 else | |
180 throw new TypeBuilderException(string.Format( | |
181 Resources.AsyncAspectBuilder_NoTargetMethod, | |
182 mi.DeclaringType.FullName, mi.Name)); | |
183 } | |
184 | |
185 return _parameterTypes == null? | |
186 context.Type.GetMethod(targetMethodName): | |
187 context.Type.GetMethod(targetMethodName, _parameterTypes); | |
188 } | |
189 | |
190 private static Type EnsureDelegateType(BuildContext context, MethodInfo method) | |
191 { | |
192 // The delegate should be defined as inner type of context.TypeBuilder. | |
193 // It's possible, but we can not define and use newly defined type as Emit target in its owner type. | |
194 // To solve this problem, we should create a top level delegate and make sure its name is unique. | |
195 // | |
196 var delegateName = context.TypeBuilder.TypeBuilder.FullName + "$" + method.Name + "$Delegate"; | |
197 var delegateType = context.GetItem<Type>(delegateName); | |
198 | |
199 if (delegateType == null) | |
200 { | |
201 var pi = method.GetParameters(); | |
202 var parameters = new Type[pi.Length]; | |
203 | |
204 for (var i = 0; i < pi.Length; i++) | |
205 parameters[i] = pi[i].ParameterType; | |
206 | |
207 const MethodImplAttributes mia = MethodImplAttributes.Runtime | MethodImplAttributes.Managed; | |
208 const MethodAttributes ma = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; | |
209 | |
210 var delegateBuilder = context.AssemblyBuilder.DefineType(delegateName, | |
211 TypeAttributes.Class | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, | |
212 typeof(MulticastDelegate)); | |
213 | |
214 // Create constructor | |
215 // | |
216 var ctorBuilder = delegateBuilder.DefineConstructor( | |
217 MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName, CallingConventions.Standard, | |
218 typeof(object), typeof(IntPtr)); | |
219 ctorBuilder.ConstructorBuilder.SetImplementationFlags(mia); | |
220 | |
221 // Define the BeginInvoke method for the delegate | |
222 // | |
223 var beginParameters = new Type[parameters.Length + 2]; | |
224 | |
225 Array.Copy(parameters, 0, beginParameters, 0, parameters.Length); | |
226 beginParameters[parameters.Length] = typeof(AsyncCallback); | |
227 beginParameters[parameters.Length+1] = typeof(object); | |
228 | |
229 var methodBuilder = delegateBuilder.DefineMethod("BeginInvoke", ma, typeof(IAsyncResult), beginParameters); | |
230 | |
231 methodBuilder.MethodBuilder.SetImplementationFlags(mia); | |
232 | |
233 // Define the EndInvoke method for the delegate | |
234 // | |
235 methodBuilder = delegateBuilder.DefineMethod("EndInvoke", ma, method.ReturnType, typeof(IAsyncResult)); | |
236 methodBuilder.MethodBuilder.SetImplementationFlags(mia); | |
237 | |
238 // Define the Invoke method for the delegate | |
239 // | |
240 methodBuilder = delegateBuilder.DefineMethod("Invoke", ma, method.ReturnType, parameters); | |
241 methodBuilder.MethodBuilder.SetImplementationFlags(mia); | |
242 | |
243 context.Items.Add(delegateName, delegateType = delegateBuilder.Create()); | |
244 } | |
245 | |
246 return delegateType; | |
247 } | |
248 | |
249 #region Helper | |
250 | |
251 /// <summary> | |
252 /// Reserved for internal BLToolkit use. | |
253 /// </summary> | |
254 public class InternalAsyncResult : IAsyncResult | |
255 { | |
256 public IAsyncResult InnerResult; | |
257 public Delegate Delegate; | |
258 public AsyncCallback AsyncCallback; | |
259 | |
260 public void CallBack(IAsyncResult ar) | |
261 { | |
262 if (AsyncCallback != null) | |
263 AsyncCallback(this); | |
264 } | |
265 | |
266 public bool IsCompleted { get { return InnerResult.IsCompleted; } } | |
267 public WaitHandle AsyncWaitHandle { get { return InnerResult.AsyncWaitHandle; } } | |
268 public object AsyncState { get { return InnerResult.AsyncState; } } | |
269 public bool CompletedSynchronously { get { return InnerResult.CompletedSynchronously; } } | |
270 } | |
271 | |
272 #endregion | |
273 } | |
274 } |