Mercurial > pub > bltoolkit
diff Source/Aspects/Builders/AsyncAspectBuilder.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Aspects/Builders/AsyncAspectBuilder.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,274 @@ +using System; +using System.Reflection; +using System.Threading; + +namespace BLToolkit.Aspects.Builders +{ + using Properties; + using TypeBuilder; + using TypeBuilder.Builders; + + /// <summary> + /// This aspect simplifies asynchronous operations. + /// </summary> + public class AsyncAspectBuilder : AbstractTypeBuilderBase + { + private readonly string _targetMethodName; + private readonly Type[] _parameterTypes; + + public AsyncAspectBuilder(string targetMethodName, Type[] parameterTypes) + { + _targetMethodName = targetMethodName; + _parameterTypes = parameterTypes; + } + + public override int GetPriority(BuildContext context) + { + return TypeBuilderConsts.Priority.AsyncAspect; + } + + public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder) + { + if (context.IsBuildStep) + return false; + + var list = new AbstractTypeBuilderList(2) { this, typeBuilder }; + var step = context.Step; + + try + { + context.Step = BuildStep.Build; + + return typeBuilder.IsApplied(context, list); + } + finally + { + context.Step = step; + } + } + + public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders) + { + if (context == null) throw new ArgumentNullException("context"); + + return context.IsBuildStep && context.BuildElement == BuildElement.AbstractMethod; + } + + protected override void BuildAbstractMethod() + { + var mi = Context.CurrentMethod; + + if (mi.ReturnType == typeof(IAsyncResult)) + BuildBeginMethod(); + else + { + var parameters = mi.GetParameters(); + + if (parameters.Length == 1 && parameters[0].ParameterType == typeof(IAsyncResult)) + BuildEndMethod(); + else + throw new TypeBuilderException(string.Format("Method '{0}.{1}' is not a 'Begin' nor an 'End' method.", mi.DeclaringType.FullName, mi.Name)); + } + } + + private void BuildBeginMethod() + { + var mi = Context.CurrentMethod; + var method = GetTargetMethod(Context, "Begin"); + var delegateType = EnsureDelegateType(Context, method); + var emit = Context.MethodBuilder.Emitter; + var type = typeof(InternalAsyncResult); + var arLocal = emit.DeclareLocal(type); + var dLocal = emit.DeclareLocal(delegateType); + + emit + .newobj (type) + .dup + .dup + .dup + .stloc (arLocal) + .ldarg_0 + .ldftn (method) + .newobj (delegateType, typeof(object), typeof(IntPtr)) + .stloc (dLocal) + .ldloc (dLocal) + .stfld (type.GetField("Delegate")) + .ldloc (dLocal) + ; + + var callbackIndex = -1; + var parameters = mi.GetParameters(); + + for (var i = 0; i < parameters.Length; ++i) + { + if (parameters[i].ParameterType == typeof(AsyncCallback)) + { + callbackIndex = i; + emit + .ldloc (arLocal) + .dup + .ldarg (parameters[i]) + .stfld (type.GetField("AsyncCallback")) + .ldftn (type.GetMethod("CallBack")) + .newobj (typeof(AsyncCallback), typeof(object), typeof(IntPtr)) + ; + } + else + emit.ldarg(parameters[i]); + } + + if (callbackIndex < 0) + { + // Callback omitted + // + emit + .ldnull + .ldnull + .end(); + } + else if (callbackIndex == parameters.Length - 1) + { + // State omitted + // + emit + .ldnull + .end(); + } + + emit + .callvirt (delegateType.GetMethod("BeginInvoke")) + .stfld (type.GetField("InnerResult")) + .stloc (Context.ReturnValue) + ; + } + + private void BuildEndMethod() + { + var method = GetTargetMethod(Context, "End"); + var delegateType = EnsureDelegateType(Context, method); + var emit = Context.MethodBuilder.Emitter; + var type = typeof(InternalAsyncResult); + var arLocal = emit.DeclareLocal(type); + + emit + .ldarg_1 + .castclass (type) + .dup + .stloc (arLocal) + .ldfld (type.GetField("Delegate")) + .castclass (delegateType) + .ldloc (arLocal) + .ldfld (type.GetField("InnerResult")) + .callvirt (delegateType, "EndInvoke", typeof(IAsyncResult)); + + if (Context.ReturnValue != null) + emit.stloc(Context.ReturnValue); + } + + private MethodInfo GetTargetMethod(BuildContext context, string prefix) + { + var targetMethodName = _targetMethodName; + + if (targetMethodName == null) + { + var mi = context.CurrentMethod; + var name = mi.Name; + + if (name.StartsWith(prefix)) + targetMethodName = name.Substring(prefix.Length); + else + throw new TypeBuilderException(string.Format( + Resources.AsyncAspectBuilder_NoTargetMethod, + mi.DeclaringType.FullName, mi.Name)); + } + + return _parameterTypes == null? + context.Type.GetMethod(targetMethodName): + context.Type.GetMethod(targetMethodName, _parameterTypes); + } + + private static Type EnsureDelegateType(BuildContext context, MethodInfo method) + { + // The delegate should be defined as inner type of context.TypeBuilder. + // It's possible, but we can not define and use newly defined type as Emit target in its owner type. + // To solve this problem, we should create a top level delegate and make sure its name is unique. + // + var delegateName = context.TypeBuilder.TypeBuilder.FullName + "$" + method.Name + "$Delegate"; + var delegateType = context.GetItem<Type>(delegateName); + + if (delegateType == null) + { + var pi = method.GetParameters(); + var parameters = new Type[pi.Length]; + + for (var i = 0; i < pi.Length; i++) + parameters[i] = pi[i].ParameterType; + + const MethodImplAttributes mia = MethodImplAttributes.Runtime | MethodImplAttributes.Managed; + const MethodAttributes ma = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + + var delegateBuilder = context.AssemblyBuilder.DefineType(delegateName, + TypeAttributes.Class | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, + typeof(MulticastDelegate)); + + // Create constructor + // + var ctorBuilder = delegateBuilder.DefineConstructor( + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName, CallingConventions.Standard, + typeof(object), typeof(IntPtr)); + ctorBuilder.ConstructorBuilder.SetImplementationFlags(mia); + + // Define the BeginInvoke method for the delegate + // + var beginParameters = new Type[parameters.Length + 2]; + + Array.Copy(parameters, 0, beginParameters, 0, parameters.Length); + beginParameters[parameters.Length] = typeof(AsyncCallback); + beginParameters[parameters.Length+1] = typeof(object); + + var methodBuilder = delegateBuilder.DefineMethod("BeginInvoke", ma, typeof(IAsyncResult), beginParameters); + + methodBuilder.MethodBuilder.SetImplementationFlags(mia); + + // Define the EndInvoke method for the delegate + // + methodBuilder = delegateBuilder.DefineMethod("EndInvoke", ma, method.ReturnType, typeof(IAsyncResult)); + methodBuilder.MethodBuilder.SetImplementationFlags(mia); + + // Define the Invoke method for the delegate + // + methodBuilder = delegateBuilder.DefineMethod("Invoke", ma, method.ReturnType, parameters); + methodBuilder.MethodBuilder.SetImplementationFlags(mia); + + context.Items.Add(delegateName, delegateType = delegateBuilder.Create()); + } + + return delegateType; + } + + #region Helper + + /// <summary> + /// Reserved for internal BLToolkit use. + /// </summary> + public class InternalAsyncResult : IAsyncResult + { + public IAsyncResult InnerResult; + public Delegate Delegate; + public AsyncCallback AsyncCallback; + + public void CallBack(IAsyncResult ar) + { + if (AsyncCallback != null) + AsyncCallback(this); + } + + public bool IsCompleted { get { return InnerResult.IsCompleted; } } + public WaitHandle AsyncWaitHandle { get { return InnerResult.AsyncWaitHandle; } } + public object AsyncState { get { return InnerResult.AsyncState; } } + public bool CompletedSynchronously { get { return InnerResult.CompletedSynchronously; } } + } + + #endregion + } +}