/src/LinFu.AOP/Emitters/InvokeMethodReplacement.cs

http://github.com/philiplaureano/LinFu · C# · 115 lines · 73 code · 17 blank · 25 comment · 0 complexity · 64a64585278c62c2777a343514ad4693 MD5 · raw file

  1. using LinFu.AOP.Cecil.Interfaces;
  2. using LinFu.AOP.Interfaces;
  3. using LinFu.Reflection.Emit;
  4. using Mono.Cecil;
  5. using Mono.Cecil.Cil;
  6. using MethodDefinitionExtensions = LinFu.AOP.Cecil.Extensions.MethodDefinitionExtensions;
  7. namespace LinFu.AOP.Cecil
  8. {
  9. /// <summary>
  10. /// Represents a class that emits the instructions that call the method replacement instead of the original method
  11. /// body.
  12. /// </summary>
  13. public class InvokeMethodReplacement : IInstructionEmitter
  14. {
  15. private readonly VariableDefinition _classMethodReplacementProvider;
  16. private readonly Instruction _executeOriginalInstructions;
  17. private readonly VariableDefinition _invocationInfo;
  18. private readonly VariableDefinition _methodReplacementProvider;
  19. /// <summary>
  20. /// Initializes a new instance of the <see cref="InvokeMethodReplacement" /> class.
  21. /// </summary>
  22. /// <param name="executeOriginalInstructions">
  23. /// The instruction label that will be used if the original instructions should
  24. /// be executed.
  25. /// </param>
  26. /// <param name="methodReplacementProvider">
  27. /// The variable that contains the <see cref="IMethodReplacementProvider" />
  28. /// instance.
  29. /// </param>
  30. /// <param name="classMethodReplacementProvider">
  31. /// The variable that contains the class-level
  32. /// <see cref="IMethodReplacementProvider" /> instance.
  33. /// </param>
  34. /// <param name="invocationInfo">The variable that contains the <see cref="IInvocationInfo" /> instance.</param>
  35. public InvokeMethodReplacement(Instruction executeOriginalInstructions,
  36. VariableDefinition methodReplacementProvider,
  37. VariableDefinition classMethodReplacementProvider,
  38. VariableDefinition invocationInfo)
  39. {
  40. _executeOriginalInstructions = executeOriginalInstructions;
  41. _methodReplacementProvider = methodReplacementProvider;
  42. _classMethodReplacementProvider = classMethodReplacementProvider;
  43. _invocationInfo = invocationInfo;
  44. }
  45. /// <summary>
  46. /// Emits the instructions that call the method replacement instead of the original method body.
  47. /// </summary>
  48. /// <param name="IL">The <see cref="ILProcessor" /> that points to the current method body.</param>
  49. public void Emit(ILProcessor IL)
  50. {
  51. var method = IL.Body.Method;
  52. var module = method.Module;
  53. var returnType = method.ReturnType;
  54. var methodReplacement = MethodDefinitionExtensions.AddLocal(method, typeof(IInterceptor));
  55. GetMethodReplacementInstance(method, IL, methodReplacement, _methodReplacementProvider, _invocationInfo);
  56. var skipGetClassMethodReplacement = IL.Create(OpCodes.Nop);
  57. IL.Emit(OpCodes.Ldloc, methodReplacement);
  58. IL.Emit(OpCodes.Brtrue, skipGetClassMethodReplacement);
  59. GetMethodReplacementInstance(method, IL, methodReplacement, _classMethodReplacementProvider,
  60. _invocationInfo);
  61. IL.Append(skipGetClassMethodReplacement);
  62. IL.Emit(OpCodes.Ldloc, methodReplacement);
  63. IL.Emit(OpCodes.Brfalse, _executeOriginalInstructions);
  64. // var returnValue = replacement.Intercept(info);
  65. InvokeInterceptor(module, IL, methodReplacement, returnType, _invocationInfo);
  66. }
  67. private static void InvokeInterceptor(ModuleDefinition module, ILProcessor IL,
  68. VariableDefinition methodReplacement, TypeReference returnType,
  69. VariableDefinition invocationInfo)
  70. {
  71. var interceptMethod = module.ImportMethod<IInterceptor>("Intercept");
  72. IL.Emit(OpCodes.Ldloc, methodReplacement);
  73. IL.Emit(OpCodes.Ldloc, invocationInfo);
  74. IL.Emit(OpCodes.Callvirt, interceptMethod);
  75. IL.PackageReturnValue(module, returnType);
  76. }
  77. private static void GetMethodReplacementInstance(MethodDefinition method,
  78. ILProcessor IL,
  79. VariableDefinition methodReplacement,
  80. VariableDefinition methodReplacementProvider,
  81. VariableDefinition invocationInfo)
  82. {
  83. var declaringType = method.DeclaringType;
  84. var module = declaringType.Module;
  85. var pushInstance = method.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull);
  86. var getReplacement = module.ImportMethod<IMethodReplacementProvider>("GetMethodReplacement");
  87. IL.Emit(OpCodes.Ldloc, methodReplacementProvider);
  88. var skipGetMethodReplacement = IL.Create(OpCodes.Nop);
  89. IL.Emit(OpCodes.Brfalse, skipGetMethodReplacement);
  90. IL.Emit(OpCodes.Ldloc, methodReplacementProvider);
  91. IL.Append(pushInstance);
  92. IL.Emit(OpCodes.Ldloc, invocationInfo);
  93. IL.Emit(OpCodes.Callvirt, getReplacement);
  94. IL.Emit(OpCodes.Stloc, methodReplacement);
  95. IL.Append(skipGetMethodReplacement);
  96. }
  97. }
  98. }