PageRenderTime 42ms CodeModel.GetById 23ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

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