/src/LinFu.Proxy/MethodBodyEmitter.cs
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}