Adding ignored bug test for DYNPROXY-51
[castle.git] / Tools / Castle.DynamicProxy2 / Castle.DynamicProxy / Generators / BaseProxyGenerator.cs
blob7343e655a4413ef04763ef60ac6c290e030644bf
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 using System;
16 using System.Collections;
17 using System.Reflection;
18 using System.Reflection.Emit;
19 using System.Runtime.Serialization;
20 using Castle.Core.Interceptor;
21 using Castle.DynamicProxy.Generators.Emitters;
22 using Castle.DynamicProxy.Generators.Emitters.CodeBuilders;
23 using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
24 using Castle.DynamicProxy.Serialization;
26 namespace Castle.DynamicProxy.Generators
28 #if DOTNET2
29 using System.Collections.Generic;
30 #endif
32 public enum ConstructorVersion
34 WithTargetMethod,
35 WithoutTargetMethod
38 /// <summary>
39 /// Base class that exposes the common functionalities
40 /// to proxy generation.
41 /// </summary>
42 /// <remarks>
43 /// TODO:
44 /// - Use the interceptor selector if provided
45 /// - Add tests and fixes for 'leaking this' problem
46 /// - Mixin support
47 /// </remarks>
48 [CLSCompliant(false)]
49 public abstract class BaseProxyGenerator
51 private static MethodInfo invocation_getArgumentsMethod = typeof (AbstractInvocation).GetMethod("get_Arguments");
53 private readonly ModuleScope scope;
54 private int nestedCounter, callbackCounter;
55 private int fieldCount = 1;
56 private FieldReference typeTokenField;
57 private Hashtable method2TokenField = new Hashtable();
58 private IList generateNewSlot = new ArrayList();
59 protected IList methodsToSkip = new ArrayList();
61 protected readonly Type targetType;
62 protected IProxyGenerationHook generationHook;
63 protected ConstructorInfo serializationConstructor;
65 protected BaseProxyGenerator(ModuleScope scope, Type targetType)
67 this.scope = scope;
68 this.targetType = targetType;
71 protected ModuleScope Scope
73 get { return scope; }
76 protected virtual ClassEmitter BuildClassEmitter(String typeName, Type parentType, IList interfaceList)
78 Type[] interfaces = new Type[interfaceList.Count];
80 interfaceList.CopyTo(interfaces, 0);
82 return BuildClassEmitter(typeName, parentType, interfaces);
85 protected virtual ClassEmitter BuildClassEmitter(String typeName, Type parentType, Type[] interfaces)
87 if (interfaces == null)
89 interfaces = new Type[0];
92 return new ClassEmitter(Scope, typeName, parentType, interfaces, true);
95 /// <summary>
96 /// Used by dinamically implement <see cref="Core.Interceptor.IProxyTargetAccessor"/>
97 /// </summary>
98 /// <returns></returns>
99 protected abstract Reference GetProxyTargetReference();
101 protected abstract bool CanOnlyProxyVirtual();
103 #region Cache related
105 protected Type GetFromCache(CacheKey key)
107 return scope.GetFromCache(key);
110 protected void AddToCache(CacheKey key, Type type)
112 scope.RegisterInCache(key, type);
115 #endregion
117 protected MethodEmitter CreateProxiedMethod(
118 Type targetType,
119 MethodInfo method,
120 ClassEmitter emitter,
121 NestedClassEmitter invocationImpl,
122 FieldReference interceptorsField,
123 Reference targetRef,
124 ConstructorVersion version,
125 MethodInfo methodOnTarget)
127 MethodAttributes atts = ObtainMethodAttributes(method);
128 MethodEmitter methodEmitter = emitter.CreateMethod(method.Name, atts);
130 return
131 ImplementProxiedMethod(targetType,
132 methodEmitter,
133 method,
134 emitter,
135 invocationImpl,
136 interceptorsField,
137 targetRef,
138 version,
139 methodOnTarget);
142 protected void ImplementBlankInterface(
143 Type targetType,
144 Type _interface,
145 ClassEmitter emitter,
146 FieldReference interceptorsField,
147 ConstructorEmitter typeInitializerConstructor)
149 PropertyToGenerate[] propsToGenerate;
150 EventToGenerate[] eventsToGenerate;
151 MethodInfo[] methods = CollectMethodsAndProperties(emitter, _interface, false, out propsToGenerate, out eventsToGenerate);
153 Dictionary<MethodInfo, NestedClassEmitter> method2Invocation = new Dictionary<MethodInfo, NestedClassEmitter>();
155 foreach (MethodInfo method in methods)
157 AddFieldToCacheMethodTokenAndStatementsToInitialize(method, typeInitializerConstructor, emitter);
159 method2Invocation[method] =
160 BuildInvocationNestedType(emitter,
161 targetType,
162 emitter.TypeBuilder,
163 method,
164 null,
165 ConstructorVersion.WithoutTargetMethod);
168 foreach (MethodInfo method in methods)
170 if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")))
172 continue;
175 NestedClassEmitter nestedClass = method2Invocation[method];
177 MethodEmitter newProxiedMethod =
178 CreateProxiedMethod(targetType,
179 method,
180 emitter,
181 nestedClass,
182 interceptorsField,
183 SelfReference.Self,
184 ConstructorVersion.WithoutTargetMethod,
185 null);
187 ReplicateNonInheritableAttributes(method, newProxiedMethod);
190 foreach (PropertyToGenerate propToGen in propsToGenerate)
192 if (propToGen.CanRead)
194 NestedClassEmitter nestedClass = method2Invocation[propToGen.GetMethod];
196 MethodAttributes atts = ObtainMethodAttributes(propToGen.GetMethod);
198 MethodEmitter getEmitter = propToGen.Emitter.CreateGetMethod(atts);
200 ImplementProxiedMethod(targetType,
201 getEmitter,
202 propToGen.GetMethod,
203 emitter,
204 nestedClass,
205 interceptorsField,
206 SelfReference.Self,
207 ConstructorVersion.WithoutTargetMethod,
208 null);
210 ReplicateNonInheritableAttributes(propToGen.GetMethod, getEmitter);
213 if (propToGen.CanWrite)
215 NestedClassEmitter nestedClass = method2Invocation[propToGen.SetMethod];
217 MethodAttributes atts = ObtainMethodAttributes(propToGen.SetMethod);
219 MethodEmitter setEmitter = propToGen.Emitter.CreateSetMethod(atts);
221 ImplementProxiedMethod(targetType,
222 setEmitter,
223 propToGen.SetMethod,
224 emitter,
225 nestedClass,
226 interceptorsField,
227 SelfReference.Self,
228 ConstructorVersion.WithoutTargetMethod,
229 null);
231 ReplicateNonInheritableAttributes(propToGen.SetMethod, setEmitter);
235 foreach (EventToGenerate eventToGenerate in eventsToGenerate)
237 NestedClassEmitter add_nestedClass = method2Invocation[eventToGenerate.AddMethod];
239 MethodAttributes add_atts = ObtainMethodAttributes(eventToGenerate.AddMethod);
241 MethodEmitter addEmitter = eventToGenerate.Emitter.CreateAddMethod(add_atts);
243 ImplementProxiedMethod(targetType,
244 addEmitter,
245 eventToGenerate.AddMethod,
246 emitter,
247 add_nestedClass,
248 interceptorsField,
249 SelfReference.Self,
250 ConstructorVersion.WithoutTargetMethod,
251 null);
253 ReplicateNonInheritableAttributes(eventToGenerate.AddMethod, addEmitter);
255 NestedClassEmitter remove_nestedClass = method2Invocation[eventToGenerate.RemoveMethod];
257 MethodAttributes remove_atts = ObtainMethodAttributes(eventToGenerate.RemoveMethod);
259 MethodEmitter removeEmitter = eventToGenerate.Emitter.CreateRemoveMethod(remove_atts);
261 ImplementProxiedMethod(targetType,
262 removeEmitter,
263 eventToGenerate.RemoveMethod,
264 emitter,
265 remove_nestedClass,
266 interceptorsField,
267 SelfReference.Self,
268 ConstructorVersion.WithoutTargetMethod,
269 null);
271 ReplicateNonInheritableAttributes(eventToGenerate.RemoveMethod, removeEmitter);
275 protected MethodEmitter ImplementProxiedMethod(
276 Type targetType,
277 MethodEmitter methodEmitter,
278 MethodInfo method,
279 ClassEmitter emitter,
280 NestedClassEmitter invocationImpl,
281 FieldReference interceptorsField,
282 Reference targetRef,
283 ConstructorVersion version,
284 MethodInfo methodOnTarget)
286 methodEmitter.CopyParametersAndReturnTypeFrom(method, emitter);
288 TypeReference[] dereferencedArguments = IndirectReference.WrapIfByRef(methodEmitter.Arguments);
290 Type iinvocation = invocationImpl.TypeBuilder;
292 Type[] set1 = null;
293 Type[] set2 = null;
295 if (iinvocation.IsGenericType)
297 // get type generics
298 set1 = targetType.GetGenericArguments();
301 if (method.IsGenericMethod)
303 // get method generics
304 set2 = method.GetGenericArguments();
307 bool isGenericInvocationClass = false;
309 if (set1 != null || set2 != null)
311 iinvocation = iinvocation.MakeGenericType(TypeUtil.Union(set1, set2));
313 isGenericInvocationClass = true;
316 LocalReference invocationImplLocal = methodEmitter.CodeBuilder.DeclareLocal(iinvocation);
318 // TODO: Initialize iinvocation instance
319 // with ordinary arguments and in and out arguments
321 Expression interceptors;
323 // if (useSelector)
325 // TODO: Generate code that checks the return of selector
326 // if no interceptors is returned, should we invoke the base.Method directly?
328 // else
330 interceptors = interceptorsField.ToExpression();
333 Expression typeTokenFieldExp = typeTokenField.ToExpression();
334 Expression methodInfoTokenExp;
336 if (method2TokenField.ContainsKey(method)) // Token is in the cache
338 methodInfoTokenExp = ((FieldReference) method2TokenField[method]).ToExpression();
340 else
342 // Not in the cache: generic method
344 methodInfoTokenExp = new MethodTokenExpression(method);
347 ConstructorInfo constructor = invocationImpl.Constructors[0].Builder;
349 if (isGenericInvocationClass)
351 constructor = TypeBuilder.GetConstructor(iinvocation, invocationImpl.Constructors[0].Builder);
354 NewInstanceExpression newInvocImpl;
356 if (version == ConstructorVersion.WithTargetMethod)
358 Expression methodOnTargetTokenExp;
360 if (method2TokenField.ContainsKey(methodOnTarget)) // Token is in the cache
362 methodOnTargetTokenExp = ((FieldReference) method2TokenField[methodOnTarget]).ToExpression();
364 else
366 // Not in the cache: generic method
368 methodOnTargetTokenExp = new MethodTokenExpression(methodOnTarget);
371 newInvocImpl =
372 new NewInstanceExpression(constructor,
373 targetRef.ToExpression(),
374 interceptors,
375 typeTokenFieldExp,
376 methodOnTargetTokenExp,
377 methodInfoTokenExp,
378 new ReferencesToObjectArrayExpression(dereferencedArguments),
379 SelfReference.Self.ToExpression());
381 else
383 newInvocImpl =
384 new NewInstanceExpression(constructor,
385 targetRef.ToExpression(),
386 interceptors,
387 typeTokenFieldExp,
388 methodInfoTokenExp,
389 new ReferencesToObjectArrayExpression(dereferencedArguments),
390 SelfReference.Self.ToExpression());
393 methodEmitter.CodeBuilder.AddStatement(new AssignStatement(invocationImplLocal, newInvocImpl));
395 methodEmitter.CodeBuilder.AddStatement(
396 new ExpressionStatement(new MethodInvocationExpression(invocationImplLocal, Constants.AbstractInvocationProceed)));
398 CopyOutAndRefParameters(dereferencedArguments, invocationImplLocal, method, methodEmitter);
400 if (method.ReturnType != typeof (void))
402 // Emit code to return with cast from ReturnValue
403 MethodInvocationExpression getRetVal =
404 new MethodInvocationExpression(invocationImplLocal, typeof (AbstractInvocation).GetMethod("get_ReturnValue"));
406 methodEmitter.CodeBuilder.AddStatement(new ReturnStatement(new ConvertExpression(method.ReturnType, getRetVal)));
408 else
410 methodEmitter.CodeBuilder.AddStatement(new ReturnStatement());
413 return methodEmitter;
416 private static void CopyOutAndRefParameters(
417 TypeReference[] dereferencedArguments, LocalReference invocationImplLocal, MethodInfo method, MethodEmitter methodEmitter)
419 ParameterInfo[] parameters = method.GetParameters();
420 bool hasByRefParam = false;
421 for (int i = 0; i < parameters.Length; i++)
423 if (parameters[i].ParameterType.IsByRef)
424 hasByRefParam = true;
426 if (!hasByRefParam)
427 return; //saving the need to create locals if there is no need
428 LocalReference invocationArgs = methodEmitter.CodeBuilder.DeclareLocal(typeof (object[]));
429 methodEmitter.CodeBuilder.AddStatement(
430 new AssignStatement(invocationArgs,
431 new MethodInvocationExpression(invocationImplLocal, invocation_getArgumentsMethod)
434 for (int i = 0; i < parameters.Length; i++)
436 if (parameters[i].ParameterType.IsByRef)
438 methodEmitter.CodeBuilder.AddStatement(
439 new AssignStatement(dereferencedArguments[i],
440 new ConvertExpression(dereferencedArguments[i].Type,
441 new LoadRefArrayElementExpression(i, invocationArgs)
448 protected void GenerateConstructor(ClassEmitter emitter, params FieldReference[] fields)
450 GenerateConstructor(emitter, null, fields);
453 protected void GenerateConstructor(
454 ClassEmitter emitter, ConstructorInfo baseConstructor, params FieldReference[] fields)
456 ArgumentReference[] args;
457 ParameterInfo[] baseConstructorParams = null;
459 if (baseConstructor != null)
461 baseConstructorParams = baseConstructor.GetParameters();
464 if (baseConstructorParams != null && baseConstructorParams.Length != 0)
466 args = new ArgumentReference[fields.Length + baseConstructorParams.Length];
468 int offset = fields.Length;
470 for (int i = offset; i < offset + baseConstructorParams.Length; i++)
472 ParameterInfo paramInfo = baseConstructorParams[i - offset];
473 args[i] = new ArgumentReference(paramInfo.ParameterType);
476 else
478 args = new ArgumentReference[fields.Length];
481 for (int i = 0; i < fields.Length; i++)
483 args[i] = new ArgumentReference(fields[i].Reference.FieldType);
486 ConstructorEmitter constructor = emitter.CreateConstructor(args);
488 for (int i = 0; i < fields.Length; i++)
490 constructor.CodeBuilder.AddStatement(new AssignStatement(fields[i], args[i].ToExpression()));
493 // Invoke base constructor
495 if (baseConstructor != null)
497 ArgumentReference[] slice = new ArgumentReference[baseConstructorParams.Length];
498 Array.Copy(args, fields.Length, slice, 0, baseConstructorParams.Length);
500 constructor.CodeBuilder.InvokeBaseConstructor(baseConstructor, slice);
502 else
504 constructor.CodeBuilder.InvokeBaseConstructor();
507 // Invoke initialize method
509 // constructor.CodeBuilder.AddStatement(
510 // new ExpressionStatement(new MethodInvocationExpression(SelfReference.Self, initCacheMethod)));
512 constructor.CodeBuilder.AddStatement(new ReturnStatement());
515 /// <summary>
516 /// Generates a parameters constructor that initializes the proxy
517 /// state with <see cref="StandardInterceptor"/> just to make it non-null.
518 /// <para>
519 /// This constructor is important to allow proxies to be XML serializable
520 /// </para>
521 /// </summary>
522 protected void GenerateParameterlessConstructor(ClassEmitter emitter, Type baseClass, FieldReference interceptorField)
524 // Check if the type actually has a default constructor
526 ConstructorInfo defaultConstructor = baseClass.GetConstructor(BindingFlags.Public, null, Type.EmptyTypes, null);
528 if (defaultConstructor == null)
530 defaultConstructor = baseClass.GetConstructor(BindingFlags.NonPublic, null, Type.EmptyTypes, null);
532 if (defaultConstructor == null || defaultConstructor.IsPrivate)
534 return;
538 ConstructorEmitter constructor = emitter.CreateConstructor();
540 // initialize fields with an empty interceptor
542 constructor.CodeBuilder.AddStatement(
543 new AssignStatement(interceptorField, new NewArrayExpression(1, typeof (IInterceptor))));
544 constructor.CodeBuilder.AddStatement(
545 new AssignArrayStatement(interceptorField, 0, new NewInstanceExpression(typeof (StandardInterceptor), new Type[0])));
547 // Invoke base constructor
549 constructor.CodeBuilder.InvokeBaseConstructor(defaultConstructor);
551 constructor.CodeBuilder.AddStatement(new ReturnStatement());
554 #region First level attributes
556 protected MethodAttributes ObtainMethodAttributes(MethodInfo method)
558 MethodAttributes atts = MethodAttributes.Virtual;
560 if (ShouldCreateNewSlot(method))
562 atts |= MethodAttributes.NewSlot;
565 if (method.IsPublic)
567 atts |= MethodAttributes.Public;
570 if (method.IsHideBySig)
572 atts |= MethodAttributes.HideBySig;
574 if (InternalsHelper.IsInternal(method) && InternalsHelper.IsInternalToDynamicProxy(method.DeclaringType.Assembly))
576 atts |= MethodAttributes.Assembly;
578 if (method.IsFamilyAndAssembly)
580 atts |= MethodAttributes.FamANDAssem;
582 else if (method.IsFamilyOrAssembly)
584 atts |= MethodAttributes.FamORAssem;
586 else if (method.IsFamily)
588 atts |= MethodAttributes.Family;
591 if (method.Name.StartsWith("set_") || method.Name.StartsWith("get_"))
593 atts |= MethodAttributes.SpecialName;
596 return atts;
599 private PropertyAttributes ObtainPropertyAttributes(PropertyInfo property)
601 PropertyAttributes atts = PropertyAttributes.None;
603 return atts;
606 #endregion
608 protected MethodBuilder CreateCallbackMethod(ClassEmitter emitter, MethodInfo methodInfo, MethodInfo methodOnTarget)
610 MethodInfo targetMethod = methodOnTarget != null ? methodOnTarget : methodInfo;
612 if (targetMethod.IsAbstract)
613 return null;
615 // MethodBuild creation
617 MethodAttributes atts = MethodAttributes.Family;
619 String name = methodInfo.Name + "_callback_" + ++callbackCounter;
621 MethodEmitter callBackMethod = emitter.CreateMethod(name, atts);
623 callBackMethod.CopyParametersAndReturnTypeFrom(targetMethod, emitter);
625 // Generic definition
627 if (targetMethod.IsGenericMethod)
629 targetMethod = targetMethod.MakeGenericMethod(callBackMethod.GenericTypeParams);
632 // Parameters exp
634 Expression[] exps = new Expression[callBackMethod.Arguments.Length];
636 for (int i = 0; i < callBackMethod.Arguments.Length; i++)
638 exps[i] = callBackMethod.Arguments[i].ToExpression();
641 // invocation on base class
643 callBackMethod.CodeBuilder.AddStatement(
644 new ReturnStatement(new MethodInvocationExpression(GetProxyTargetReference(), targetMethod, exps)));
646 return callBackMethod.MethodBuilder;
649 #region IInvocation related
651 /// <summary>
652 /// If callbackMethod is null the InvokeOnTarget implementation
653 /// is just the code to throw an exception
654 /// </summary>
655 /// <param name="emitter"></param>
656 /// <param name="targetType"></param>
657 /// <param name="targetForInvocation"></param>
658 /// <param name="methodInfo"></param>
659 /// <param name="callbackMethod"></param>
660 /// <param name="version"></param>
661 /// <returns></returns>
662 protected NestedClassEmitter BuildInvocationNestedType(
663 ClassEmitter emitter,
664 Type targetType,
665 Type targetForInvocation,
666 MethodInfo methodInfo,
667 MethodInfo callbackMethod,
668 ConstructorVersion version)
670 return BuildInvocationNestedType(emitter, targetType, targetForInvocation, methodInfo, callbackMethod, version, false);
673 /// <summary>
674 /// If callbackMethod is null the InvokeOnTarget implementation
675 /// is just the code to throw an exception
676 /// </summary>
677 /// <param name="emitter"></param>
678 /// <param name="targetType"></param>
679 /// <param name="targetForInvocation"></param>
680 /// <param name="methodInfo"></param>
681 /// <param name="callbackMethod"></param>
682 /// <param name="version"></param>
683 /// <param name="allowChangeTarget">If true the invocation will implement the IChangeProxyTarget interface</param>
684 /// <returns></returns>
685 protected NestedClassEmitter BuildInvocationNestedType(
686 ClassEmitter emitter,
687 Type targetType,
688 Type targetForInvocation,
689 MethodInfo methodInfo,
690 MethodInfo callbackMethod,
691 ConstructorVersion version,
692 bool allowChangeTarget)
694 nestedCounter++;
696 Type[] interfaces = new Type[0];
697 if (allowChangeTarget)
699 interfaces = new Type[] {typeof (IChangeProxyTarget)};
702 NestedClassEmitter nested =
703 new NestedClassEmitter(emitter,
704 "Invocation" + methodInfo.Name + "_" + nestedCounter.ToString(),
705 typeof (AbstractInvocation),
706 interfaces);
708 Type[] genTypes = TypeUtil.Union(targetType.GetGenericArguments(), methodInfo.GetGenericArguments());
709 #if DOTNET2
710 nested.CreateGenericParameters(genTypes);
711 #endif
712 // Create the invocation fields
714 FieldReference targetRef = nested.CreateField("target", targetForInvocation);
716 // Create constructor
718 CreateIInvocationConstructor(targetForInvocation, nested, targetRef, version);
720 if (allowChangeTarget)
722 ArgumentReference argument1 = new ArgumentReference(typeof (object));
723 MethodEmitter methodEmitter =
724 nested.CreateMethod("ChangeInvocationTarget", new ReturnReferenceExpression(typeof (void)), MethodAttributes.Public | MethodAttributes.Virtual, argument1);
725 methodEmitter.CodeBuilder.AddStatement(
726 new AssignStatement(targetRef,
727 new ConvertExpression(targetType, argument1.ToExpression())
730 methodEmitter.CodeBuilder.AddStatement(new ReturnStatement());
733 // InvokeMethodOnTarget implementation
735 if (callbackMethod != null)
737 ParameterInfo[] parameters = methodInfo.GetParameters();
739 CreateIInvocationInvokeOnTarget(emitter, nested, parameters, targetRef, callbackMethod);
741 else
743 CreateEmptyIInvocationInvokeOnTarget(nested);
746 nested.DefineCustomAttribute(new SerializableAttribute());
748 return nested;
751 protected void CreateIInvocationInvokeOnTarget(
752 ClassEmitter targetTypeEmitter,
753 NestedClassEmitter nested,
754 ParameterInfo[] parameters,
755 FieldReference targetField,
756 MethodInfo callbackMethod)
758 const MethodAttributes methodAtts = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual;
760 MethodEmitter method =
761 nested.CreateMethod("InvokeMethodOnTarget", new ReturnReferenceExpression(typeof (void)), methodAtts);
763 Expression[] args = new Expression[parameters.Length];
765 // Idea: instead of grab parameters one by one
766 // we should grab an array
767 Hashtable byRefArguments = new Hashtable();
768 for (int i = 0; i < parameters.Length; i++)
770 ParameterInfo param = parameters[i];
772 Type paramType = param.ParameterType;
774 if (HasGenericParameters(paramType))
776 paramType = paramType.GetGenericTypeDefinition().MakeGenericType(nested.GetGenericArgumentsFor(paramType));
778 else if (paramType.IsGenericParameter)
780 paramType = nested.GetGenericArgument(paramType.Name);
783 if (paramType.IsByRef)
785 LocalReference localReference = method.CodeBuilder.DeclareLocal(paramType.GetElementType());
786 method.CodeBuilder.AddStatement(
787 new AssignStatement(localReference,
788 new ConvertExpression(paramType.GetElementType(),
789 new MethodInvocationExpression(SelfReference.Self,
790 typeof (AbstractInvocation).GetMethod("GetArgumentValue"),
791 new LiteralIntExpression(i)))));
792 ByRefReference byRefReference = new ByRefReference(localReference);
793 args[i] = new ReferenceExpression(byRefReference);
794 byRefArguments[i] = localReference;
796 else
798 args[i] =
799 new ConvertExpression(paramType,
800 new MethodInvocationExpression(SelfReference.Self,
801 typeof (AbstractInvocation).GetMethod("GetArgumentValue"),
802 new LiteralIntExpression(i)));
806 MethodInvocationExpression baseMethodInvExp;
808 if (callbackMethod.IsGenericMethod)
810 callbackMethod = callbackMethod.MakeGenericMethod(nested.GetGenericArgumentsFor(callbackMethod));
813 baseMethodInvExp = new MethodInvocationExpression(targetField, callbackMethod, args);
815 LocalReference ret_local = null;
817 if (callbackMethod.ReturnType != typeof (void))
819 if (callbackMethod.ReturnType.IsGenericParameter)
821 ret_local = method.CodeBuilder.DeclareLocal(nested.GetGenericArgument(callbackMethod.ReturnType.Name));
823 else if (HasGenericParameters(callbackMethod.ReturnType))
825 ret_local =
826 method.CodeBuilder.DeclareLocal(
827 callbackMethod.ReturnType.GetGenericTypeDefinition().MakeGenericType(
828 nested.GetGenericArgumentsFor(callbackMethod.ReturnType)));
830 else
832 ret_local = method.CodeBuilder.DeclareLocal(callbackMethod.ReturnType);
835 method.CodeBuilder.AddStatement(new AssignStatement(ret_local, baseMethodInvExp));
837 else
839 method.CodeBuilder.AddStatement(new ExpressionStatement(baseMethodInvExp));
842 foreach (DictionaryEntry byRefArgument in byRefArguments)
844 int index = (int) byRefArgument.Key;
845 LocalReference localReference = (LocalReference) byRefArgument.Value;
846 method.CodeBuilder.AddStatement(
847 new ExpressionStatement(
848 new MethodInvocationExpression(SelfReference.Self,
849 typeof (AbstractInvocation).GetMethod("SetArgumentValue"),
850 new LiteralIntExpression(index),
851 new ConvertExpression(typeof(object),localReference.Type,new ReferenceExpression(localReference)))
855 if (callbackMethod.ReturnType != typeof (void))
857 MethodInvocationExpression setRetVal =
858 new MethodInvocationExpression(SelfReference.Self,
859 typeof (AbstractInvocation).GetMethod("set_ReturnValue"),
860 new ConvertExpression(typeof (object), ret_local.Type, ret_local.ToExpression()));
862 method.CodeBuilder.AddStatement(new ExpressionStatement(setRetVal));
865 method.CodeBuilder.AddStatement(new ReturnStatement());
868 protected void CreateEmptyIInvocationInvokeOnTarget(NestedClassEmitter nested)
870 const MethodAttributes methodAtts = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual;
872 MethodEmitter method =
873 nested.CreateMethod("InvokeMethodOnTarget", new ReturnReferenceExpression(typeof (void)), methodAtts);
875 // TODO: throw exception
877 String message =
878 String.Format("This is a DynamicProxy2 error: the interceptor attempted " +
879 "to 'Proceed' for a method without a target, for example, an interface method or an abstract method");
881 method.CodeBuilder.AddStatement(new ThrowStatement(typeof (NotImplementedException), message));
883 method.CodeBuilder.AddStatement(new ReturnStatement());
886 /// <summary>
887 /// Generates the constructor for the nested class that extends
888 /// <see cref="AbstractInvocation"/>
889 /// </summary>
890 /// <param name="targetFieldType"></param>
891 /// <param name="nested"></param>
892 /// <param name="targetField"></param>
893 /// <param name="version"></param>
894 protected void CreateIInvocationConstructor(
895 Type targetFieldType, NestedClassEmitter nested, FieldReference targetField, ConstructorVersion version)
897 ArgumentReference cArg0 = new ArgumentReference(targetFieldType);
898 ArgumentReference cArg1 = new ArgumentReference(typeof (IInterceptor[]));
899 ArgumentReference cArg2 = new ArgumentReference(typeof (Type));
900 ArgumentReference cArg3 = new ArgumentReference(typeof (MethodInfo));
901 ArgumentReference cArg4 = null;
902 ArgumentReference cArg6 = new ArgumentReference(typeof (object));
904 if (version == ConstructorVersion.WithTargetMethod)
906 cArg4 = new ArgumentReference(typeof (MethodInfo));
909 ArgumentReference cArg5 = new ArgumentReference(typeof (object[]));
911 ConstructorEmitter constructor;
913 if (cArg4 == null)
915 constructor = nested.CreateConstructor(cArg0, cArg1, cArg2, cArg3, cArg5, cArg6);
917 else
919 constructor = nested.CreateConstructor(cArg0, cArg1, cArg2, cArg3, cArg4, cArg5, cArg6);
922 constructor.CodeBuilder.AddStatement(new AssignStatement(targetField, cArg0.ToExpression()));
924 if (cArg4 == null)
926 constructor.CodeBuilder.InvokeBaseConstructor(Constants.AbstractInvocationConstructorWithoutTargetMethod,
927 cArg0,
928 cArg6,
929 cArg1,
930 cArg2,
931 cArg3,
932 cArg5);
934 else
936 constructor.CodeBuilder.InvokeBaseConstructor(Constants.AbstractInvocationConstructorWithTargetMethod,
937 cArg0,
938 cArg6,
939 cArg1,
940 cArg2,
941 cArg3,
942 cArg4,
943 cArg5);
946 constructor.CodeBuilder.AddStatement(new ReturnStatement());
949 #endregion
951 #region Custom Attribute handling
953 protected void ReplicateNonInheritableAttributes(Type targetType, ClassEmitter emitter)
955 object[] attrs = targetType.GetCustomAttributes(false);
957 foreach (Attribute attribute in attrs)
959 if (IsInheritable(attribute)) continue;
961 emitter.DefineCustomAttribute(attribute);
965 protected void ReplicateNonInheritableAttributes(MethodInfo method, MethodEmitter emitter)
967 object[] attrs = method.GetCustomAttributes(false);
969 foreach (Attribute attribute in attrs)
971 if (IsInheritable(attribute)) continue;
973 emitter.DefineCustomAttribute(attribute);
977 #endregion
979 #region Type tokens related operations
981 protected void GenerateConstructors(ClassEmitter emitter, Type baseType, params FieldReference[] fields)
983 ConstructorInfo[] constructors =
984 baseType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
986 foreach (ConstructorInfo constructor in constructors)
988 if (constructor.IsPrivate) continue;
990 GenerateConstructor(emitter, constructor, fields);
994 protected ConstructorEmitter GenerateStaticConstructor(ClassEmitter emitter)
996 return emitter.CreateTypeConstructor();
999 /// <summary>
1000 /// Improvement: this cache should be static. We should generate a
1001 /// type constructor instead
1002 /// </summary>
1003 protected void CreateInitializeCacheMethodBody(
1004 Type targetType, MethodInfo[] methods, ClassEmitter classEmitter, ConstructorEmitter typeInitializerConstructor)
1006 typeTokenField = classEmitter.CreateStaticField("typeTokenCache", typeof (Type));
1008 typeInitializerConstructor.CodeBuilder.AddStatement(
1009 new AssignStatement(typeTokenField, new TypeTokenExpression(targetType)));
1011 CacheMethodTokens(classEmitter, methods, typeInitializerConstructor);
1014 protected void CacheMethodTokens(
1015 ClassEmitter classEmitter, MethodInfo[] methods, ConstructorEmitter typeInitializerConstructor)
1017 foreach (MethodInfo method in methods)
1019 // Aparently we cannot cache generic methods
1020 if (method.IsGenericMethod) continue;
1022 AddFieldToCacheMethodTokenAndStatementsToInitialize(method, typeInitializerConstructor, classEmitter);
1026 protected void AddFieldToCacheMethodTokenAndStatementsToInitialize(
1027 MethodInfo method, ConstructorEmitter typeInitializerConstructor, ClassEmitter classEmitter)
1029 if (!method2TokenField.ContainsKey(method))
1031 FieldReference fieldCache =
1032 classEmitter.CreateStaticField("tokenCache" + fieldCount++, typeof (MethodInfo));
1034 method2TokenField.Add(method, fieldCache);
1036 typeInitializerConstructor.CodeBuilder.AddStatement(
1037 new AssignStatement(fieldCache, new MethodTokenExpression(method)));
1041 protected void CompleteInitCacheMethod(ConstructorCodeBuilder constCodeBuilder)
1043 constCodeBuilder.AddStatement(new ReturnStatement());
1046 protected void AddDefaultInterfaces(IList interfaceList)
1048 if (!interfaceList.Contains(typeof (IProxyTargetAccessor)))
1050 interfaceList.Add(typeof (IProxyTargetAccessor));
1054 protected void ImplementProxyTargetAccessor(Type targetType, ClassEmitter emitter)
1056 MethodAttributes attributes = MethodAttributes.Virtual | MethodAttributes.Public;
1058 MethodEmitter methodEmitter =
1059 emitter.CreateMethod("DynProxyGetTarget", attributes, new ReturnReferenceExpression(typeof (object)));
1061 methodEmitter.CodeBuilder.AddStatement(
1062 new ReturnStatement(new ConvertExpression(typeof (object), targetType, GetProxyTargetReference().ToExpression())));
1065 #endregion
1067 #region Utility methods
1069 protected void CollectMethodsToProxy(ArrayList methodList, Type type, bool onlyVirtuals)
1071 CollectMethods(methodList, type, onlyVirtuals);
1073 if (type.IsInterface)
1075 Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
1077 foreach (Type interType in typeChain)
1079 CollectMethods(methodList, interType, onlyVirtuals);
1084 protected void CollectPropertyMethodsToProxy(
1085 ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter, out PropertyToGenerate[] propsToGenerate)
1087 if (type.IsInterface)
1089 ArrayList toGenerateList = new ArrayList();
1091 toGenerateList.AddRange(CollectProperties(methodList, type, onlyVirtuals, emitter));
1093 Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
1095 foreach (Type interType in typeChain)
1097 toGenerateList.AddRange(CollectProperties(methodList, interType, onlyVirtuals, emitter));
1100 propsToGenerate = (PropertyToGenerate[]) toGenerateList.ToArray(typeof (PropertyToGenerate));
1102 else
1104 propsToGenerate = CollectProperties(methodList, type, onlyVirtuals, emitter);
1108 /// <summary>
1109 /// Performs some basic screening and invokes the <see cref="IProxyGenerationHook"/>
1110 /// to select methods.
1111 /// </summary>
1112 /// <param name="method"></param>
1113 /// <param name="onlyVirtuals"></param>
1114 /// <returns></returns>
1115 protected bool AcceptMethod(MethodInfo method, bool onlyVirtuals)
1117 // we can never intercept a sealed (final) method
1118 if (method.IsFinal)
1119 return false;
1121 bool isInternalsAndNotVisibleToDynamicProxy = InternalsHelper.IsInternal(method)
1122 && InternalsHelper.IsInternalToDynamicProxy(method.DeclaringType.Assembly) == false;
1123 if (isInternalsAndNotVisibleToDynamicProxy)
1124 return false;
1126 if (onlyVirtuals && !method.IsVirtual)
1128 if (method.DeclaringType != typeof (object) && method.DeclaringType != typeof (MarshalByRefObject))
1130 generationHook.NonVirtualMemberNotification(targetType, method);
1133 return false;
1136 //can only proxy methods that are public or protected (or internals that have already been checked above)
1137 if ((method.IsPublic || method.IsFamily || method.IsAssembly) == false)
1138 return false;
1140 if (method.DeclaringType == typeof (object))
1142 return false;
1144 if (method.DeclaringType == typeof (MarshalByRefObject))
1146 return false;
1149 return generationHook.ShouldInterceptMethod(targetType, method);
1153 protected MethodInfo[] CollectMethodsAndProperties(
1154 ClassEmitter emitter,
1155 Type targetType,
1156 out PropertyToGenerate[] propsToGenerate,
1157 out EventToGenerate[] eventsToGenerate)
1159 bool onlyVirtuals = CanOnlyProxyVirtual();
1161 return CollectMethodsAndProperties(emitter, targetType, onlyVirtuals, out propsToGenerate, out eventsToGenerate);
1164 protected MethodInfo[] CollectMethodsAndProperties(
1165 ClassEmitter emitter,
1166 Type targetType,
1167 bool onlyVirtuals,
1168 out PropertyToGenerate[] propsToGenerate,
1169 out EventToGenerate[] eventsToGenerate)
1171 ArrayList methodsList = new ArrayList();
1173 CollectMethodsToProxy(methodsList, targetType, onlyVirtuals);
1174 CollectPropertyMethodsToProxy(methodsList, targetType, onlyVirtuals, emitter, out propsToGenerate);
1175 CollectEventMethodsToProxy(methodsList, targetType, onlyVirtuals, emitter, out eventsToGenerate);
1176 return (MethodInfo[]) methodsList.ToArray(typeof (MethodInfo));
1179 private void CollectEventMethodsToProxy(
1180 ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter, out EventToGenerate[] eventsToGenerates)
1182 if (type.IsInterface)
1184 ArrayList toGenerateList = new ArrayList();
1186 toGenerateList.AddRange(CollectEvents(methodList, type, onlyVirtuals, emitter));
1188 Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
1190 foreach (Type interType in typeChain)
1192 toGenerateList.AddRange(CollectEvents(methodList, interType, onlyVirtuals, emitter));
1195 eventsToGenerates = (EventToGenerate[]) toGenerateList.ToArray(typeof (EventToGenerate));
1197 else
1199 eventsToGenerates = CollectEvents(methodList, type, onlyVirtuals, emitter);
1203 /// <summary>
1204 /// Checks if the method is public or protected.
1205 /// </summary>
1206 /// <param name="method"></param>
1207 /// <returns></returns>
1208 private bool IsAccessible(MethodInfo method)
1210 return method.IsPublic || method.IsFamily || method.IsFamilyAndAssembly || method.IsFamilyOrAssembly;
1213 private bool HasGenericParameters(Type type)
1215 if (type.IsGenericType)
1217 Type[] genTypes = type.GetGenericArguments();
1219 foreach (Type genType in genTypes)
1221 if (genType.IsGenericParameter)
1223 return true;
1228 return false;
1231 private bool NoFilter(Type type, object filterCriteria)
1233 return true;
1236 private void CollectMethods(ArrayList methodsList, Type type, bool onlyVirtuals)
1238 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
1240 MethodInfo[] methods = type.GetMethods(flags);
1242 foreach (MethodInfo method in methods)
1244 if (method.IsFinal)
1246 AddMethodToGenerateNewSlot(method);
1247 continue;
1250 if (method.IsSpecialName)
1252 continue;
1255 if (AcceptMethod(method, onlyVirtuals))
1257 methodsList.Add(method);
1262 private EventToGenerate[] CollectEvents(ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter)
1264 ArrayList toGenerateList = new ArrayList();
1266 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
1268 EventInfo[] events = type.GetEvents(flags);
1270 foreach (EventInfo eventInfo in events)
1272 MethodInfo addMethod = eventInfo.GetAddMethod(true);
1273 MethodInfo removeMethod = eventInfo.GetRemoveMethod(true);
1274 bool shouldGenerate = false;
1276 if (addMethod != null && IsAccessible(addMethod) && AcceptMethod(addMethod, onlyVirtuals))
1278 shouldGenerate = true;
1279 methodList.Add(addMethod);
1282 if (removeMethod != null && IsAccessible(removeMethod) && AcceptMethod(removeMethod, onlyVirtuals))
1284 shouldGenerate = true;
1285 methodList.Add(removeMethod);
1288 if (shouldGenerate == false)
1289 continue;
1291 EventAttributes atts = ObtainEventAttributes(eventInfo);
1293 EventEmitter eventEmitter = emitter.CreateEvent(eventInfo.Name, atts, eventInfo.EventHandlerType);
1295 EventToGenerate eventToGenerate = new EventToGenerate(eventEmitter, addMethod, removeMethod, atts);
1297 toGenerateList.Add(eventToGenerate);
1300 return (EventToGenerate[]) toGenerateList.ToArray(typeof (EventToGenerate));
1303 private EventAttributes ObtainEventAttributes(EventInfo eventInfo)
1305 return EventAttributes.None;
1308 private PropertyToGenerate[] CollectProperties(
1309 ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter)
1311 ArrayList toGenerateList = new ArrayList();
1313 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
1315 PropertyInfo[] properties = type.GetProperties(flags);
1317 foreach (PropertyInfo propInfo in properties)
1319 bool generateReadable, generateWritable;
1321 generateWritable = generateReadable = false;
1323 MethodInfo setMethod, getMethod;
1324 setMethod = getMethod = null;
1326 if (propInfo.CanRead)
1328 getMethod = propInfo.GetGetMethod(true);
1330 if (IsAccessible(getMethod) && AcceptMethod(getMethod, onlyVirtuals))
1332 methodList.Add(getMethod);
1333 generateReadable = true;
1337 if (propInfo.CanWrite)
1339 setMethod = propInfo.GetSetMethod(true);
1341 if (IsAccessible(setMethod) && AcceptMethod(setMethod, onlyVirtuals))
1343 methodList.Add(setMethod);
1344 generateWritable = true;
1348 if (!generateWritable && !generateReadable)
1350 continue;
1353 PropertyAttributes atts = ObtainPropertyAttributes(propInfo);
1355 PropertyEmitter propEmitter = emitter.CreateProperty(propInfo.Name, atts, propInfo.PropertyType);
1357 PropertyToGenerate propToGenerate =
1358 new PropertyToGenerate(generateReadable, generateWritable, propEmitter, getMethod, setMethod);
1360 toGenerateList.Add(propToGenerate);
1363 return (PropertyToGenerate[]) toGenerateList.ToArray(typeof (PropertyToGenerate));
1366 private bool IsInheritable(Attribute attribute)
1368 object[] attrs = attribute.GetType().GetCustomAttributes(typeof (AttributeUsageAttribute), true);
1370 if (attrs.Length != 0)
1372 AttributeUsageAttribute usage = (AttributeUsageAttribute) attrs[0];
1374 return usage.Inherited;
1377 return true;
1380 #endregion
1382 protected void AddMethodToGenerateNewSlot(MethodInfo method)
1384 generateNewSlot.Add(method);
1387 /// <summary>
1388 /// Checks if the method has the same signature as a method that was marked as
1389 /// one that should generate a new vtable slot.
1390 /// </summary>
1391 protected bool ShouldCreateNewSlot(MethodInfo method)
1393 string methodStr = method.ToString();
1394 foreach (MethodInfo candidate in generateNewSlot)
1396 if (candidate.ToString() == methodStr)
1397 return true;
1399 return false;
1402 protected virtual void ImplementGetObjectData(ClassEmitter emitter, FieldReference interceptorsField, Type[] interfaces)
1404 /*// To prevent re-implementation of this interface.
1405 _generated.Add(typeof(ISerializable));*/
1407 if (interfaces == null)
1408 interfaces = new Type[0];
1410 Type[] get_type_args = new Type[] {typeof (String), typeof (bool), typeof (bool)};
1411 Type[] key_and_object = new Type[] {typeof (String), typeof (Object)};
1412 MethodInfo addValueMethod = typeof (SerializationInfo).GetMethod("AddValue", key_and_object);
1414 ArgumentReference arg1 = new ArgumentReference(typeof (SerializationInfo));
1415 ArgumentReference arg2 = new ArgumentReference(typeof (StreamingContext));
1416 MethodEmitter getObjectData = emitter.CreateMethod("GetObjectData",
1417 new ReturnReferenceExpression(typeof (void)), arg1, arg2);
1419 LocalReference typeLocal = getObjectData.CodeBuilder.DeclareLocal(typeof (Type));
1421 getObjectData.CodeBuilder.AddStatement(new AssignStatement(
1422 typeLocal,
1423 new MethodInvocationExpression(null,
1424 typeof (Type).GetMethod("GetType",
1425 get_type_args),
1426 new ConstReference(
1427 typeof (ProxyObjectReference).
1428 AssemblyQualifiedName).ToExpression(),
1429 new ConstReference(1).ToExpression(),
1430 new ConstReference(0).ToExpression())));
1432 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1433 new MethodInvocationExpression(
1434 arg1, typeof (SerializationInfo).GetMethod("SetType"),
1435 typeLocal.ToExpression())));
1437 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1438 new MethodInvocationExpression(arg1, addValueMethod,
1439 new ConstReference("__interceptors").
1440 ToExpression(),
1441 interceptorsField.ToExpression())));
1443 LocalReference interfacesLocal =
1444 getObjectData.CodeBuilder.DeclareLocal(typeof (String[]));
1446 getObjectData.CodeBuilder.AddStatement(
1447 new AssignStatement(interfacesLocal,
1448 new NewArrayExpression(interfaces.Length, typeof (String))));
1450 for (int i = 0; i < interfaces.Length; i++)
1452 getObjectData.CodeBuilder.AddStatement(new AssignArrayStatement(
1453 interfacesLocal, i,
1454 new ConstReference(interfaces[i].AssemblyQualifiedName).ToExpression()));
1457 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1458 new MethodInvocationExpression(arg1, addValueMethod,
1459 new ConstReference("__interfaces").
1460 ToExpression(),
1461 interfacesLocal.ToExpression())));
1463 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1464 new MethodInvocationExpression(arg1, addValueMethod,
1465 new ConstReference("__baseType").
1466 ToExpression(),
1467 new TypeTokenExpression(emitter.BaseType))));
1469 CustomizeGetObjectData(getObjectData.CodeBuilder, arg1, arg2);
1471 getObjectData.CodeBuilder.AddStatement(new ReturnStatement());
1474 protected virtual void CustomizeGetObjectData(
1475 AbstractCodeBuilder codebuilder, ArgumentReference arg1,
1476 ArgumentReference arg2)
1481 protected bool VerifyIfBaseImplementsGetObjectData(Type baseType)
1483 // If base type implements ISerializable, we have to make sure
1484 // the GetObjectData is marked as virtual
1486 if (typeof (ISerializable).IsAssignableFrom(baseType))
1488 MethodInfo getObjectDataMethod = baseType.GetMethod("GetObjectData",
1489 new Type[] {typeof (SerializationInfo), typeof (StreamingContext)});
1491 if (getObjectDataMethod == null) //explicit interface implementation
1493 return false;
1496 if (!getObjectDataMethod.IsVirtual || getObjectDataMethod.IsFinal)
1498 String message = String.Format("The type {0} implements ISerializable, but GetObjectData is not marked as virtual",
1499 baseType.FullName);
1500 throw new ArgumentException(message);
1503 methodsToSkip.Add(getObjectDataMethod);
1505 serializationConstructor = baseType.GetConstructor(
1506 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
1507 null,
1508 new Type[] {typeof (SerializationInfo), typeof (StreamingContext)},
1509 null);
1511 if (serializationConstructor == null)
1513 String message = String.Format("The type {0} implements ISerializable, but failed to provide a deserialization constructor", baseType.FullName);
1514 throw new ArgumentException(message);
1517 return true;
1519 return false;