/src/LinFu.AOP/Emitters/AddOriginalInstructions.cs

http://github.com/philiplaureano/LinFu · C# · 64 lines · 37 code · 9 blank · 18 comment · 10 complexity · 7cfe28f9023b5422439fafc2dafa0e82 MD5 · raw file

  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using LinFu.AOP.Cecil.Interfaces;
  4. using Mono.Cecil.Cil;
  5. namespace LinFu.AOP.Cecil
  6. {
  7. /// <summary>
  8. /// Represents an instruction emitter that adds the original method instructions to a given method body.
  9. /// </summary>
  10. public class AddOriginalInstructions : IInstructionEmitter
  11. {
  12. private readonly Instruction _endLabel;
  13. private readonly IEnumerable<Instruction> _oldInstructions;
  14. /// <summary>
  15. /// Initializes a new instance of the <see cref="AddOriginalInstructions" /> class.
  16. /// </summary>
  17. /// <param name="oldInstructions">The original method instructions.</param>
  18. /// <param name="endLabel">The instruction label that marks the end of the method body.</param>
  19. public AddOriginalInstructions(IEnumerable<Instruction> oldInstructions, Instruction endLabel)
  20. {
  21. _oldInstructions = oldInstructions;
  22. _endLabel = endLabel;
  23. }
  24. /// <summary>
  25. /// Adds the original instructions to a given method body.
  26. /// </summary>
  27. /// <param name="IL">The <see cref="ILProcessor" /> responsible for the target method body.</param>
  28. public void Emit(ILProcessor IL)
  29. {
  30. var originalInstructions = new List<Instruction>(_oldInstructions);
  31. var lastInstruction = originalInstructions.LastOrDefault();
  32. if (lastInstruction != null && lastInstruction.OpCode == OpCodes.Ret)
  33. {
  34. // HACK: Convert the Ret instruction into a Nop
  35. // instruction so that the code will
  36. // fall through to the epilog
  37. lastInstruction.OpCode = OpCodes.Br;
  38. lastInstruction.Operand = _endLabel;
  39. }
  40. foreach (var instruction in (IEnumerable<Instruction>) originalInstructions)
  41. {
  42. if (instruction.OpCode != OpCodes.Ret || instruction == lastInstruction)
  43. continue;
  44. if (lastInstruction == null)
  45. continue;
  46. // HACK: Modify all ret instructions to call
  47. // the epilog after execution
  48. instruction.OpCode = OpCodes.Br;
  49. instruction.Operand = lastInstruction;
  50. }
  51. // Emit the original instructions
  52. foreach (var instruction in originalInstructions) IL.Append(instruction);
  53. }
  54. }
  55. }