0
|
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 }
|