/src/LinFu.AOP/NewOperatorInterception/RedirectNewInstancesToActivator.cs

http://github.com/philiplaureano/LinFu · C# · 227 lines · 158 code · 45 blank · 24 comment · 6 complexity · 7beb0456fec3e3543b012f87cde33170 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using LinFu.AOP.Cecil.Extensions;
  6. using LinFu.AOP.Cecil.Interfaces;
  7. using LinFu.AOP.Interfaces;
  8. using LinFu.Reflection.Emit;
  9. using Mono.Cecil;
  10. using Mono.Cecil.Cil;
  11. namespace LinFu.AOP.Cecil
  12. {
  13. internal class RedirectNewInstancesToActivator : INewObjectWeaver
  14. {
  15. private readonly INewInstanceFilter _filter;
  16. private MethodReference _addMethod;
  17. private MethodReference _canActivate;
  18. private VariableDefinition _constructorArguments;
  19. private MethodReference _createInstance;
  20. private VariableDefinition _currentActivator;
  21. private VariableDefinition _currentArgument;
  22. private MethodReference _getItem;
  23. private MethodReference _getStaticActivator;
  24. private MethodReference _getTypeFromHandle;
  25. private MethodReference _methodActivationContextCtor;
  26. private VariableDefinition _methodContext;
  27. private MethodReference _objectListCtor;
  28. private MethodReference _reverseMethod;
  29. private MethodReference _toArrayMethod;
  30. public RedirectNewInstancesToActivator(INewInstanceFilter filter)
  31. {
  32. _filter = filter;
  33. }
  34. public RedirectNewInstancesToActivator(Func<MethodReference, TypeReference, MethodReference, bool> filter)
  35. {
  36. _filter = new NewInstanceInterceptionAdapter(filter);
  37. }
  38. public bool ShouldIntercept(MethodReference constructor, TypeReference concreteType, MethodReference hostMethod)
  39. {
  40. // Intercept all types by default
  41. if (_filter == null)
  42. return true;
  43. return _filter.ShouldWeave(constructor, concreteType, hostMethod);
  44. }
  45. public void AddAdditionalMembers(TypeDefinition host)
  46. {
  47. // Make sure the type implements IActivatorHost
  48. var interfaceWeaver = new ImplementActivatorHostWeaver();
  49. host.Accept(interfaceWeaver);
  50. }
  51. public void ImportReferences(ModuleDefinition module)
  52. {
  53. // Static method imports
  54. _getStaticActivator = module.ImportMethod("GetActivator", typeof(TypeActivatorRegistry),
  55. BindingFlags.Public | BindingFlags.Static);
  56. _getTypeFromHandle = module.ImportMethod<Type>("GetTypeFromHandle",
  57. BindingFlags.Public | BindingFlags.Static);
  58. // Constructor imports
  59. _methodActivationContextCtor = module.ImportConstructor<TypeActivationContext>(typeof(object),
  60. typeof(MethodBase),
  61. typeof(Type),
  62. typeof(object[]));
  63. // Instance method imports
  64. _objectListCtor = module.ImportConstructor<List<object>>();
  65. _toArrayMethod = module.ImportMethod<List<object>>("ToArray", new Type[0]);
  66. _addMethod = module.ImportMethod<List<object>>("Add", typeof(object));
  67. _reverseMethod = module.ImportMethod<List<object>>("Reverse", new Type[0]);
  68. _canActivate = module.ImportMethod<ITypeActivator>("CanActivate");
  69. _getItem = module.ImportMethod<List<object>>("get_Item", typeof(int));
  70. var createInstanceMethod = typeof(IActivator<ITypeActivationContext>).GetMethod("CreateInstance");
  71. _createInstance = module.Import(createInstanceMethod);
  72. }
  73. public void EmitNewObject(MethodDefinition hostMethod, ILProcessor IL, MethodReference targetConstructor,
  74. TypeReference concreteType)
  75. {
  76. var parameters = targetConstructor.Parameters;
  77. var skipInterception = IL.Create(OpCodes.Nop);
  78. SaveConstructorArguments(IL, parameters);
  79. EmitCreateMethodActivationContext(hostMethod, IL, concreteType);
  80. // Skip the interception if an activator cannot be found
  81. EmitGetActivator(hostMethod, IL, skipInterception);
  82. IL.Emit(OpCodes.Stloc, _currentActivator);
  83. IL.Emit(OpCodes.Ldloc, _currentActivator);
  84. IL.Emit(OpCodes.Brfalse, skipInterception);
  85. // Determine if the activator can instantiate the method from the
  86. // current context
  87. IL.Emit(OpCodes.Ldloc, _currentActivator);
  88. IL.Emit(OpCodes.Ldloc, _methodContext);
  89. IL.Emit(OpCodes.Callvirt, _canActivate);
  90. IL.Emit(OpCodes.Brfalse, skipInterception);
  91. // Use the activator to create the object instance
  92. EmitCreateInstance(IL);
  93. // }
  94. var endCreate = IL.Create(OpCodes.Nop);
  95. IL.Emit(OpCodes.Br, endCreate);
  96. // else {
  97. IL.Append(skipInterception);
  98. // Restore the arguments that were popped off the stack
  99. // by the list of constructor arguments
  100. var parameterCount = parameters.Count;
  101. for (var index = 0; index < parameterCount; index++)
  102. {
  103. var currentParameter = parameters[index];
  104. IL.Emit(OpCodes.Ldloc, _constructorArguments);
  105. IL.Emit(OpCodes.Ldc_I4, index);
  106. IL.Emit(OpCodes.Callvirt, _getItem);
  107. IL.Emit(OpCodes.Unbox_Any, currentParameter.ParameterType);
  108. }
  109. IL.Emit(OpCodes.Newobj, targetConstructor);
  110. // }
  111. IL.Append(endCreate);
  112. }
  113. public void AddLocals(MethodDefinition hostMethod)
  114. {
  115. _constructorArguments = hostMethod.AddLocal<List<object>>();
  116. _currentArgument = hostMethod.AddLocal<object>();
  117. _methodContext = hostMethod.AddLocal<ITypeActivationContext>();
  118. _currentActivator = hostMethod.AddLocal<ITypeActivator>();
  119. }
  120. private void EmitCreateInstance(ILProcessor IL)
  121. {
  122. // T instance = this.Activator.CreateInstance(context);
  123. IL.Emit(OpCodes.Ldloc, _currentActivator);
  124. IL.Emit(OpCodes.Ldloc, _methodContext);
  125. IL.Emit(OpCodes.Callvirt, _createInstance);
  126. }
  127. private void EmitCreateMethodActivationContext(MethodDefinition method, ILProcessor IL,
  128. TypeReference concreteType)
  129. {
  130. // TODO: Add static method support
  131. var pushThis = method.IsStatic ? IL.Create(OpCodes.Ldnull) : IL.Create(OpCodes.Ldarg_0);
  132. // Push the 'this' pointer onto the stack
  133. IL.Append(pushThis);
  134. var module = method.DeclaringType.Module;
  135. // Push the current method onto the stack
  136. IL.PushMethod(method, module);
  137. // Push the concrete type onto the stack
  138. IL.Emit(OpCodes.Ldtoken, concreteType);
  139. IL.Emit(OpCodes.Call, _getTypeFromHandle);
  140. IL.Emit(OpCodes.Ldloc, _constructorArguments);
  141. IL.Emit(OpCodes.Callvirt, _toArrayMethod);
  142. IL.Emit(OpCodes.Newobj, _methodActivationContextCtor);
  143. // var context = new MethodActivationContext(this, currentMethod, concreteType, args);
  144. IL.Emit(OpCodes.Stloc, _methodContext);
  145. }
  146. private void SaveConstructorArguments(ILProcessor IL, IEnumerable<ParameterDefinition> currentParams)
  147. {
  148. var parameters = currentParams.ToArray();
  149. var parameterCount = parameters.Length;
  150. IL.Emit(OpCodes.Newobj, _objectListCtor);
  151. IL.Emit(OpCodes.Stloc, _constructorArguments);
  152. var index = parameterCount - 1;
  153. while (index >= 0)
  154. {
  155. var param = parameters[index];
  156. SaveConstructorArgument(IL, param);
  157. index--;
  158. }
  159. // Reverse the constructor arguments so that they appear in the correct order
  160. IL.Emit(OpCodes.Ldloc, _constructorArguments);
  161. IL.Emit(OpCodes.Callvirt, _reverseMethod);
  162. }
  163. private void SaveConstructorArgument(ILProcessor IL, ParameterDefinition param)
  164. {
  165. // Box the type if necessary
  166. var parameterType = param.ParameterType;
  167. if (parameterType.IsValueType || parameterType is GenericParameter)
  168. IL.Emit(OpCodes.Box, parameterType);
  169. // Save the current argument
  170. IL.Emit(OpCodes.Stloc, _currentArgument);
  171. // Add the item to the item to the collection
  172. IL.Emit(OpCodes.Ldloc, _constructorArguments);
  173. IL.Emit(OpCodes.Ldloc, _currentArgument);
  174. IL.Emit(OpCodes.Callvirt, _addMethod);
  175. }
  176. private void EmitGetActivator(MethodDefinition method, ILProcessor IL, Instruction skipInterception)
  177. {
  178. IL.Emit(OpCodes.Ldloc, _methodContext);
  179. IL.Emit(OpCodes.Call, _getStaticActivator);
  180. }
  181. }
  182. }