PageRenderTime 83ms CodeModel.GetById 40ms app.highlight 7ms RepoModel.GetById 33ms app.codeStats 0ms

/src/LinFu.AOP/MethodBodyInterception/InterceptMethodBody.cs

http://github.com/philiplaureano/LinFu
C# | 145 lines | 95 code | 19 blank | 31 comment | 1 complexity | 0c85bc297c57f20a353a4acfc859dded MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using LinFu.AOP.Cecil.Interfaces;
  5using LinFu.AOP.Interfaces;
  6using LinFu.Reflection.Emit;
  7using Mono.Cecil;
  8using Mono.Cecil.Cil;
  9
 10namespace LinFu.AOP.Cecil
 11{
 12    /// <summary>
 13    ///     Represents a method rewriter type that adds interception capabilities to any given method body.
 14    /// </summary>
 15    public class InterceptMethodBody : BaseMethodRewriter, IMethodWeaver
 16    {
 17        private readonly Func<MethodReference, bool> _methodFilter;
 18
 19        /// <summary>
 20        ///     Initializes a new instance of the <see cref="InterceptMethodBody" /> class.
 21        /// </summary>
 22        /// <param name="methodFilter">
 23        ///     The method filter that will determine the methods with the method bodies that will be
 24        ///     intercepted.
 25        /// </param>
 26        public InterceptMethodBody(Func<MethodReference, bool> methodFilter)
 27        {
 28            _methodFilter = methodFilter;
 29        }
 30
 31        /// <summary>
 32        ///     Determines whether or not the given method should be modified.
 33        /// </summary>
 34        /// <param name="targetMethod">The target method.</param>
 35        /// <returns>A <see cref="bool" /> indicating whether or not a method should be rewritten.</returns>
 36        protected override bool ShouldRewrite(MethodDefinition targetMethod)
 37        {
 38            return _methodFilter(targetMethod);
 39        }
 40
 41        /// <summary>
 42        ///     Rewrites the instructions in the target method body.
 43        /// </summary>
 44        /// <param name="method">The target method.</param>
 45        /// <param name="IL">The <see cref="ILProcessor" /> instance that represents the method body.</param>
 46        /// <param name="oldInstructions">The IL instructions of the original method body.</param>
 47        protected override void RewriteMethodBody(MethodDefinition method, ILProcessor IL,
 48            IEnumerable<Instruction> oldInstructions)
 49        {
 50            if (IsExcluded(method))
 51            {
 52                AddOriginalInstructions(IL, oldInstructions);
 53                return;
 54            }
 55
 56            var interceptionDisabled = method.AddLocal<bool>();
 57            var invocationInfo = method.AddLocal<IInvocationInfo>();
 58            var aroundInvokeProvider = method.AddLocal<IAroundInvokeProvider>();
 59            var methodReplacementProvider = method.AddLocal<IMethodReplacementProvider>();
 60
 61
 62            var returnValue = method.AddLocal<object>();
 63            var classMethodReplacementProvider = method.AddLocal<IMethodReplacementProvider>();
 64
 65            Func<ModuleDefinition, MethodReference> getInstanceMethodReplacementProviderMethod =
 66                module => module.Import(typeof(IMethodReplacementHost).GetMethod("get_MethodBodyReplacementProvider"));
 67
 68            var parameters = new MethodBodyRewriterParameters(IL,
 69                oldInstructions,
 70                interceptionDisabled,
 71                invocationInfo, returnValue,
 72                methodReplacementProvider,
 73                aroundInvokeProvider,
 74                classMethodReplacementProvider,
 75                getInstanceMethodReplacementProviderMethod,
 76                typeof(AroundMethodBodyRegistry));
 77
 78            var emitter = new InvocationInfoEmitter(true);
 79
 80            IInstructionEmitter getMethodReplacementProvider =
 81                new GetMethodReplacementProvider(methodReplacementProvider, method,
 82                    getInstanceMethodReplacementProviderMethod);
 83
 84            IInstructionEmitter getInterceptionDisabled = new GetInterceptionDisabled(parameters);
 85            ISurroundMethodBody surroundMethodBody = new SurroundMethodBody(parameters, "AroundMethodBodyProvider");
 86            IInstructionEmitter getClassMethodReplacementProvider = new GetClassMethodReplacementProvider(parameters,
 87                module =>
 88                    module.Import(
 89                        typeof(
 90                            MethodBodyReplacementProviderRegistry
 91                        ).GetMethod
 92                            ("GetProvider")));
 93            IInstructionEmitter addMethodReplacement = new AddMethodReplacementImplementation(parameters);
 94
 95            var rewriter = new InterceptAndSurroundMethodBody(emitter, getInterceptionDisabled, surroundMethodBody,
 96                getMethodReplacementProvider,
 97                getClassMethodReplacementProvider, addMethodReplacement,
 98                parameters);
 99
100            // Determine whether or not the method should be intercepted
101            rewriter.Rewrite(method, IL, oldInstructions);
102        }
103
104        private void AddOriginalInstructions(ILProcessor IL, IEnumerable<Instruction> oldInstructions)
105        {
106            foreach (var instruction in oldInstructions) IL.Append(instruction);
107        }
108
109        private bool IsExcluded(MethodDefinition method)
110        {
111            var excludedTypes = new[]
112            {
113                typeof(IMethodReplacementHost),
114                typeof(IModifiableType), typeof(IActivatorHost),
115                typeof(IFieldInterceptionHost), typeof(IAroundInvokeHost)
116            };
117            var excludedMethods = (from type in excludedTypes
118                from currentMethod in type.GetMethods()
119                select currentMethod.Name).ToList();
120
121            var methodName = method.Name;
122            return excludedMethods.Contains(methodName);
123        }
124
125        /// <summary>
126        ///     Determines whether or not the current item should be modified.
127        /// </summary>
128        /// <param name="item">The target item.</param>
129        /// <returns>Returns <c>true</c> if the current item can be modified; otherwise, it should return <c>false.</c></returns>
130        public bool ShouldWeave(MethodDefinition item)
131        {
132            return ShouldRewrite(item);
133        }
134
135        /// <summary>
136        ///     Modifies the target <paramref name="item" />.
137        /// </summary>
138        /// <param name="item">The item to be modified.</param>
139        public void Weave(MethodDefinition item)
140        {
141            var oldInstructions = item.Body.Instructions.ToArray();
142            Rewrite(item, item.GetILGenerator(), oldInstructions);
143        }
144    }
145}