/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

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