PageRenderTime 176ms CodeModel.GetById 60ms app.highlight 47ms RepoModel.GetById 64ms app.codeStats 0ms

/src/LinFu.AOP/MethodCallInterception/InterceptMethodCalls.cs

http://github.com/philiplaureano/LinFu
C# | 408 lines | 277 code | 95 blank | 36 comment | 12 complexity | bc8d953038238662efe3cca100d0b4ab MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Diagnostics;
  4using System.Reflection;
  5using LinFu.AOP.Cecil.Interfaces;
  6using LinFu.AOP.Interfaces;
  7using LinFu.Reflection.Emit;
  8using Mono.Cecil;
  9using Mono.Cecil.Cil;
 10
 11namespace LinFu.AOP.Cecil
 12{
 13    internal class InterceptMethodCalls : InstructionSwapper, IMethodWeaver
 14    {
 15        private readonly IMethodCallFilter _callFilter;
 16        private VariableDefinition _aroundInvokeProvider;
 17
 18
 19        private MethodReference _canReplace;
 20
 21
 22        private VariableDefinition _canReplaceFlag;
 23        private VariableDefinition _currentArgument;
 24        private VariableDefinition _currentArguments;
 25        private MethodReference _getProvider;
 26        private MethodReference _getReplacement;
 27        private MethodReference _getStaticProvider;
 28        private TypeReference _hostInterfaceType;
 29        private VariableDefinition _instanceProvider;
 30        private MethodReference _intercept;
 31        private VariableDefinition _interceptionDisabled;
 32        private VariableDefinition _invocationInfo;
 33        private MethodReference _invocationInfoCtor;
 34        private VariableDefinition _methodReplacementProvider;
 35        private VariableDefinition _parameterTypes;
 36        private MethodReference _popMethod;
 37        private MethodReference _pushMethod;
 38        private VariableDefinition _replacement;
 39        private VariableDefinition _returnValue;
 40        private MethodReference _stackCtor;
 41        private VariableDefinition _staticProvider;
 42        private VariableDefinition _target;
 43        private MethodReference _toArray;
 44        private VariableDefinition _typeArguments;
 45
 46
 47        public InterceptMethodCalls(Func<MethodReference, bool> hostMethodFilter,
 48            Func<MethodReference, bool> methodCallFilter)
 49        {
 50            _callFilter = new MethodCallFilterAdapter(hostMethodFilter, methodCallFilter);
 51        }
 52
 53        public InterceptMethodCalls(IMethodCallFilter callFilter)
 54        {
 55            _callFilter = callFilter;
 56        }
 57
 58        public override void ImportReferences(ModuleDefinition module)
 59        {
 60            var types = new[]
 61            {
 62                typeof(object),
 63                typeof(MethodBase),
 64                typeof(StackTrace),
 65                typeof(Type[]),
 66                typeof(Type[]),
 67                typeof(Type),
 68                typeof(object[])
 69            };
 70
 71            _invocationInfoCtor = module.ImportConstructor<InvocationInfo>(types);
 72            _stackCtor = module.ImportConstructor<Stack<object>>();
 73
 74            _pushMethod = module.ImportMethod<Stack<object>>("Push");
 75            _popMethod = module.ImportMethod<Stack<object>>("Pop");
 76            _toArray = module.ImportMethod<Stack<object>>("ToArray");
 77            _getProvider = module.ImportMethod<IMethodReplacementHost>("get_MethodCallReplacementProvider");
 78            _getStaticProvider = module.ImportMethod("GetProvider", typeof(MethodCallReplacementProviderRegistry));
 79
 80            _canReplace = module.ImportMethod<IMethodReplacementProvider>("CanReplace");
 81            _getReplacement = module.ImportMethod<IMethodReplacementProvider>("GetMethodReplacement");
 82            _hostInterfaceType = module.ImportType<IMethodReplacementHost>();
 83            _intercept = module.ImportMethod<IInterceptor>("Intercept");
 84        }
 85
 86        public override void AddLocals(MethodDefinition hostMethod)
 87        {
 88            var body = hostMethod.Body;
 89            body.InitLocals = true;
 90
 91            _currentArguments = hostMethod.AddLocal<Stack<object>>("__arguments");
 92            _currentArgument = hostMethod.AddLocal<object>("__currentArgument");
 93            _parameterTypes = hostMethod.AddLocal<Type[]>("__parameterTypes");
 94            _typeArguments = hostMethod.AddLocal<Type[]>("__typeArguments");
 95            _invocationInfo = hostMethod.AddLocal<IInvocationInfo>("___invocationInfo");
 96
 97            _target = hostMethod.AddLocal<object>("__target");
 98            _replacement = hostMethod.AddLocal<IInterceptor>("__interceptor");
 99            _canReplaceFlag = hostMethod.AddLocal<bool>("__canReplace");
100
101            _staticProvider = hostMethod.AddLocal<IMethodReplacementProvider>("__staticProvider");
102            _instanceProvider = hostMethod.AddLocal<IMethodReplacementProvider>("__instanceProvider");
103            _interceptionDisabled = hostMethod.AddLocal<bool>();
104
105            _methodReplacementProvider = hostMethod.AddLocal<IMethodReplacementProvider>();
106            _aroundInvokeProvider = hostMethod.AddLocal<IAroundInvokeProvider>();
107            _returnValue = hostMethod.AddLocal<object>();
108        }
109
110        protected override void Replace(Instruction oldInstruction, MethodDefinition hostMethod,
111            ILProcessor IL)
112        {
113            var targetMethod = (MethodReference) oldInstruction.Operand;
114
115            var callOriginalMethod = IL.Create(OpCodes.Nop);
116            var returnType = targetMethod.ReturnType;
117            var endLabel = IL.Create(OpCodes.Nop);
118            var module = hostMethod.DeclaringType.Module;
119
120            // Create the stack that will hold the method arguments
121            IL.Emit(OpCodes.Newobj, _stackCtor);
122            IL.Emit(OpCodes.Stloc, _currentArguments);
123
124            // Make sure that the argument stack doesn't show up in
125            // any of the other interception routines
126            IgnoreLocal(IL, _currentArguments, module);
127
128            SaveInvocationInfo(IL, targetMethod, module, returnType);
129
130            var getInterceptionDisabled = new GetInterceptionDisabled(hostMethod, _interceptionDisabled);
131            getInterceptionDisabled.Emit(IL);
132
133            var surroundMethodBody = new SurroundMethodBody(_methodReplacementProvider, _aroundInvokeProvider,
134                _invocationInfo, _interceptionDisabled, _returnValue,
135                typeof(AroundInvokeMethodCallRegistry),
136                "AroundMethodCallProvider");
137
138            surroundMethodBody.AddProlog(IL);
139            // Use the MethodReplacementProvider attached to the
140            // current host instance
141            Replace(IL, oldInstruction, targetMethod, hostMethod, endLabel, callOriginalMethod);
142
143            IL.Append(endLabel);
144
145            surroundMethodBody.AddEpilog(IL);
146        }
147
148        private void IgnoreLocal(ILProcessor IL, VariableDefinition targetVariable, ModuleDefinition module)
149        {
150            IL.Emit(OpCodes.Ldloc, targetVariable);
151
152            var addInstance = module.Import(typeof(IgnoredInstancesRegistry).GetMethod("AddInstance"));
153            IL.Emit(OpCodes.Call, addInstance);
154        }
155
156        private void Replace(ILProcessor IL, Instruction oldInstruction, MethodReference targetMethod,
157            MethodDefinition hostMethod, Instruction endLabel, Instruction callOriginalMethod)
158        {
159            var returnType = targetMethod.ReturnType;
160            var module = hostMethod.DeclaringType.Module;
161            if (!hostMethod.IsStatic)
162                GetInstanceProvider(IL);
163
164
165            var pushInstance = hostMethod.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull);
166
167            // If all else fails, use the static method replacement provider
168            IL.Append(pushInstance);
169            IL.Emit(OpCodes.Ldloc, _invocationInfo);
170            IL.Emit(OpCodes.Call, _getStaticProvider);
171            IL.Emit(OpCodes.Stloc, _staticProvider);
172
173            var restoreArgumentStack = IL.Create(OpCodes.Nop);
174
175            var callReplacement = IL.Create(OpCodes.Nop);
176            var useStaticProvider = IL.Create(OpCodes.Nop);
177
178
179            IL.Emit(OpCodes.Ldloc, _instanceProvider);
180            IL.Emit(OpCodes.Brfalse, useStaticProvider);
181
182
183            EmitCanReplace(IL, hostMethod, _instanceProvider);
184            IL.Emit(OpCodes.Ldloc, _canReplaceFlag);
185            IL.Emit(OpCodes.Brfalse, useStaticProvider);
186
187            EmitGetMethodReplacement(IL, hostMethod, _instanceProvider);
188
189
190            IL.Emit(OpCodes.Ldloc, _replacement);
191            IL.Emit(OpCodes.Brtrue, callReplacement);
192
193
194            IL.Append(useStaticProvider);
195            // if (!MethodReplacementProvider.CanReplace(info))
196            //      CallOriginalMethod();
197            EmitCanReplace(IL, hostMethod, _staticProvider);
198
199            IL.Emit(OpCodes.Ldloc, _canReplaceFlag);
200            IL.Emit(OpCodes.Brfalse, restoreArgumentStack);
201
202            EmitGetMethodReplacement(IL, hostMethod, _staticProvider);
203
204            IL.Append(callReplacement);
205
206            // if (replacement == null)
207            //      CallOriginalMethod();
208            IL.Emit(OpCodes.Ldloc, _replacement);
209            IL.Emit(OpCodes.Brfalse, restoreArgumentStack);
210
211            EmitInterceptorCall(IL);
212
213            IL.PackageReturnValue(module, returnType);
214
215            IL.Emit(OpCodes.Br, endLabel);
216
217            IL.Append(restoreArgumentStack);
218
219            // Reconstruct the method arguments if the interceptor
220            // cannot be found
221
222            // Push the target instance
223            ReconstructMethodArguments(IL, targetMethod);
224
225            // Mark the CallOriginalMethod instruction label
226            IL.Append(callOriginalMethod);
227
228            // Call the original method
229            IL.Append(oldInstruction);
230        }
231
232        private void GetInstanceProvider(ILProcessor IL)
233        {
234            var skipInstanceProvider = IL.Create(OpCodes.Nop);
235
236            IL.Emit(OpCodes.Ldarg_0);
237            IL.Emit(OpCodes.Isinst, _hostInterfaceType);
238            IL.Emit(OpCodes.Brfalse, skipInstanceProvider);
239            IL.Emit(OpCodes.Ldarg_0);
240            IL.Emit(OpCodes.Isinst, _hostInterfaceType);
241            IL.Emit(OpCodes.Callvirt, _getProvider);
242            IL.Emit(OpCodes.Stloc, _instanceProvider);
243
244            IL.Emit(OpCodes.Ldloc, _instanceProvider);
245            IL.Emit(OpCodes.Brtrue, skipInstanceProvider);
246
247            IL.Append(skipInstanceProvider);
248        }
249
250        private void ReconstructMethodArguments(ILProcessor IL, MethodReference targetMethod)
251        {
252            if (targetMethod.HasThis)
253                IL.Emit(OpCodes.Ldloc, _target);
254
255            // Push the arguments back onto the stack
256            foreach (ParameterReference param in targetMethod.Parameters)
257            {
258                IL.Emit(OpCodes.Ldloc, _currentArguments);
259                IL.Emit(OpCodes.Callvirt, _popMethod);
260                IL.Emit(OpCodes.Unbox_Any, param.ParameterType);
261            }
262        }
263
264        private void SaveInvocationInfo(ILProcessor IL, MethodReference targetMethod, ModuleDefinition module,
265            TypeReference returnType)
266        {
267            // If the target method is an instance method, then the remaining item on the stack
268            // will be the target object instance
269
270
271            // Put all the method arguments into the argument stack
272            foreach (ParameterReference param in targetMethod.Parameters)
273            {
274                // Save the current argument
275                var parameterType = param.ParameterType;
276                if (parameterType.IsValueType || parameterType is GenericParameter)
277                    IL.Emit(OpCodes.Box, parameterType);
278
279                IL.Emit(OpCodes.Stloc, _currentArgument);
280                IL.Emit(OpCodes.Ldloc, _currentArguments);
281                IL.Emit(OpCodes.Ldloc, _currentArgument);
282                IL.Emit(OpCodes.Callvirt, _pushMethod);
283            }
284
285
286            // Static methods will always have a null reference as the target
287            if (!targetMethod.HasThis)
288                IL.Emit(OpCodes.Ldnull);
289
290            // Box the target, if necessary
291            var declaringType = targetMethod.GetDeclaringType();
292            if (targetMethod.HasThis && (declaringType.IsValueType || declaringType is GenericParameter))
293                IL.Emit(OpCodes.Box, declaringType);
294
295            // Save the target
296            IL.Emit(OpCodes.Stloc, _target);
297            IL.Emit(OpCodes.Ldloc, _target);
298
299            // Push the current method
300            IL.PushMethod(targetMethod, module);
301
302            // Push the stack trace
303            PushStackTrace(IL, module);
304
305            var systemType = module.Import(typeof(Type));
306
307            // Save the parameter types
308            var parameterCount = targetMethod.Parameters.Count;
309            IL.Emit(OpCodes.Ldc_I4, parameterCount);
310            IL.Emit(OpCodes.Newarr, systemType);
311            IL.Emit(OpCodes.Stloc, _parameterTypes);
312
313            IL.SaveParameterTypes(targetMethod, module, _parameterTypes);
314            IL.Emit(OpCodes.Ldloc, _parameterTypes);
315
316            // Save the type arguments
317            var genericParameterCount = targetMethod.GenericParameters.Count;
318            IL.Emit(OpCodes.Ldc_I4, genericParameterCount);
319            IL.Emit(OpCodes.Newarr, systemType);
320            IL.Emit(OpCodes.Stloc, _typeArguments);
321            IL.PushGenericArguments(targetMethod, module, _typeArguments);
322            IL.Emit(OpCodes.Ldloc, _typeArguments);
323
324            // Push the return type
325            IL.PushType(returnType, module);
326
327            // Save the method arguments
328            IL.Emit(OpCodes.Ldloc, _currentArguments);
329            IL.Emit(OpCodes.Callvirt, _toArray);
330
331            IL.Emit(OpCodes.Newobj, _invocationInfoCtor);
332            IL.Emit(OpCodes.Stloc, _invocationInfo);
333
334            IgnoreLocal(IL, _invocationInfo, module);
335        }
336
337        private void PushStackTrace(ILProcessor IL, ModuleDefinition module)
338        {
339            IL.PushStackTrace(module);
340        }
341
342        private void EmitInterceptorCall(ILProcessor IL)
343        {
344            // var result = replacement.Intercept(info);
345            IL.Emit(OpCodes.Ldloc, _replacement);
346            IL.Emit(OpCodes.Ldloc, _invocationInfo);
347            IL.Emit(OpCodes.Callvirt, _intercept);
348        }
349
350        private void EmitCanReplace(ILProcessor IL, IMethodSignature hostMethod, VariableDefinition provider)
351        {
352            var skipGetProvider = IL.Create(OpCodes.Nop);
353
354            IL.Emit(OpCodes.Ldloc, provider);
355            IL.Emit(OpCodes.Brfalse, skipGetProvider);
356
357            IL.Emit(OpCodes.Ldloc, provider);
358
359            // Push the host instance
360            var pushInstance = hostMethod.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull);
361            IL.Append(pushInstance);
362            IL.Emit(OpCodes.Ldloc, _invocationInfo);
363            IL.Emit(OpCodes.Callvirt, _canReplace);
364
365            IL.Emit(OpCodes.Stloc, _canReplaceFlag);
366            IL.Append(skipGetProvider);
367        }
368
369        private void EmitGetMethodReplacement(ILProcessor IL, IMethodSignature hostMethod, VariableDefinition provider)
370        {
371            // var replacement = MethodReplacementProvider.GetReplacement(info);
372            IL.Emit(OpCodes.Ldloc, provider);
373
374            // Push the host instance
375            var pushInstance = hostMethod.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull);
376            IL.Append(pushInstance);
377            IL.Emit(OpCodes.Ldloc, _invocationInfo);
378            IL.Emit(OpCodes.Callvirt, _getReplacement);
379            IL.Emit(OpCodes.Stloc, _replacement);
380        }
381
382        protected override bool ShouldReplace(Instruction oldInstruction, MethodDefinition hostMethod)
383        {
384            // Intercept the call and callvirt instructions
385            var opCode = oldInstruction.OpCode;
386            if (opCode != OpCodes.Callvirt && opCode != OpCodes.Call)
387                return false;
388
389            var targetMethod = (MethodReference) oldInstruction.Operand;
390            var declaringType = targetMethod.DeclaringType;
391
392
393            //return _hostMethodFilter(hostMethod) && _methodCallFilter(targetMethod);
394            return _callFilter.ShouldWeave(hostMethod.DeclaringType, hostMethod, targetMethod);
395        }
396
397        public bool ShouldWeave(MethodDefinition item)
398        {
399            // Modify everything by default
400            return item.HasBody;
401        }
402
403        public void Weave(MethodDefinition item)
404        {
405            Rewrite(item,item.GetILGenerator(),item.Body.Instructions.ToArray());
406        }
407    }
408}