PageRenderTime 25ms CodeModel.GetById 12ms app.highlight 9ms RepoModel.GetById 2ms app.codeStats 0ms

/src/LinFu.Proxy/MethodBodyEmitter.cs

http://github.com/philiplaureano/LinFu
C# | 206 lines | 116 code | 33 blank | 57 comment | 5 complexity | 3330308c3c65f72aa716946071853a01 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using System.Reflection;
  5using LinFu.AOP.Cecil;
  6using LinFu.AOP.Cecil.Extensions;
  7using LinFu.AOP.Cecil.Interfaces;
  8using LinFu.AOP.Interfaces;
  9using LinFu.IoC.Configuration;
 10using LinFu.IoC.Interfaces;
 11using LinFu.Proxy.Interfaces;
 12using LinFu.Reflection.Emit;
 13using Mono.Cecil;
 14using Mono.Cecil.Cil;
 15
 16namespace LinFu.Proxy
 17{
 18    /// <summary>
 19    ///     Provides the default implementation for the
 20    ///     <see cref="IMethodBodyEmitter" /> interface.
 21    /// </summary>
 22    [Implements(typeof(IMethodBodyEmitter), LifecycleType.OncePerRequest)]
 23    internal class MethodBodyEmitter : IMethodBodyEmitter, IInitialize
 24    {
 25        /// <summary>
 26        ///     Initializes the class with the default values.
 27        /// </summary>
 28        public MethodBodyEmitter()
 29        {
 30            InvocationInfoEmitter = new InvocationInfoEmitter();
 31        }
 32
 33        /// <summary>
 34        ///     The <see cref="IEmitInvocationInfo" /> instance that
 35        /// </summary>
 36        public IEmitInvocationInfo InvocationInfoEmitter { get; set; }
 37
 38
 39        /// <summary>
 40        ///     Initializes the MethodBodyEmitter class.
 41        /// </summary>
 42        /// <param name="source"></param>
 43        public void Initialize(IServiceContainer source)
 44        {
 45            InvocationInfoEmitter = (IEmitInvocationInfo) source.GetService(typeof(IEmitInvocationInfo));
 46        }
 47
 48
 49        /// <summary>
 50        ///     Generates a method body for the <paramref name="targetMethod" />.
 51        /// </summary>
 52        /// <param name="originalMethod">The method currently being intercepted.</param>
 53        /// <param name="targetMethod">The target method that will contain the new method body.</param>
 54        public void Emit(MethodInfo originalMethod, MethodDefinition targetMethod)
 55        {
 56            var invocationInfo = targetMethod.AddLocal<IInvocationInfo>();
 57            invocationInfo.Name = "___invocationInfo___";
 58
 59            // Emit the code to generate the IInvocationInfo instance
 60            // and save it into the invocationInfo local variable
 61            if (InvocationInfoEmitter != null)
 62                InvocationInfoEmitter.Emit(originalMethod, targetMethod, invocationInfo);
 63
 64            var declaringType = targetMethod.DeclaringType;
 65            var module = declaringType.Module;
 66            var proxyType = module.ImportType<IProxy>();
 67            var getInterceptorMethod = module.ImportMethod("get_Interceptor", typeof(IProxy));
 68            var interceptor = targetMethod.AddLocal<IInterceptor>();
 69            var arguments = targetMethod.AddLocal<object[]>();
 70
 71            // if (!(this is IProxy))
 72            var IL = targetMethod.GetILGenerator();
 73            IL.Emit(OpCodes.Ldarg_0);
 74            IL.Emit(OpCodes.Isinst, proxyType);
 75
 76            var noImplementationFound = IL.Create(OpCodes.Nop);
 77            IL.Emit(OpCodes.Brfalse, noImplementationFound);
 78
 79            var endLabel = IL.Create(OpCodes.Nop);
 80            EmitGetInterceptorInstruction(IL, proxyType, getInterceptorMethod);
 81            IL.Emit(OpCodes.Stloc, interceptor);
 82
 83            //If (interceptor == null)
 84            //    throw a not implemented exception here
 85            IL.Emit(OpCodes.Ldloc, interceptor);
 86            IL.Emit(OpCodes.Brfalse, noImplementationFound);
 87
 88
 89            // var returnValue = interceptor.Intercept(info);
 90            var voidType = module.ImportType(typeof(void));
 91            var interceptMethod = module.ImportMethod<IInterceptor>("Intercept", typeof(IInvocationInfo));
 92            IL.Emit(OpCodes.Ldloc, interceptor);
 93            IL.Emit(OpCodes.Ldloc, invocationInfo);
 94            IL.Emit(OpCodes.Callvirt, interceptMethod);
 95
 96            // Save the ref arguments
 97            var parameters = from ParameterDefinition param in targetMethod.Parameters
 98                select param;
 99
100            // Determine the return type
101            var returnType = targetMethod.ReturnType != null
102                ? targetMethod.ReturnType
103                : voidType;
104
105            IL.PackageReturnValue(module, returnType);
106
107            SaveRefArguments(IL, parameters, invocationInfo, arguments);
108            IL.Emit(OpCodes.Br, endLabel);
109
110            // This code at this point will execute if no implementation
111            // is found
112            IL.Append(noImplementationFound);
113
114            ImplementNotFound(IL);
115
116            IL.Append(endLabel);
117            IL.Emit(OpCodes.Ret);
118        }
119
120
121        /// <summary>
122        ///     Emits the IL instructions to obtain an <see cref="IInterceptor" /> instance for the proxy type.
123        /// </summary>
124        /// <param name="IL">The <see cref="ILProcessor" /> responsible for emitting the method body.</param>
125        /// <param name="proxyType">The proxy type.</param>
126        /// <param name="getInterceptorMethod">The getter method for the interceptor.</param>
127        protected virtual void EmitGetInterceptorInstruction(ILProcessor IL, TypeReference proxyType,
128            MethodReference getInterceptorMethod)
129        {
130            IL.Emit(OpCodes.Ldarg_0);
131            IL.Emit(OpCodes.Isinst, proxyType);
132            IL.Emit(OpCodes.Callvirt, getInterceptorMethod);
133        }
134
135        /// <summary>
136        ///     Causes the <see cref="ILProcessor" /> to make the method throw a
137        ///     <see cref="NotImplementedException" /> if the method cannot be found.
138        /// </summary>
139        /// <param name="IL">The <see cref="ILProcessor" /> responsible for emitting the method body.</param>
140        protected virtual void ImplementNotFound(ILProcessor IL)
141        {
142            var body = IL.Body;
143            var declaringType = body.Method.DeclaringType;
144            var module = declaringType.Module;
145
146            // throw new NotImplementedException();
147            var notImplementedConstructor = module.ImportConstructor<NotImplementedException>();
148            IL.Emit(OpCodes.Newobj, notImplementedConstructor);
149            IL.Emit(OpCodes.Throw);
150        }
151
152        /// <summary>
153        ///     Saves the ref arguments of a given method using the
154        ///     <paramref name="arguments" /> from the <paramref name="invocationInfo" />
155        ///     object.
156        /// </summary>
157        /// <param name="IL">The <see cref="ILProcessor" /> that will emit the method body.</param>
158        /// <param name="parameters">The parameters of the target method.</param>
159        /// <param name="invocationInfo">The local variable that contains the <see cref="IInvocationInfo" /> instance.</param>
160        /// <param name="arguments">
161        ///     The local variable that will store the arguments from the <see cref="IInvocationInfo" />
162        ///     instance.
163        /// </param>
164        private static void SaveRefArguments(ILProcessor IL, IEnumerable<ParameterDefinition> parameters,
165            VariableDefinition invocationInfo, VariableDefinition arguments)
166        {
167            var body = IL.Body;
168            var targetMethod = body.Method;
169            var declaringType = targetMethod.DeclaringType;
170            var module = declaringType.Module;
171
172            // Save the arguments returned from the handler method
173            var getArguments = module.ImportMethod<IInvocationInfo>("get_Arguments");
174
175            IL.Emit(OpCodes.Ldloc, invocationInfo);
176            IL.Emit(OpCodes.Callvirt, getArguments);
177            IL.Emit(OpCodes.Stloc, arguments);
178
179            var index = 0;
180            foreach (var param in parameters)
181            {
182                if (!param.IsByRef())
183                {
184                    index++;
185                    continue;
186                }
187
188                // Load the destination address
189                IL.Emit(OpCodes.Ldarg, index + 1);
190
191                // Load the argument value
192                IL.Emit(OpCodes.Ldloc, arguments);
193                IL.Emit(OpCodes.Ldc_I4, index++);
194                IL.Emit(OpCodes.Ldelem_Ref);
195
196                // Determine the actual parameter type
197                if (param.ParameterType.IsByReference)
198                    continue;
199
200                var actualParameterType = param.ParameterType;
201                IL.Emit(OpCodes.Unbox_Any, actualParameterType);
202                IL.Stind(param.ParameterType);
203            }
204        }
205    }
206}