private static AdviceContext CreateAdviceContext(AdviceValues adviceValues, AspectInfo aspectInfo) { AdviceContext adviceContext = new InnerMethodContext(adviceValues, aspectInfo.PointcutMethod, aspectInfo.PointcutMethodDelegate); for (var adviceIndex = aspectInfo.Advices.Count - 1; adviceIndex >= 0; adviceIndex--) { var advice = aspectInfo.Advices[adviceIndex]; // aspects are processed from highest to lowest level, so they are linked here in the opposite order // 3. as parameter if (advice.ParameterAdvice != null && advice.ParameterIndex.HasValue) { var parameterIndex = advice.ParameterIndex.Value; var parameterInfo = GetParameterInfo(aspectInfo.AdvisedMethod, parameterIndex); adviceContext = new ParameterAdviceContext(advice.ParameterAdvice, parameterInfo, parameterIndex, adviceValues, adviceContext); } // 2. as method if (advice.MethodAdvice != null) { adviceContext = new MethodAdviceContext(advice.MethodAdvice, aspectInfo.AdvisedMethod, adviceValues, adviceContext); } // 2b. as async method if (advice.AsyncMethodAdvice != null) { adviceContext = new MethodAsyncAdviceContext(advice.AsyncMethodAdvice, aspectInfo.AdvisedMethod, adviceValues, adviceContext); } // 1. as property if (advice.PropertyAdvice != null && aspectInfo.PointcutProperty != null) { adviceContext = new PropertyAdviceContext(advice.PropertyAdvice, aspectInfo.PointcutProperty, aspectInfo.IsPointcutPropertySetter, adviceValues, adviceContext); } // 1b. as event if (advice.EventAdvice != null && aspectInfo.PointcutEvent != null) { adviceContext = new EventAdviceContext(advice.EventAdvice, aspectInfo.PointcutEvent, aspectInfo.IsPointcutEventAdder, adviceValues, adviceContext); } } return(adviceContext); }
/// <summary> /// Runs a method interception. /// We use a static method here, if one day we want to reuse Invocations or change mecanism, /// it will be easier from C# code /// </summary> /// <param name="target">The target.</param> /// <param name="parameters">The parameters.</param> /// <param name="methodHandle">The method handle.</param> /// <param name="innerMethodHandle">The inner method handle.</param> /// <param name="delegatableMethodHandle">The delegatable method handle.</param> /// <param name="typeHandle">The type handle.</param> /// <param name="abstractedTarget">if set to <c>true</c> [abstracted target].</param> /// <param name="genericArguments">The generic arguments (to static type and/or method) in a single array.</param> /// <returns></returns> // ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMethodReturnValue.Global public static object ProceedAdvice2(object target, object[] parameters, RuntimeMethodHandle methodHandle, RuntimeMethodHandle innerMethodHandle, RuntimeMethodHandle delegatableMethodHandle, RuntimeTypeHandle typeHandle, bool abstractedTarget, Type[] genericArguments) { var aspectInfo = GetAspectInfo(methodHandle, innerMethodHandle, delegatableMethodHandle, typeHandle, abstractedTarget, genericArguments); // this is the case with auto implemented interface if (target is AdvisedInterface advisedInterface) { aspectInfo = aspectInfo.AddAdvice(new AdviceInfo(advisedInterface.Advice)); } foreach (var advice in aspectInfo.Advices) { InjectIntroducedFields(advice, aspectInfo.AdvisedMethod.DeclaringType); } // from here, we build an advice chain, with at least one final advice: the one who calls the method var adviceValues = new AdviceValues(target, aspectInfo.AdvisedMethod.DeclaringType, parameters); // at least there is one context AdviceContext adviceContext = new InnerMethodContext(adviceValues, aspectInfo.PointcutMethod, aspectInfo.PointcutMethodDelegate); for (var adviceIndex = aspectInfo.Advices.Count - 1; adviceIndex >= 0; adviceIndex--) { var advice = aspectInfo.Advices[adviceIndex]; // aspects are processed from highest to lowest level, so they are linked here in the opposite order // 3. as parameter if (advice.ParameterAdvice != null && advice.ParameterIndex.HasValue) { var parameterIndex = advice.ParameterIndex.Value; var parameterInfo = GetParameterInfo(aspectInfo.AdvisedMethod, parameterIndex); adviceContext = new ParameterAdviceContext(advice.ParameterAdvice, parameterInfo, parameterIndex, adviceValues, adviceContext); } // 2. as method if (advice.MethodAdvice != null) { adviceContext = new MethodAdviceContext(advice.MethodAdvice, aspectInfo.AdvisedMethod, adviceValues, adviceContext); } // 2b. as async method if (advice.AsyncMethodAdvice != null) { adviceContext = new MethodAsyncAdviceContext(advice.AsyncMethodAdvice, aspectInfo.AdvisedMethod, adviceValues, adviceContext); } // 1. as property if (advice.PropertyAdvice != null && aspectInfo.PointcutProperty != null) { adviceContext = new PropertyAdviceContext(advice.PropertyAdvice, aspectInfo.PointcutProperty, aspectInfo.IsPointcutPropertySetter, adviceValues, adviceContext); } // 1b. as event if (advice.EventAdvice != null && aspectInfo.PointcutEvent != null) { adviceContext = new EventAdviceContext(advice.EventAdvice, aspectInfo.PointcutEvent, aspectInfo.IsPointcutEventAdder, adviceValues, adviceContext); } } // if the method is no task, then we return immediately // (and the adviceTask is completed) var adviceTask = adviceContext.Invoke(); var advisedMethodInfo = aspectInfo.AdvisedMethod as MethodInfo; var returnType = advisedMethodInfo?.ReturnType; // no Task means aspect was sync, so everything already ended // or it may also been an async void, meaning that we don't care about it if (adviceTask == null || returnType == null || !typeof(Task).GetAssignmentReader().IsAssignableFrom(returnType)) { adviceTask?.Wait(); return(adviceValues.ReturnValue); } // otherwise, see if it is a Task or Task<> // Task is simple too: the advised method is a subtask, // so the advice is completed after the method is completed too if (returnType == typeof(Task)) { return(adviceTask); } // only Task<> left here var taskType = returnType.GetTaskType(); // a reflection equivalent of ContinueWith<TNewResult>, but this TNewResult, under taskType is known only at run-time return(adviceTask.ContinueWith(t => GetResult(t, adviceValues), taskType)); }
/// <summary> /// Runs a method interception. /// We use a static method here, if one day we want to reuse Invocations or change mecanism, /// it will be easier from C# code /// </summary> /// <param name="target">The target.</param> /// <param name="parameters">The parameters.</param> /// <param name="methodBase">The raw method base.</param> /// <param name="innerMethod">The inner method.</param> /// <param name="abstractedTarget">if set to <c>true</c> [abstracted target].</param> /// <param name="genericArguments">The generic arguments (to static type and/or method) in a single array.</param> /// <returns></returns> // ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMethodReturnValue.Global public static object ProceedAdvice(object target, object[] parameters, MethodBase methodBase, MethodBase innerMethod, bool abstractedTarget, Type[] genericArguments) { var aspectInfo = GetAspectInfo(methodBase, innerMethod, abstractedTarget, genericArguments); // this is the case with auto implemented interface var advisedInterface = target as AdvisedInterface; if (advisedInterface != null) { aspectInfo = aspectInfo.AddAdvice(new AdviceInfo(advisedInterface.Advice)); } foreach (var advice in aspectInfo.Advices.Select(a => a.Advice).Distinct()) { InjectIntroducedFields(advice, methodBase.DeclaringType); } // from here, we build an advice chain, with at least one final advice: the one who calls the method var adviceValues = new AdviceValues(target, aspectInfo.AdvisedMethod.DeclaringType, parameters); // at least there is one context AdviceContext adviceContext = new InnerMethodContext(adviceValues, aspectInfo.PointcutMethod); foreach (var advice in aspectInfo.Advices.Reverse()) { // aspects are processed from highest to lowest level, so they are linked here in the opposite order // 3. as parameter if (advice.ParameterAdvice != null && advice.ParameterIndex.HasValue) { var parameterIndex = advice.ParameterIndex.Value; var parameterInfo = GetParameterInfo(aspectInfo.AdvisedMethod, parameterIndex); adviceContext = new ParameterAdviceContext(advice.ParameterAdvice, parameterInfo, parameterIndex, adviceValues, adviceContext); } // 2. as method if (advice.MethodAdvice != null) { adviceContext = new MethodAdviceContext(advice.MethodAdvice, aspectInfo.AdvisedMethod, adviceValues, adviceContext); } // 2b. as async method if (advice.AsyncMethodAdvice != null) { adviceContext = new MethodAsyncAdviceContext(advice.AsyncMethodAdvice, aspectInfo.AdvisedMethod, adviceValues, adviceContext); } // 1. as property if (advice.PropertyAdvice != null && aspectInfo.PointcutProperty != null) { adviceContext = new PropertyAdviceContext(advice.PropertyAdvice, aspectInfo.PointcutProperty, aspectInfo.IsPointcutPropertySetter, adviceValues, adviceContext); } } // if the method is no task, then we return immediately // (and the adviceTask is completed) var adviceTask = adviceContext.Invoke(); var advisedMethodInfo = aspectInfo.AdvisedMethod as MethodInfo; var returnType = advisedMethodInfo?.ReturnType; // no Task means aspect was sync, so everything already ended // TODO: this is actually not true, since an async method can be void :frown: if (adviceTask == null || returnType == null || !typeof(Task).IsAssignableFrom(returnType)) { adviceTask?.Wait(); return(adviceValues.ReturnValue); } // otherwise, see if it is a Task or Task<> // Task is simple too: the advised method is a subtask, // so the advice is completed after the method is completed too if (returnType == typeof(Task)) { return(adviceTask); } // only Task<> left here // we need to create a new source and mark it as complete once the advice has completed var adviceTaskSource = TaskCompletionSource.Create(returnType.GetTaskType()); adviceTask.ContinueWith(t => ContinueTask(t, adviceTaskSource, adviceValues)); return(adviceTaskSource.Task); }