PageRenderTime 104ms CodeModel.GetById 60ms app.highlight 14ms RepoModel.GetById 27ms app.codeStats 0ms

/src/LinFu.AOP/FieldInterception/InterceptFieldAccess.cs

http://github.com/philiplaureano/LinFu
C# | 244 lines | 142 code | 44 blank | 58 comment | 17 complexity | 50a78d92d8e51cb9d5b5839b34a6f012 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Reflection;
  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 <see cref="MethodRewriter" /> that intercepts calls to field getters and setters and redirects those
 14    ///     calls to
 15    ///     a <see cref="IFieldInterceptor" /> instance.
 16    /// </summary>
 17    internal class InterceptFieldAccess : InstructionSwapper
 18    {
 19        private static readonly HashSet<OpCode> _fieldInstructions = new HashSet<OpCode>();
 20        private readonly IFieldFilter _filter;
 21        private MethodReference _canIntercept;
 22        private VariableDefinition _currentArgument;
 23        private VariableDefinition _fieldContext;
 24
 25        private MethodReference _fieldContextCtor;
 26        private TypeReference _fieldInterceptionHostType;
 27        private VariableDefinition _fieldInterceptor;
 28        private MethodReference _getInstanceInterceptor;
 29        private MethodReference _getInterceptor;
 30        private MethodReference _getValue;
 31        private MethodReference _setValue;
 32
 33        static InterceptFieldAccess()
 34        {
 35            _fieldInstructions.Add(OpCodes.Ldsfld);
 36            _fieldInstructions.Add(OpCodes.Ldfld);
 37            _fieldInstructions.Add(OpCodes.Stsfld);
 38            _fieldInstructions.Add(OpCodes.Stfld);
 39        }
 40
 41        /// <summary>
 42        ///     Initializes a new instance of the InterceptFieldAccess class.
 43        /// </summary>
 44        /// <param name="filter">The filter that determines which fields should be intercepted.</param>
 45        public InterceptFieldAccess(Func<FieldReference, bool> filter)
 46        {
 47            _filter = new FieldFilterAdapter(filter);
 48        }
 49
 50        /// <summary>
 51        ///     Initializes a new instance of the InterceptFieldAccess class.
 52        /// </summary>
 53        /// <param name="filter">The filter that determines which fields should be intercepted.</param>
 54        public InterceptFieldAccess(IFieldFilter filter)
 55        {
 56            _filter = filter;
 57        }
 58
 59        /// <summary>
 60        ///     Adds locals to the target method.
 61        /// </summary>
 62        /// <param name="hostMethod">The method to be modified</param>
 63        public override void AddLocals(MethodDefinition hostMethod)
 64        {
 65            _fieldContext = hostMethod.AddLocal<IFieldInterceptionContext>("__<>FieldInterceptionContext<>__");
 66            _fieldInterceptor = hostMethod.AddLocal<IFieldInterceptor>("__<>FieldInterceptor<>__");
 67            _currentArgument = hostMethod.AddLocal<object>("__<>CurrentArgument<>__");
 68        }
 69
 70        /// <summary>
 71        ///     Adds references to the target module.
 72        /// </summary>
 73        /// <param name="module">The module that will be modified.</param>
 74        public override void ImportReferences(ModuleDefinition module)
 75        {
 76            var parameterTypes = new[] {typeof(object), typeof(MethodBase), typeof(FieldInfo), typeof(Type)};
 77
 78            _fieldInterceptionHostType = module.ImportType<IFieldInterceptionHost>();
 79
 80            _fieldContextCtor = module.ImportConstructor<FieldInterceptionContext>(parameterTypes);
 81            module.ImportMethod<FieldInfo>("GetFieldFromHandle", typeof(RuntimeFieldHandle), typeof(RuntimeTypeHandle));
 82            module.ImportMethod<object>("GetType");
 83            _getInterceptor = module.ImportMethod<FieldInterceptorRegistry>("GetInterceptor");
 84            _getInstanceInterceptor = module.ImportMethod<IFieldInterceptionHost>("get_FieldInterceptor");
 85            _canIntercept = module.ImportMethod<IFieldInterceptor>("CanIntercept");
 86
 87            _getValue = module.ImportMethod<IFieldInterceptor>("GetValue");
 88            _setValue = module.ImportMethod<IFieldInterceptor>("SetValue");
 89        }
 90
 91        /// <summary>
 92        ///     Determines whether or not the method rewriter should replace the <paramref name="oldInstruction" />.
 93        /// </summary>
 94        /// <remarks>
 95        ///     The <see cref="InterceptFieldAccess" /> class only modifies instructions that get or set the value of static
 96        ///     and instance fields.
 97        /// </remarks>
 98        /// <param name="oldInstruction">The instruction that is currently being evaluated.</param>
 99        /// <param name="hostMethod">The method that hosts the current instruction.</param>
100        /// <returns><c>true</c> if the method should be replaced; otherwise, it should return <c>false</c>.</returns>
101        protected override bool ShouldReplace(Instruction oldInstruction, MethodDefinition hostMethod)
102        {
103            if (!_fieldInstructions.Contains(oldInstruction.OpCode))
104                return false;
105
106            // Match the field filter
107            var targetField = (FieldReference) oldInstruction.Operand;
108
109            return _filter.ShouldWeave(hostMethod, targetField);
110        }
111
112        /// <summary>
113        ///     Replaces the <paramref name="oldInstruction" /> with a set of new instructions.
114        /// </summary>
115        /// <param name="oldInstruction">The instruction currently being evaluated.</param>
116        /// <param name="hostMethod">The method that contains the target instruction.</param>
117        /// <param name="IL">The ILProcessor that will be used to emit the method body instructions.</param>
118        protected override void Replace(Instruction oldInstruction, MethodDefinition hostMethod, ILProcessor IL)
119        {
120            var targetField = (FieldReference) oldInstruction.Operand;
121            var fieldType = targetField.FieldType;
122            var isSetter = oldInstruction.OpCode == OpCodes.Stsfld || oldInstruction.OpCode == OpCodes.Stfld;
123
124            if (isSetter)
125            {
126                hostMethod.Body.InitLocals = true;
127                // Save the setter argument and box it if necessary
128                if (fieldType.IsValueType || fieldType is GenericParameter)
129                    IL.Emit(OpCodes.Box, fieldType);
130
131                IL.Emit(OpCodes.Stloc, _currentArgument);
132            }
133
134            // There's no need to push the current object instance
135            // since the this pointer is pushed prior to the field call
136            if (hostMethod.IsStatic)
137                IL.Emit(OpCodes.Ldnull);
138
139            // Push the current method
140            var module = hostMethod.DeclaringType.Module;
141
142            // Push the current method onto the stack
143            IL.PushMethod(hostMethod, module);
144
145            // Push the current field onto the stack            
146            IL.PushField(targetField, module);
147
148            // Push the host type onto the stack
149            IL.PushType(hostMethod.DeclaringType, module);
150
151            // Create the IFieldInterceptionContext instance
152            IL.Emit(OpCodes.Newobj, _fieldContextCtor);
153            IL.Emit(OpCodes.Stloc, _fieldContext);
154
155            var skipInterception = IL.Create(OpCodes.Nop);
156            // Obtain an interceptor instance
157            if (hostMethod.IsStatic)
158            {
159                IL.Emit(OpCodes.Ldloc, _fieldContext);
160                IL.Emit(OpCodes.Call, _getInterceptor);
161            }
162            else
163            {
164                IL.Emit(OpCodes.Ldarg_0);
165                IL.Emit(OpCodes.Isinst, _fieldInterceptionHostType);
166                IL.Emit(OpCodes.Brfalse, skipInterception);
167
168                IL.Emit(OpCodes.Ldarg_0);
169                IL.Emit(OpCodes.Isinst, _fieldInterceptionHostType);
170                IL.Emit(OpCodes.Callvirt, _getInstanceInterceptor);
171            }
172
173            // The field interceptor cannot be null
174            IL.Emit(OpCodes.Stloc, _fieldInterceptor);
175            IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
176            IL.Emit(OpCodes.Brfalse, skipInterception);
177
178            // if (FieldInterceptor.CanIntercept(context) {
179            IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
180            IL.Emit(OpCodes.Ldloc, _fieldContext);
181            IL.Emit(OpCodes.Callvirt, _canIntercept);
182            IL.Emit(OpCodes.Brfalse, skipInterception);
183
184            var isGetter = oldInstruction.OpCode == OpCodes.Ldsfld || oldInstruction.OpCode == OpCodes.Ldfld;
185
186            var endLabel = IL.Create(OpCodes.Nop);
187
188            //Call the interceptor instead of the getter or setter
189            if (isGetter)
190            {
191                IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
192                IL.Emit(OpCodes.Ldloc, _fieldContext);
193                IL.Emit(OpCodes.Callvirt, _getValue);
194                IL.Emit(OpCodes.Unbox_Any, fieldType);
195            }
196
197            if (isSetter)
198            {
199                // Push the 'this' pointer for instance field setters
200                if (!hostMethod.IsStatic)
201                    IL.Emit(OpCodes.Ldarg_0);
202
203                IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
204                IL.Emit(OpCodes.Ldloc, _fieldContext);
205                IL.Emit(OpCodes.Ldloc, _currentArgument);
206
207                // Unbox the setter value
208                IL.Emit(OpCodes.Unbox_Any, fieldType);
209
210                IL.Emit(OpCodes.Callvirt, _setValue);
211
212                // Set the actual field value
213                IL.Emit(OpCodes.Unbox_Any, fieldType);
214                IL.Emit(oldInstruction.OpCode, targetField);
215            }
216
217            IL.Emit(OpCodes.Br, endLabel);
218
219            // }
220            IL.Append(skipInterception);
221
222            // else {
223
224            // Load the original field
225            if (!hostMethod.IsStatic)
226                IL.Emit(OpCodes.Ldarg_0);
227
228
229            if (isSetter)
230            {
231                IL.Emit(OpCodes.Ldloc, _currentArgument);
232
233                // Unbox the setter value
234                IL.Emit(OpCodes.Unbox_Any, fieldType);
235            }
236
237            IL.Emit(oldInstruction.OpCode, targetField);
238
239            // }
240
241            IL.Append(endLabel);
242        }
243    }
244}