PageRenderTime 19ms CodeModel.GetById 5ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/src/LinFu.AOP/InvocationInfoEmitter.cs

http://github.com/philiplaureano/LinFu
C# | 171 lines | 103 code | 31 blank | 37 comment | 4 complexity | 6e901a10e6dd37444f44f713ef0e1b75 MD5 | raw file
  1using System;
  2using System.Diagnostics;
  3using System.Reflection;
  4using LinFu.AOP.Cecil.Interfaces;
  5using LinFu.AOP.Interfaces;
  6using LinFu.IoC.Configuration;
  7using LinFu.Reflection.Emit;
  8using Mono.Cecil;
  9using Mono.Cecil.Cil;
 10
 11namespace LinFu.AOP.Cecil
 12{
 13    /// <summary>
 14    ///     Represents the default implementation for the
 15    ///     <see cref="IEmitInvocationInfo" /> class.
 16    /// </summary>
 17    [Implements(typeof(IEmitInvocationInfo), LifecycleType.OncePerRequest)]
 18    public class InvocationInfoEmitter : IEmitInvocationInfo
 19    {
 20        private static readonly ConstructorInfo _invocationInfoConstructor;
 21        private static readonly MethodInfo _getTypeFromHandle;
 22        private readonly bool _pushStackTrace;
 23
 24        static InvocationInfoEmitter()
 25        {
 26            var types = new[]
 27            {
 28                typeof(object),
 29                typeof(MethodBase),
 30                typeof(StackTrace),
 31                typeof(Type[]),
 32                typeof(Type[]),
 33                typeof(Type),
 34                typeof(object[])
 35            };
 36
 37            _invocationInfoConstructor = typeof(InvocationInfo).GetConstructor(types);
 38
 39            _getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle",
 40                BindingFlags.Static | BindingFlags.Public);
 41        }
 42
 43        /// <summary>
 44        ///     Initializes a new instance of the InvocationInfoEmitter class.
 45        /// </summary>
 46        public InvocationInfoEmitter()
 47            : this(false)
 48        {
 49        }
 50
 51        /// <summary>
 52        ///     Initializes a new instance of the InvocationInfoEmitter class.
 53        /// </summary>
 54        /// <param name="pushStackTrace">Determines whether or not stack trace information will be available at runtime.</param>
 55        public InvocationInfoEmitter(bool pushStackTrace)
 56        {
 57            _pushStackTrace = pushStackTrace;
 58        }
 59
 60
 61        /// <summary>
 62        ///     Emits the IL to save information about
 63        ///     the method currently being executed.
 64        /// </summary>
 65        /// <seealso cref="IInvocationInfo" />
 66        /// <param name="targetMethod">The target method currently being executed.</param>
 67        /// <param name="interceptedMethod">
 68        ///     The method that will be passed to the <paramref name="invocationInfo" /> as the
 69        ///     currently executing method.
 70        /// </param>
 71        /// <param name="invocationInfo">The local variable that will store the resulting <see cref="IInvocationInfo" /> instance.</param>
 72        public void Emit(MethodDefinition targetMethod, MethodReference interceptedMethod,
 73            VariableDefinition invocationInfo)
 74        {
 75            var module = targetMethod.DeclaringType.Module;
 76            var currentMethod = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(MethodBase));
 77            var parameterTypes = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(Type[]));
 78            var arguments = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(object[]));
 79            var typeArguments = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(Type[]));
 80            var systemType = module.ImportType(typeof(Type));
 81
 82            var IL = targetMethod.Body.GetILProcessor();
 83
 84
 85            // Type[] typeArguments = new Type[genericTypeCount];
 86            var genericParameterCount = targetMethod.GenericParameters.Count;
 87            IL.Emit(OpCodes.Ldc_I4, genericParameterCount);
 88            IL.Emit(OpCodes.Newarr, systemType);
 89            IL.Emit(OpCodes.Stloc, typeArguments);
 90
 91            // object[] arguments = new object[argumentCount];            
 92            IL.PushArguments(targetMethod, module, arguments);
 93
 94            // object target = this;
 95            if (targetMethod.HasThis)
 96                IL.Emit(OpCodes.Ldarg_0);
 97            else
 98                IL.Emit(OpCodes.Ldnull);
 99
100            IL.PushMethod(interceptedMethod, module);
101
102            IL.Emit(OpCodes.Stloc, currentMethod);
103
104            // MethodBase targetMethod = currentMethod as MethodBase;            
105            IL.Emit(OpCodes.Ldloc, currentMethod);
106
107            // Push the generic type arguments onto the stack
108            if (genericParameterCount > 0)
109                IL.PushGenericArguments(targetMethod, module, typeArguments);
110
111            // Make sure that the generic methodinfo is instantiated with the
112            // proper type arguments
113            if (targetMethod.GenericParameters.Count > 0)
114            {
115                var methodInfoType = module.Import(typeof(MethodInfo));
116                IL.Emit(OpCodes.Isinst, methodInfoType);
117
118                var getIsGenericMethodDef = module.ImportMethod<MethodInfo>("get_IsGenericMethodDefinition");
119                IL.Emit(OpCodes.Dup);
120                IL.Emit(OpCodes.Callvirt, getIsGenericMethodDef);
121
122                // Determine if the current method is a generic method
123                // definition
124                var skipMakeGenericMethod = IL.Create(OpCodes.Nop);
125                IL.Emit(OpCodes.Brfalse, skipMakeGenericMethod);
126
127                // Instantiate the specific generic method instance
128                var makeGenericMethod = module.ImportMethod<MethodInfo>("MakeGenericMethod", typeof(Type[]));
129                IL.Emit(OpCodes.Ldloc, typeArguments);
130                IL.Emit(OpCodes.Callvirt, makeGenericMethod);
131                IL.Append(skipMakeGenericMethod);
132            }
133
134            if (_pushStackTrace)
135                IL.PushStackTrace(module);
136            else
137                IL.Emit(OpCodes.Ldnull);
138
139            // Save the parameter types
140            IL.Emit(OpCodes.Ldc_I4, targetMethod.Parameters.Count);
141            IL.Emit(OpCodes.Newarr, systemType);
142            IL.Emit(OpCodes.Stloc, parameterTypes);
143
144            IL.SaveParameterTypes(targetMethod, module, parameterTypes);
145            IL.Emit(OpCodes.Ldloc, parameterTypes);
146
147            // Push the type arguments back onto the stack
148            IL.Emit(OpCodes.Ldloc, typeArguments);
149
150            // Save the return type
151            var getTypeFromHandle = module.Import(_getTypeFromHandle);
152
153            var returnType = targetMethod.ReturnType;
154            IL.Emit(OpCodes.Ldtoken, returnType);
155            IL.Emit(OpCodes.Call, getTypeFromHandle);
156
157            // Push the arguments back onto the stack
158            IL.Emit(OpCodes.Ldloc, arguments);
159
160
161            // InvocationInfo info = new InvocationInfo(...);
162            var infoConstructor = module.Import(_invocationInfoConstructor);
163            IL.Emit(OpCodes.Newobj, infoConstructor);
164            IL.Emit(OpCodes.Stloc, invocationInfo);
165            IL.Emit(OpCodes.Ldloc, invocationInfo);
166
167            var addInstance = module.Import(typeof(IgnoredInstancesRegistry).GetMethod("AddInstance"));
168            IL.Emit(OpCodes.Call, addInstance);
169        }
170    }
171}