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 }