/src/LinFu.AOP/InvocationInfoEmitter.cs

http://github.com/philiplaureano/LinFu · C# · 171 lines · 103 code · 31 blank · 37 comment · 4 complexity · 6e901a10e6dd37444f44f713ef0e1b75 MD5 · raw file

  1. using System;
  2. using System.Diagnostics;
  3. using System.Reflection;
  4. using LinFu.AOP.Cecil.Interfaces;
  5. using LinFu.AOP.Interfaces;
  6. using LinFu.IoC.Configuration;
  7. using LinFu.Reflection.Emit;
  8. using Mono.Cecil;
  9. using Mono.Cecil.Cil;
  10. namespace LinFu.AOP.Cecil
  11. {
  12. /// <summary>
  13. /// Represents the default implementation for the
  14. /// <see cref="IEmitInvocationInfo" /> class.
  15. /// </summary>
  16. [Implements(typeof(IEmitInvocationInfo), LifecycleType.OncePerRequest)]
  17. public class InvocationInfoEmitter : IEmitInvocationInfo
  18. {
  19. private static readonly ConstructorInfo _invocationInfoConstructor;
  20. private static readonly MethodInfo _getTypeFromHandle;
  21. private readonly bool _pushStackTrace;
  22. static InvocationInfoEmitter()
  23. {
  24. var types = new[]
  25. {
  26. typeof(object),
  27. typeof(MethodBase),
  28. typeof(StackTrace),
  29. typeof(Type[]),
  30. typeof(Type[]),
  31. typeof(Type),
  32. typeof(object[])
  33. };
  34. _invocationInfoConstructor = typeof(InvocationInfo).GetConstructor(types);
  35. _getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle",
  36. BindingFlags.Static | BindingFlags.Public);
  37. }
  38. /// <summary>
  39. /// Initializes a new instance of the InvocationInfoEmitter class.
  40. /// </summary>
  41. public InvocationInfoEmitter()
  42. : this(false)
  43. {
  44. }
  45. /// <summary>
  46. /// Initializes a new instance of the InvocationInfoEmitter class.
  47. /// </summary>
  48. /// <param name="pushStackTrace">Determines whether or not stack trace information will be available at runtime.</param>
  49. public InvocationInfoEmitter(bool pushStackTrace)
  50. {
  51. _pushStackTrace = pushStackTrace;
  52. }
  53. /// <summary>
  54. /// Emits the IL to save information about
  55. /// the method currently being executed.
  56. /// </summary>
  57. /// <seealso cref="IInvocationInfo" />
  58. /// <param name="targetMethod">The target method currently being executed.</param>
  59. /// <param name="interceptedMethod">
  60. /// The method that will be passed to the <paramref name="invocationInfo" /> as the
  61. /// currently executing method.
  62. /// </param>
  63. /// <param name="invocationInfo">The local variable that will store the resulting <see cref="IInvocationInfo" /> instance.</param>
  64. public void Emit(MethodDefinition targetMethod, MethodReference interceptedMethod,
  65. VariableDefinition invocationInfo)
  66. {
  67. var module = targetMethod.DeclaringType.Module;
  68. var currentMethod = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(MethodBase));
  69. var parameterTypes = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(Type[]));
  70. var arguments = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(object[]));
  71. var typeArguments = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(Type[]));
  72. var systemType = module.ImportType(typeof(Type));
  73. var IL = targetMethod.Body.GetILProcessor();
  74. // Type[] typeArguments = new Type[genericTypeCount];
  75. var genericParameterCount = targetMethod.GenericParameters.Count;
  76. IL.Emit(OpCodes.Ldc_I4, genericParameterCount);
  77. IL.Emit(OpCodes.Newarr, systemType);
  78. IL.Emit(OpCodes.Stloc, typeArguments);
  79. // object[] arguments = new object[argumentCount];
  80. IL.PushArguments(targetMethod, module, arguments);
  81. // object target = this;
  82. if (targetMethod.HasThis)
  83. IL.Emit(OpCodes.Ldarg_0);
  84. else
  85. IL.Emit(OpCodes.Ldnull);
  86. IL.PushMethod(interceptedMethod, module);
  87. IL.Emit(OpCodes.Stloc, currentMethod);
  88. // MethodBase targetMethod = currentMethod as MethodBase;
  89. IL.Emit(OpCodes.Ldloc, currentMethod);
  90. // Push the generic type arguments onto the stack
  91. if (genericParameterCount > 0)
  92. IL.PushGenericArguments(targetMethod, module, typeArguments);
  93. // Make sure that the generic methodinfo is instantiated with the
  94. // proper type arguments
  95. if (targetMethod.GenericParameters.Count > 0)
  96. {
  97. var methodInfoType = module.Import(typeof(MethodInfo));
  98. IL.Emit(OpCodes.Isinst, methodInfoType);
  99. var getIsGenericMethodDef = module.ImportMethod<MethodInfo>("get_IsGenericMethodDefinition");
  100. IL.Emit(OpCodes.Dup);
  101. IL.Emit(OpCodes.Callvirt, getIsGenericMethodDef);
  102. // Determine if the current method is a generic method
  103. // definition
  104. var skipMakeGenericMethod = IL.Create(OpCodes.Nop);
  105. IL.Emit(OpCodes.Brfalse, skipMakeGenericMethod);
  106. // Instantiate the specific generic method instance
  107. var makeGenericMethod = module.ImportMethod<MethodInfo>("MakeGenericMethod", typeof(Type[]));
  108. IL.Emit(OpCodes.Ldloc, typeArguments);
  109. IL.Emit(OpCodes.Callvirt, makeGenericMethod);
  110. IL.Append(skipMakeGenericMethod);
  111. }
  112. if (_pushStackTrace)
  113. IL.PushStackTrace(module);
  114. else
  115. IL.Emit(OpCodes.Ldnull);
  116. // Save the parameter types
  117. IL.Emit(OpCodes.Ldc_I4, targetMethod.Parameters.Count);
  118. IL.Emit(OpCodes.Newarr, systemType);
  119. IL.Emit(OpCodes.Stloc, parameterTypes);
  120. IL.SaveParameterTypes(targetMethod, module, parameterTypes);
  121. IL.Emit(OpCodes.Ldloc, parameterTypes);
  122. // Push the type arguments back onto the stack
  123. IL.Emit(OpCodes.Ldloc, typeArguments);
  124. // Save the return type
  125. var getTypeFromHandle = module.Import(_getTypeFromHandle);
  126. var returnType = targetMethod.ReturnType;
  127. IL.Emit(OpCodes.Ldtoken, returnType);
  128. IL.Emit(OpCodes.Call, getTypeFromHandle);
  129. // Push the arguments back onto the stack
  130. IL.Emit(OpCodes.Ldloc, arguments);
  131. // InvocationInfo info = new InvocationInfo(...);
  132. var infoConstructor = module.Import(_invocationInfoConstructor);
  133. IL.Emit(OpCodes.Newobj, infoConstructor);
  134. IL.Emit(OpCodes.Stloc, invocationInfo);
  135. IL.Emit(OpCodes.Ldloc, invocationInfo);
  136. var addInstance = module.Import(typeof(IgnoredInstancesRegistry).GetMethod("AddInstance"));
  137. IL.Emit(OpCodes.Call, addInstance);
  138. }
  139. }
  140. }