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