Adding support for serialization / remoting.
[castle.git] / Tools / Castle.DynamicProxy2 / Castle.DynamicProxy / Generators / BaseProxyGenerator.cs
blob3d15d464cd4706aa8b44b00102baf59766b5641d
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 namespace Castle.DynamicProxy.Generators
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Reflection;
21 using System.Reflection.Emit;
22 using Castle.Core.Interceptor;
23 using Castle.DynamicProxy.Generators.Emitters;
24 using Castle.DynamicProxy.Generators.Emitters.CodeBuilders;
25 using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
26 using System.Runtime.Serialization;
27 using Castle.DynamicProxy.Serialization;
29 public enum ConstructorVersion
31 WithTargetMethod,
32 WithoutTargetMethod
35 /// <summary>
36 /// Base class that exposes the common functionalities
37 /// to proxy generation.
38 /// </summary>
39 /// <remarks>
40 /// TODO:
41 /// - Add events so people can hook into the proxy generation and change the generated code
42 /// - Add serialization support
43 /// - Add Xml serialization support
44 /// - Allow one to specify the base class for interface proxies
45 /// - Use the interceptor selector if provided
46 /// - Expose parameters of non-parameterless constructors on the generated constructor
47 /// - Add tests and fixes for 'leaking this' problem
48 /// - Mixin support
49 /// </remarks>
50 [CLSCompliant(false)]
51 public abstract class BaseProxyGenerator
53 private static MethodInfo invocation_getArgumentsMethod = typeof(AbstractInvocation).GetMethod("get_Arguments");
55 private readonly ModuleScope scope;
56 private int nestedCounter, callbackCounter;
57 private int fieldCount = 1;
58 private FieldReference typeTokenField;
59 private Dictionary<MethodInfo, FieldReference> method2TokenField = new Dictionary<MethodInfo, FieldReference>();
60 private IList generateNewSlot = new ArrayList();
61 protected IList methodsToSkip = new ArrayList();
63 protected readonly Type targetType;
64 protected IProxyGenerationHook generationHook;
65 // protected MethodEmitter initCacheMethod;
66 protected ConstructorInfo serializationConstructor;
68 protected BaseProxyGenerator(ModuleScope scope, Type targetType)
70 this.scope = scope;
71 this.targetType = targetType;
74 protected ModuleScope Scope
76 get { return scope; }
79 protected virtual ClassEmitter BuildClassEmitter(String typeName, Type parentType, IList interfaceList)
81 Type[] interfaces = new Type[interfaceList.Count];
83 interfaceList.CopyTo(interfaces, 0);
85 return BuildClassEmitter(typeName, parentType, interfaces);
88 protected virtual ClassEmitter BuildClassEmitter(String typeName, Type parentType, Type[] interfaces)
90 if (interfaces == null)
92 interfaces = new Type[0];
95 return new ClassEmitter(Scope, typeName, parentType, interfaces, true);
98 /// <summary>
99 /// Used by dinamically implement <see cref="Core.Interceptor.IProxyTargetAccessor"/>
100 /// </summary>
101 /// <returns></returns>
102 protected abstract Reference GetProxyTargetReference();
104 protected abstract bool CanOnlyProxyVirtual();
106 #region Cache related
108 protected Type GetFromCache(CacheKey key)
110 return scope.GetFromCache(key);
113 protected void AddToCache(CacheKey key, Type type)
115 scope.RegisterInCache(key, type);
118 #endregion
120 protected MethodEmitter CreateProxiedMethod(
121 Type targetType,
122 MethodInfo method,
123 ClassEmitter emitter,
124 NestedClassEmitter invocationImpl,
125 FieldReference interceptorsField,
126 Reference targetRef,
127 ConstructorVersion version,
128 MethodInfo methodOnTarget)
130 MethodAttributes atts = ObtainMethodAttributes(method);
131 MethodEmitter methodEmitter = emitter.CreateMethod(method.Name, atts);
133 return
134 ImplementProxiedMethod(targetType,
135 methodEmitter,
136 method,
137 emitter,
138 invocationImpl,
139 interceptorsField,
140 targetRef,
141 version,
142 methodOnTarget);
145 protected void ImplementBlankInterface(
146 Type targetType,
147 Type _interface,
148 ClassEmitter emitter,
149 FieldReference interceptorsField,
150 ConstructorEmitter typeInitializerConstructor)
152 PropertyToGenerate[] propsToGenerate;
153 EventToGenerate[] eventsToGenerate;
154 MethodInfo[] methods = CollectMethodsAndProperties(emitter, _interface, false, out propsToGenerate, out eventsToGenerate);
156 Dictionary<MethodInfo, NestedClassEmitter> method2Invocation = new Dictionary<MethodInfo, NestedClassEmitter>();
158 foreach (MethodInfo method in methods)
160 AddFieldToCacheMethodTokenAndStatementsToInitialize(method, typeInitializerConstructor, emitter);
162 method2Invocation[method] =
163 BuildInvocationNestedType(emitter,
164 targetType,
165 emitter.TypeBuilder,
166 method,
167 null,
168 ConstructorVersion.WithoutTargetMethod);
171 foreach (MethodInfo method in methods)
173 if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")))
175 continue;
178 NestedClassEmitter nestedClass = method2Invocation[method];
180 MethodEmitter newProxiedMethod =
181 CreateProxiedMethod(targetType,
182 method,
183 emitter,
184 nestedClass,
185 interceptorsField,
186 SelfReference.Self,
187 ConstructorVersion.WithoutTargetMethod,
188 null);
190 ReplicateNonInheritableAttributes(method, newProxiedMethod);
193 foreach (PropertyToGenerate propToGen in propsToGenerate)
195 if (propToGen.CanRead)
197 NestedClassEmitter nestedClass = method2Invocation[propToGen.GetMethod];
199 MethodAttributes atts = ObtainMethodAttributes(propToGen.GetMethod);
201 MethodEmitter getEmitter = propToGen.Emitter.CreateGetMethod(atts);
203 ImplementProxiedMethod(targetType,
204 getEmitter,
205 propToGen.GetMethod,
206 emitter,
207 nestedClass,
208 interceptorsField,
209 SelfReference.Self,
210 ConstructorVersion.WithoutTargetMethod,
211 null);
213 ReplicateNonInheritableAttributes(propToGen.GetMethod, getEmitter);
216 if (propToGen.CanWrite)
218 NestedClassEmitter nestedClass = method2Invocation[propToGen.GetMethod];
220 MethodAttributes atts = ObtainMethodAttributes(propToGen.SetMethod);
222 MethodEmitter setEmitter = propToGen.Emitter.CreateSetMethod(atts);
224 ImplementProxiedMethod(targetType,
225 setEmitter,
226 propToGen.SetMethod,
227 emitter,
228 nestedClass,
229 interceptorsField,
230 SelfReference.Self,
231 ConstructorVersion.WithoutTargetMethod,
232 null);
234 ReplicateNonInheritableAttributes(propToGen.SetMethod, setEmitter);
238 foreach (EventToGenerate eventToGenerate in eventsToGenerate)
240 NestedClassEmitter add_nestedClass = method2Invocation[eventToGenerate.AddMethod];
242 MethodAttributes add_atts = ObtainMethodAttributes(eventToGenerate.AddMethod);
244 MethodEmitter addEmitter = eventToGenerate.Emitter.CreateAddMethod(add_atts);
246 ImplementProxiedMethod(targetType,
247 addEmitter,
248 eventToGenerate.AddMethod,
249 emitter,
250 add_nestedClass,
251 interceptorsField,
252 SelfReference.Self,
253 ConstructorVersion.WithoutTargetMethod,
254 null);
256 ReplicateNonInheritableAttributes(eventToGenerate.AddMethod, addEmitter);
258 NestedClassEmitter remove_nestedClass = method2Invocation[eventToGenerate.RemoveMethod];
260 MethodAttributes remove_atts = ObtainMethodAttributes(eventToGenerate.RemoveMethod);
262 MethodEmitter removeEmitter = eventToGenerate.Emitter.CreateRemoveMethod(remove_atts);
264 ImplementProxiedMethod(targetType,
265 removeEmitter,
266 eventToGenerate.RemoveMethod,
267 emitter,
268 remove_nestedClass,
269 interceptorsField,
270 SelfReference.Self,
271 ConstructorVersion.WithoutTargetMethod,
272 null);
274 ReplicateNonInheritableAttributes(eventToGenerate.RemoveMethod, removeEmitter);
278 protected MethodEmitter ImplementProxiedMethod(
279 Type targetType,
280 MethodEmitter methodEmitter,
281 MethodInfo method,
282 ClassEmitter emitter,
283 NestedClassEmitter invocationImpl,
284 FieldReference interceptorsField,
285 Reference targetRef,
286 ConstructorVersion version,
287 MethodInfo methodOnTarget)
289 methodEmitter.CopyParametersAndReturnTypeFrom(method, emitter);
291 TypeReference[] dereferencedArguments = IndirectReference.WrapIfByRef(methodEmitter.Arguments);
293 Type iinvocation = invocationImpl.TypeBuilder;
295 Type[] set1 = null;
296 Type[] set2 = null;
298 if (iinvocation.IsGenericType)
300 // get type generics
301 set1 = targetType.GetGenericArguments();
304 if (method.IsGenericMethod)
306 // get method generics
307 set2 = method.GetGenericArguments();
310 bool isGenericInvocationClass = false;
312 if (set1 != null || set2 != null)
314 iinvocation = iinvocation.MakeGenericType(TypeUtil.Union(set1, set2));
316 isGenericInvocationClass = true;
319 LocalReference invocationImplLocal = methodEmitter.CodeBuilder.DeclareLocal(iinvocation);
321 // TODO: Initialize iinvocation instance
322 // with ordinary arguments and in and out arguments
324 Expression interceptors;
326 // if (useSelector)
328 // TODO: Generate code that checks the return of selector
329 // if no interceptors is returned, should we invoke the base.Method directly?
331 // else
333 interceptors = interceptorsField.ToExpression();
336 Expression typeTokenFieldExp = typeTokenField.ToExpression();
337 Expression methodInfoTokenExp;
339 if (method2TokenField.ContainsKey(method)) // Token is in the cache
341 methodInfoTokenExp = method2TokenField[method].ToExpression();
343 else
345 // Not in the cache: generic method
347 methodInfoTokenExp = new MethodTokenExpression(method);
350 ConstructorInfo constructor = invocationImpl.Constructors[0].Builder;
352 if (isGenericInvocationClass)
354 constructor = TypeBuilder.GetConstructor(iinvocation, invocationImpl.Constructors[0].Builder);
357 NewInstanceExpression newInvocImpl;
359 if (version == ConstructorVersion.WithTargetMethod)
361 Expression methodOnTargetTokenExp;
363 if (method2TokenField.ContainsKey(methodOnTarget)) // Token is in the cache
365 methodOnTargetTokenExp = method2TokenField[methodOnTarget].ToExpression();
367 else
369 // Not in the cache: generic method
371 methodOnTargetTokenExp = new MethodTokenExpression(methodOnTarget);
374 newInvocImpl =
375 new NewInstanceExpression(constructor,
376 targetRef.ToExpression(),
377 interceptors,
378 typeTokenFieldExp,
379 methodOnTargetTokenExp,
380 methodInfoTokenExp,
381 new ReferencesToObjectArrayExpression(dereferencedArguments),
382 SelfReference.Self.ToExpression());
384 else
386 newInvocImpl =
387 new NewInstanceExpression(constructor,
388 targetRef.ToExpression(),
389 interceptors,
390 typeTokenFieldExp,
391 methodInfoTokenExp,
392 new ReferencesToObjectArrayExpression(dereferencedArguments),
393 SelfReference.Self.ToExpression());
396 methodEmitter.CodeBuilder.AddStatement(new AssignStatement(invocationImplLocal, newInvocImpl));
398 methodEmitter.CodeBuilder.AddStatement(
399 new ExpressionStatement(new MethodInvocationExpression(invocationImplLocal, Constants.AbstractInvocationProceed)));
401 CopyOutAndRefParameters(dereferencedArguments, invocationImplLocal, method, methodEmitter);
403 if (method.ReturnType != typeof(void))
405 // Emit code to return with cast from ReturnValue
406 MethodInvocationExpression getRetVal =
407 new MethodInvocationExpression(invocationImplLocal, typeof(AbstractInvocation).GetMethod("get_ReturnValue"));
409 methodEmitter.CodeBuilder.AddStatement(new ReturnStatement(new ConvertExpression(method.ReturnType, getRetVal)));
411 else
413 methodEmitter.CodeBuilder.AddStatement(new ReturnStatement());
416 return methodEmitter;
419 private static void CopyOutAndRefParameters(TypeReference[] dereferencedArguments, LocalReference invocationImplLocal, MethodInfo method, MethodEmitter methodEmitter)
421 ParameterInfo[] parameters = method.GetParameters();
422 bool hasByRefParam = false;
423 for (int i = 0; i < parameters.Length; i++)
425 if (parameters[i].ParameterType.IsByRef)
426 hasByRefParam = true;
428 if (!hasByRefParam)
429 return;//saving the need to create locals if there is no need
430 LocalReference invocationArgs = methodEmitter.CodeBuilder.DeclareLocal(typeof(object[]));
431 methodEmitter.CodeBuilder.AddStatement(
432 new AssignStatement(invocationArgs,
433 new MethodInvocationExpression(invocationImplLocal, invocation_getArgumentsMethod)
436 for (int i = 0; i < parameters.Length; i++)
438 if (parameters[i].ParameterType.IsByRef)
440 methodEmitter.CodeBuilder.AddStatement(
441 new AssignStatement(dereferencedArguments[i],
442 new ConvertExpression(dereferencedArguments[i].Type,
443 new LoadRefArrayElementExpression(i, invocationArgs)
450 protected void GenerateConstructor(ClassEmitter emitter, params FieldReference[] fields)
452 GenerateConstructor(emitter, null, fields);
455 protected void GenerateConstructor(
456 ClassEmitter emitter, ConstructorInfo baseConstructor, params FieldReference[] fields)
458 ArgumentReference[] args;
459 ParameterInfo[] baseConstructorParams = null;
461 if (baseConstructor != null)
463 baseConstructorParams = baseConstructor.GetParameters();
466 if (baseConstructorParams != null && baseConstructorParams.Length != 0)
468 args = new ArgumentReference[fields.Length + baseConstructorParams.Length];
470 int offset = fields.Length;
472 for (int i = offset; i < offset + baseConstructorParams.Length; i++)
474 ParameterInfo paramInfo = baseConstructorParams[i - offset];
475 args[i] = new ArgumentReference(paramInfo.ParameterType);
478 else
480 args = new ArgumentReference[fields.Length];
483 for (int i = 0; i < fields.Length; i++)
485 args[i] = new ArgumentReference(fields[i].Reference.FieldType);
488 ConstructorEmitter constructor = emitter.CreateConstructor(args);
490 for (int i = 0; i < fields.Length; i++)
492 constructor.CodeBuilder.AddStatement(new AssignStatement(fields[i], args[i].ToExpression()));
495 // Invoke base constructor
497 if (baseConstructor != null)
499 ArgumentReference[] slice = new ArgumentReference[baseConstructorParams.Length];
500 Array.Copy(args, fields.Length, slice, 0, baseConstructorParams.Length);
502 constructor.CodeBuilder.InvokeBaseConstructor(baseConstructor, slice);
504 else
506 constructor.CodeBuilder.InvokeBaseConstructor();
509 // Invoke initialize method
511 // constructor.CodeBuilder.AddStatement(
512 // new ExpressionStatement(new MethodInvocationExpression(SelfReference.Self, initCacheMethod)));
514 constructor.CodeBuilder.AddStatement(new ReturnStatement());
517 /// <summary>
518 /// Generates a parameters constructor that initializes the proxy
519 /// state with <see cref="StandardInterceptor"/> just to make it non-null.
520 /// <para>
521 /// This constructor is important to allow proxies to be XML serializable
522 /// </para>
523 /// </summary>
524 protected void GenerateParameterlessConstructor(ClassEmitter emitter, Type baseClass, FieldReference interceptorField)
526 // Check if the type actually has a default constructor
528 ConstructorInfo defaultConstructor = baseClass.GetConstructor(BindingFlags.Public, null, Type.EmptyTypes, null);
530 if (defaultConstructor == null)
532 defaultConstructor = baseClass.GetConstructor(BindingFlags.NonPublic, null, Type.EmptyTypes, null);
534 if (defaultConstructor == null || defaultConstructor.IsPrivate)
536 return;
540 ConstructorEmitter constructor = emitter.CreateConstructor();
542 // initialize fields with an empty interceptor
544 constructor.CodeBuilder.AddStatement(
545 new AssignStatement(interceptorField, new NewArrayExpression(1, typeof(IInterceptor))));
546 constructor.CodeBuilder.AddStatement(
547 new AssignArrayStatement(interceptorField, 0, new NewInstanceExpression(typeof(StandardInterceptor), new Type[0])));
549 // Invoke base constructor
551 constructor.CodeBuilder.InvokeBaseConstructor(defaultConstructor);
553 constructor.CodeBuilder.AddStatement(new ReturnStatement());
556 #region First level attributes
558 protected MethodAttributes ObtainMethodAttributes(MethodInfo method)
560 MethodAttributes atts = MethodAttributes.Virtual;
562 if(ShouldCreateNewSlot(method))
564 atts |= MethodAttributes.NewSlot;
567 if (method.IsPublic)
569 atts |= MethodAttributes.Public;
572 if (method.IsHideBySig)
574 atts |= MethodAttributes.HideBySig;
576 if (InternalsHelper.IsInternal(method) && InternalsHelper.IsInternalToDynamicProxy(method.DeclaringType.Assembly))
578 atts |= MethodAttributes.Assembly;
580 if (method.IsFamilyAndAssembly)
582 atts |= MethodAttributes.FamANDAssem;
584 else if (method.IsFamilyOrAssembly)
586 atts |= MethodAttributes.FamORAssem;
588 else if (method.IsFamily)
590 atts |= MethodAttributes.Family;
593 if (method.Name.StartsWith("set_") || method.Name.StartsWith("get_"))
595 atts |= MethodAttributes.SpecialName;
598 return atts;
601 private PropertyAttributes ObtainPropertyAttributes(PropertyInfo property)
603 PropertyAttributes atts = PropertyAttributes.None;
605 return atts;
608 #endregion
610 protected MethodBuilder CreateCallbackMethod(ClassEmitter emitter, MethodInfo methodInfo, MethodInfo methodOnTarget)
612 MethodInfo targetMethod = methodOnTarget != null ? methodOnTarget : methodInfo;
614 // MethodBuild creation
616 MethodAttributes atts = MethodAttributes.Family;
618 String name = methodInfo.Name + "_callback_" + ++callbackCounter;
620 MethodEmitter callBackMethod = emitter.CreateMethod(name, atts);
622 callBackMethod.CopyParametersAndReturnTypeFrom(targetMethod, emitter);
624 // Generic definition
626 if (targetMethod.IsGenericMethod)
628 targetMethod = targetMethod.MakeGenericMethod(callBackMethod.GenericTypeParams);
631 // Parameters exp
633 Expression[] exps = new Expression[callBackMethod.Arguments.Length];
635 for (int i = 0; i < callBackMethod.Arguments.Length; i++)
637 exps[i] = callBackMethod.Arguments[i].ToExpression();
640 // invocation on base class
642 callBackMethod.CodeBuilder.AddStatement(
643 new ReturnStatement(new MethodInvocationExpression(GetProxyTargetReference(), targetMethod, exps)));
645 return callBackMethod.MethodBuilder;
648 #region IInvocation related
650 /// <summary>
651 /// If callbackMethod is null the InvokeOnTarget implementation
652 /// is just the code to throw an exception
653 /// </summary>
654 /// <param name="emitter"></param>
655 /// <param name="targetType"></param>
656 /// <param name="targetForInvocation"></param>
657 /// <param name="methodInfo"></param>
658 /// <param name="callbackMethod"></param>
659 /// <param name="version"></param>
660 /// <returns></returns>
661 protected NestedClassEmitter BuildInvocationNestedType(
662 ClassEmitter emitter,
663 Type targetType,
664 Type targetForInvocation,
665 MethodInfo methodInfo,
666 MethodInfo callbackMethod,
667 ConstructorVersion version)
669 return BuildInvocationNestedType(emitter, targetType, targetForInvocation, methodInfo, callbackMethod, version, false);
672 /// <summary>
673 /// If callbackMethod is null the InvokeOnTarget implementation
674 /// is just the code to throw an exception
675 /// </summary>
676 /// <param name="emitter"></param>
677 /// <param name="targetType"></param>
678 /// <param name="targetForInvocation"></param>
679 /// <param name="methodInfo"></param>
680 /// <param name="callbackMethod"></param>
681 /// <param name="version"></param>
682 /// <param name="allowChangeTarget">If true the invocation will implement the IChangeProxyTarget interface</param>
683 /// <returns></returns>
684 protected NestedClassEmitter BuildInvocationNestedType(
685 ClassEmitter emitter,
686 Type targetType,
687 Type targetForInvocation,
688 MethodInfo methodInfo,
689 MethodInfo callbackMethod,
690 ConstructorVersion version,
691 bool allowChangeTarget)
693 nestedCounter++;
695 Type[] interfaces = new Type[0];
696 if (allowChangeTarget)
698 interfaces = new Type[] { typeof(IChangeProxyTarget) };
701 NestedClassEmitter nested =
702 new NestedClassEmitter(emitter,
703 "Invocation" + methodInfo.Name + "_" + nestedCounter.ToString(),
704 typeof(AbstractInvocation),
705 interfaces);
707 Type[] genTypes = TypeUtil.Union(targetType.GetGenericArguments(), methodInfo.GetGenericArguments());
709 nested.CreateGenericParameters(genTypes);
711 // Create the invocation fields
713 FieldReference targetRef = nested.CreateField("target", targetForInvocation);
715 // Create constructor
717 CreateIInvocationConstructor(targetForInvocation, nested, targetRef, version);
719 if (allowChangeTarget)
721 ArgumentReference argument1 = new ArgumentReference(typeof(object));
722 MethodEmitter methodEmitter =
723 nested.CreateMethod("ChangeInvocationTarget", new ReturnReferenceExpression(typeof(void)), MethodAttributes.Public | MethodAttributes.Virtual, argument1);
724 methodEmitter.CodeBuilder.AddStatement(
725 new AssignStatement(targetRef,
726 new ConvertExpression(targetType, argument1.ToExpression())
729 methodEmitter.CodeBuilder.AddStatement(new ReturnStatement());
732 // InvokeMethodOnTarget implementation
734 if (callbackMethod != null)
736 ParameterInfo[] parameters = methodInfo.GetParameters();
738 CreateIInvocationInvokeOnTarget(emitter, nested, parameters, targetRef, callbackMethod);
740 else
742 CreateEmptyIInvocationInvokeOnTarget(nested);
745 nested.DefineCustomAttribute(new SerializableAttribute());
747 return nested;
750 protected void CreateIInvocationInvokeOnTarget(
751 ClassEmitter targetTypeEmitter,
752 NestedClassEmitter nested,
753 ParameterInfo[] parameters,
754 FieldReference targetField,
755 MethodInfo callbackMethod)
757 const MethodAttributes methodAtts = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual;
759 MethodEmitter method =
760 nested.CreateMethod("InvokeMethodOnTarget", new ReturnReferenceExpression(typeof(void)), methodAtts);
762 Expression[] args = new Expression[parameters.Length];
764 // Idea: instead of grab parameters one by one
765 // we should grab an array
767 for (int i = 0; i < parameters.Length; i++)
769 ParameterInfo param = parameters[i];
771 Type paramType = param.ParameterType;
773 if (HasGenericParameters(paramType))
775 paramType = paramType.GetGenericTypeDefinition().MakeGenericType(nested.GetGenericArgumentsFor(paramType));
777 else if (paramType.IsGenericParameter)
779 paramType = nested.GetGenericArgument(paramType.Name);
782 args[i] =
783 new ConvertExpression(paramType,
784 new MethodInvocationExpression(SelfReference.Self,
785 typeof(AbstractInvocation).GetMethod("GetArgumentValue"),
786 new LiteralIntExpression(i)));
789 MethodInvocationExpression baseMethodInvExp;
791 if (callbackMethod.IsGenericMethod)
793 callbackMethod = callbackMethod.MakeGenericMethod(nested.GetGenericArgumentsFor(callbackMethod));
796 baseMethodInvExp = new MethodInvocationExpression(targetField, callbackMethod, args);
798 LocalReference ret_local = null;
800 if (callbackMethod.ReturnType != typeof(void))
802 if (callbackMethod.ReturnType.IsGenericParameter)
804 ret_local = method.CodeBuilder.DeclareLocal(nested.GetGenericArgument(callbackMethod.ReturnType.Name));
806 else if (HasGenericParameters(callbackMethod.ReturnType))
808 ret_local =
809 method.CodeBuilder.DeclareLocal(
810 callbackMethod.ReturnType.GetGenericTypeDefinition().MakeGenericType(
811 nested.GetGenericArgumentsFor(callbackMethod.ReturnType)));
813 else
815 ret_local = method.CodeBuilder.DeclareLocal(callbackMethod.ReturnType);
818 method.CodeBuilder.AddStatement(new AssignStatement(ret_local, baseMethodInvExp));
820 else
822 method.CodeBuilder.AddStatement(new ExpressionStatement(baseMethodInvExp));
825 if (callbackMethod.ReturnType != typeof(void))
827 MethodInvocationExpression setRetVal =
828 new MethodInvocationExpression(SelfReference.Self,
829 typeof(AbstractInvocation).GetMethod("set_ReturnValue"),
830 new ConvertExpression(typeof(object), ret_local.Type, ret_local.ToExpression()));
832 method.CodeBuilder.AddStatement(new ExpressionStatement(setRetVal));
835 method.CodeBuilder.AddStatement(new ReturnStatement());
838 protected void CreateEmptyIInvocationInvokeOnTarget(NestedClassEmitter nested)
840 const MethodAttributes methodAtts = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual;
842 MethodEmitter method =
843 nested.CreateMethod("InvokeMethodOnTarget", new ReturnReferenceExpression(typeof(void)), methodAtts);
845 // TODO: throw exception
847 String message =
848 String.Format("This is a DynamicProxy2 error: the interceptor attempted " +
849 "to 'Proceed' for a method without a target, for example, an interface method");
851 method.CodeBuilder.AddStatement(new ThrowStatement(typeof(NotImplementedException), message));
853 method.CodeBuilder.AddStatement(new ReturnStatement());
856 /// <summary>
857 /// Generates the constructor for the nested class that extends
858 /// <see cref="AbstractInvocation"/>
859 /// </summary>
860 /// <param name="targetFieldType"></param>
861 /// <param name="nested"></param>
862 /// <param name="targetField"></param>
863 /// <param name="version"></param>
864 protected void CreateIInvocationConstructor(
865 Type targetFieldType, NestedClassEmitter nested, FieldReference targetField, ConstructorVersion version)
867 ArgumentReference cArg0 = new ArgumentReference(targetFieldType);
868 ArgumentReference cArg1 = new ArgumentReference(typeof(IInterceptor[]));
869 ArgumentReference cArg2 = new ArgumentReference(typeof(Type));
870 ArgumentReference cArg3 = new ArgumentReference(typeof(MethodInfo));
871 ArgumentReference cArg4 = null;
872 ArgumentReference cArg6 = new ArgumentReference(typeof(object));
874 if (version == ConstructorVersion.WithTargetMethod)
876 cArg4 = new ArgumentReference(typeof(MethodInfo));
879 ArgumentReference cArg5 = new ArgumentReference(typeof(object[]));
881 ConstructorEmitter constructor;
883 if (cArg4 == null)
885 constructor = nested.CreateConstructor(cArg0, cArg1, cArg2, cArg3, cArg5, cArg6);
887 else
889 constructor = nested.CreateConstructor(cArg0, cArg1, cArg2, cArg3, cArg4, cArg5, cArg6);
892 constructor.CodeBuilder.AddStatement(new AssignStatement(targetField, cArg0.ToExpression()));
894 if (cArg4 == null)
896 constructor.CodeBuilder.InvokeBaseConstructor(Constants.AbstractInvocationConstructorWithoutTargetMethod,
897 cArg0,
898 cArg6,
899 cArg1,
900 cArg2,
901 cArg3,
902 cArg5);
904 else
906 constructor.CodeBuilder.InvokeBaseConstructor(Constants.AbstractInvocationConstructorWithTargetMethod,
907 cArg0,
908 cArg6,
909 cArg1,
910 cArg2,
911 cArg3,
912 cArg4,
913 cArg5);
916 constructor.CodeBuilder.AddStatement(new ReturnStatement());
919 #endregion
921 #region Custom Attribute handling
923 protected void ReplicateNonInheritableAttributes(Type targetType, ClassEmitter emitter)
925 object[] attrs = targetType.GetCustomAttributes(false);
927 foreach (Attribute attribute in attrs)
929 if (IsInheritable(attribute)) continue;
931 emitter.DefineCustomAttribute(attribute);
935 protected void ReplicateNonInheritableAttributes(MethodInfo method, MethodEmitter emitter)
937 object[] attrs = method.GetCustomAttributes(false);
939 foreach (Attribute attribute in attrs)
941 if (IsInheritable(attribute)) continue;
943 emitter.DefineCustomAttribute(attribute);
947 #endregion
949 #region Type tokens related operations
951 protected void GenerateConstructors(ClassEmitter emitter, Type baseType, params FieldReference[] fields)
953 ConstructorInfo[] constructors =
954 baseType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
956 foreach (ConstructorInfo constructor in constructors)
958 if (constructor.IsPrivate) continue;
960 GenerateConstructor(emitter, constructor, fields);
964 protected ConstructorEmitter GenerateStaticConstructor(ClassEmitter emitter)
966 return emitter.CreateTypeConstructor();
969 /// <summary>
970 /// Improvement: this cache should be static. We should generate a
971 /// type constructor instead
972 /// </summary>
973 protected void CreateInitializeCacheMethodBody(
974 Type targetType, MethodInfo[] methods, ClassEmitter classEmitter, ConstructorEmitter typeInitializerConstructor)
976 typeTokenField = classEmitter.CreateStaticField("typeTokenCache", typeof(Type));
978 typeInitializerConstructor.CodeBuilder.AddStatement(
979 new AssignStatement(typeTokenField, new TypeTokenExpression(targetType)));
981 CacheMethodTokens(classEmitter, methods, typeInitializerConstructor);
984 protected void CacheMethodTokens(
985 ClassEmitter classEmitter, MethodInfo[] methods, ConstructorEmitter typeInitializerConstructor)
987 foreach (MethodInfo method in methods)
989 // Aparently we cannot cache generic methods
990 if (method.IsGenericMethod) continue;
992 AddFieldToCacheMethodTokenAndStatementsToInitialize(method, typeInitializerConstructor, classEmitter);
996 protected void AddFieldToCacheMethodTokenAndStatementsToInitialize(
997 MethodInfo method, ConstructorEmitter typeInitializerConstructor, ClassEmitter classEmitter)
999 FieldReference fieldCache = classEmitter.CreateStaticField("tokenCache" + fieldCount++, typeof(MethodInfo));
1001 if (method2TokenField.ContainsKey(method))
1003 throw new ArgumentException(
1004 String.Format("Duplicate method {0} on type {1}", method.ToString(), method.DeclaringType.FullName));
1007 method2TokenField.Add(method, fieldCache);
1009 typeInitializerConstructor.CodeBuilder.AddStatement(
1010 new AssignStatement(fieldCache, new MethodTokenExpression(method)));
1013 protected void CompleteInitCacheMethod(ConstructorCodeBuilder constCodeBuilder)
1015 constCodeBuilder.AddStatement(new ReturnStatement());
1018 protected void AddDefaultInterfaces(IList interfaceList)
1020 if (!interfaceList.Contains(typeof(IProxyTargetAccessor)))
1022 interfaceList.Add(typeof(IProxyTargetAccessor));
1026 protected void ImplementProxyTargetAccessor(Type targetType, ClassEmitter emitter)
1028 MethodAttributes attributes = MethodAttributes.Virtual | MethodAttributes.Public;
1030 MethodEmitter methodEmitter =
1031 emitter.CreateMethod("DynProxyGetTarget", attributes, new ReturnReferenceExpression(typeof(object)));
1033 methodEmitter.CodeBuilder.AddStatement(
1034 new ReturnStatement(new ConvertExpression(typeof(object), targetType, GetProxyTargetReference().ToExpression())));
1037 #endregion
1039 #region Utility methods
1041 protected void CollectMethodsToProxy(ArrayList methodList, Type type, bool onlyVirtuals)
1043 CollectMethods(methodList, type, onlyVirtuals);
1045 if (type.IsInterface)
1047 Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
1049 foreach (Type interType in typeChain)
1051 CollectMethods(methodList, interType, onlyVirtuals);
1056 protected void CollectPropertyMethodsToProxy(
1057 ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter, out PropertyToGenerate[] propsToGenerate)
1059 if (type.IsInterface)
1061 ArrayList toGenerateList = new ArrayList();
1063 toGenerateList.AddRange(CollectProperties(methodList, type, onlyVirtuals, emitter));
1065 Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
1067 foreach (Type interType in typeChain)
1069 toGenerateList.AddRange(CollectProperties(methodList, interType, onlyVirtuals, emitter));
1072 propsToGenerate = (PropertyToGenerate[])toGenerateList.ToArray(typeof(PropertyToGenerate));
1074 else
1076 propsToGenerate = CollectProperties(methodList, type, onlyVirtuals, emitter);
1080 /// <summary>
1081 /// Performs some basic screening and invokes the <see cref="IProxyGenerationHook"/>
1082 /// to select methods.
1083 /// </summary>
1084 /// <param name="method"></param>
1085 /// <param name="onlyVirtuals"></param>
1086 /// <returns></returns>
1087 protected bool AcceptMethod(MethodInfo method, bool onlyVirtuals)
1089 // we can never intercept a sealed (final) method
1090 if (method.IsFinal)
1091 return false;
1093 bool isInternalsAndNotVisibleToDynamicProxy = InternalsHelper.IsInternal(method)
1094 && InternalsHelper.IsInternalToDynamicProxy(method.DeclaringType.Assembly) == false;
1095 if (isInternalsAndNotVisibleToDynamicProxy)
1096 return false;
1098 if (onlyVirtuals && !method.IsVirtual)
1100 if (method.DeclaringType != typeof(object) && method.DeclaringType != typeof(MarshalByRefObject))
1102 generationHook.NonVirtualMemberNotification(targetType, method);
1105 return false;
1108 // TODO: Only protected and public should accepted
1110 if (method.DeclaringType == typeof(object))
1112 return false;
1114 if (method.DeclaringType == typeof(MarshalByRefObject))
1116 return false;
1119 return generationHook.ShouldInterceptMethod(targetType, method);
1123 protected MethodInfo[] CollectMethodsAndProperties(
1124 ClassEmitter emitter,
1125 Type targetType,
1126 out PropertyToGenerate[] propsToGenerate,
1127 out EventToGenerate[] eventsToGenerate)
1129 bool onlyVirtuals = CanOnlyProxyVirtual();
1131 return CollectMethodsAndProperties(emitter, targetType, onlyVirtuals, out propsToGenerate, out eventsToGenerate);
1134 protected MethodInfo[] CollectMethodsAndProperties(
1135 ClassEmitter emitter,
1136 Type targetType,
1137 bool onlyVirtuals,
1138 out PropertyToGenerate[] propsToGenerate,
1139 out EventToGenerate[] eventsToGenerate)
1141 ArrayList methodsList = new ArrayList();
1143 CollectMethodsToProxy(methodsList, targetType, onlyVirtuals);
1144 CollectPropertyMethodsToProxy(methodsList, targetType, onlyVirtuals, emitter, out propsToGenerate);
1145 CollectEventMethodsToProxy(methodsList, targetType, onlyVirtuals, emitter, out eventsToGenerate);
1146 return (MethodInfo[])methodsList.ToArray(typeof(MethodInfo));
1149 private void CollectEventMethodsToProxy(
1150 ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter, out EventToGenerate[] eventsToGenerates)
1152 if (type.IsInterface)
1154 ArrayList toGenerateList = new ArrayList();
1156 toGenerateList.AddRange(CollectEvents(methodList, type, onlyVirtuals, emitter));
1158 Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
1160 foreach (Type interType in typeChain)
1162 toGenerateList.AddRange(CollectEvents(methodList, interType, onlyVirtuals, emitter));
1165 eventsToGenerates = (EventToGenerate[])toGenerateList.ToArray(typeof(EventToGenerate));
1167 else
1169 eventsToGenerates = CollectEvents(methodList, type, onlyVirtuals, emitter);
1173 /// <summary>
1174 /// Checks if the method is public or protected.
1175 /// </summary>
1176 /// <param name="method"></param>
1177 /// <returns></returns>
1178 private bool IsAccessible(MethodInfo method)
1180 return method.IsPublic || method.IsFamily || method.IsFamilyAndAssembly || method.IsFamilyOrAssembly;
1183 private bool HasGenericParameters(Type type)
1185 if (type.IsGenericType)
1187 Type[] genTypes = type.GetGenericArguments();
1189 foreach (Type genType in genTypes)
1191 if (genType.IsGenericParameter)
1193 return true;
1198 return false;
1201 private bool NoFilter(Type type, object filterCriteria)
1203 return true;
1206 private void CollectMethods(ArrayList methodsList, Type type, bool onlyVirtuals)
1208 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
1210 MethodInfo[] methods = type.GetMethods(flags);
1212 foreach (MethodInfo method in methods)
1214 if(method.IsFinal)
1216 AddMethodToGenerateNewSlot(method);
1217 continue;
1220 if (method.IsSpecialName)
1222 continue;
1225 if (AcceptMethod(method, onlyVirtuals))
1227 methodsList.Add(method);
1232 private EventToGenerate[] CollectEvents(ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter)
1234 ArrayList toGenerateList = new ArrayList();
1236 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
1238 EventInfo[] events = type.GetEvents(flags);
1240 foreach (EventInfo eventInfo in events)
1242 MethodInfo addMethod = eventInfo.GetAddMethod();
1243 MethodInfo removeMethod = eventInfo.GetRemoveMethod();
1244 bool shouldGenerate = false;
1246 if (IsAccessible(addMethod) && AcceptMethod(addMethod, onlyVirtuals))
1248 shouldGenerate = true;
1249 methodList.Add(addMethod);
1252 if (IsAccessible(removeMethod) && AcceptMethod(removeMethod, onlyVirtuals))
1254 shouldGenerate = true;
1255 methodList.Add(removeMethod);
1258 if (shouldGenerate == false)
1259 continue;
1261 EventAttributes atts = ObtainEventAttributes(eventInfo);
1263 EventEmitter eventEmitter = emitter.CreateEvent(eventInfo.Name, atts, eventInfo.EventHandlerType);
1265 EventToGenerate eventToGenerate = new EventToGenerate(eventEmitter, addMethod, removeMethod, atts);
1267 toGenerateList.Add(eventToGenerate);
1270 return (EventToGenerate[])toGenerateList.ToArray(typeof(EventToGenerate));
1273 private EventAttributes ObtainEventAttributes(EventInfo eventInfo)
1275 return EventAttributes.None;
1278 private PropertyToGenerate[] CollectProperties(
1279 ArrayList methodList, Type type, bool onlyVirtuals, ClassEmitter emitter)
1281 ArrayList toGenerateList = new ArrayList();
1283 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
1285 PropertyInfo[] properties = type.GetProperties(flags);
1287 foreach (PropertyInfo propInfo in properties)
1289 bool generateReadable, generateWritable;
1291 generateWritable = generateReadable = false;
1293 MethodInfo setMethod, getMethod;
1294 setMethod = getMethod = null;
1296 if (propInfo.CanRead)
1298 getMethod = propInfo.GetGetMethod(true);
1300 if (IsAccessible(getMethod) && AcceptMethod(getMethod, onlyVirtuals))
1302 methodList.Add(getMethod);
1303 generateReadable = true;
1307 if (propInfo.CanWrite)
1309 setMethod = propInfo.GetSetMethod(true);
1311 if (IsAccessible(setMethod) && AcceptMethod(setMethod, onlyVirtuals))
1313 methodList.Add(setMethod);
1314 generateWritable = true;
1318 if (!generateWritable && !generateReadable)
1320 continue;
1323 PropertyAttributes atts = ObtainPropertyAttributes(propInfo);
1325 PropertyEmitter propEmitter = emitter.CreateProperty(propInfo.Name, atts, propInfo.PropertyType);
1327 PropertyToGenerate propToGenerate =
1328 new PropertyToGenerate(generateReadable, generateWritable, propEmitter, getMethod, setMethod);
1330 toGenerateList.Add(propToGenerate);
1333 return (PropertyToGenerate[])toGenerateList.ToArray(typeof(PropertyToGenerate));
1336 private bool IsInheritable(Attribute attribute)
1338 object[] attrs = attribute.GetType().GetCustomAttributes(typeof(AttributeUsageAttribute), true);
1340 if (attrs.Length != 0)
1342 AttributeUsageAttribute usage = (AttributeUsageAttribute)attrs[0];
1344 return usage.Inherited;
1347 return true;
1350 #endregion
1352 protected void AddMethodToGenerateNewSlot(MethodInfo method)
1354 generateNewSlot.Add(method);
1357 /// <summary>
1358 /// Checks if the method has the same signature as a method that was marked as
1359 /// one that should generate a new vtable slot.
1360 /// </summary>
1361 protected bool ShouldCreateNewSlot(MethodInfo method)
1363 string methodStr = method.ToString();
1364 foreach (MethodInfo candidate in generateNewSlot)
1366 if (candidate.ToString() == methodStr)
1367 return true;
1369 return false;
1372 protected virtual void ImplementGetObjectData(ClassEmitter emitter, FieldReference interceptorsField, Type[] interfaces)
1374 /*// To prevent re-implementation of this interface.
1375 _generated.Add(typeof(ISerializable));*/
1377 if(interfaces==null)
1378 interfaces = new Type[0];
1380 Type[] get_type_args = new Type[] {typeof(String), typeof(bool), typeof(bool)};
1381 Type[] key_and_object = new Type[] {typeof(String), typeof(Object)};
1382 MethodInfo addValueMethod = typeof(SerializationInfo).GetMethod("AddValue", key_and_object);
1384 ArgumentReference arg1 = new ArgumentReference(typeof(SerializationInfo));
1385 ArgumentReference arg2 = new ArgumentReference(typeof(StreamingContext));
1386 MethodEmitter getObjectData = emitter.CreateMethod("GetObjectData",
1387 new ReturnReferenceExpression(typeof(void)), arg1, arg2);
1389 LocalReference typeLocal = getObjectData.CodeBuilder.DeclareLocal(typeof(Type));
1391 getObjectData.CodeBuilder.AddStatement(new AssignStatement(
1392 typeLocal,
1393 new MethodInvocationExpression(null,
1394 typeof(Type).GetMethod("GetType",
1395 get_type_args),
1396 new ConstReference(
1397 typeof(ProxyObjectReference).
1398 AssemblyQualifiedName).ToExpression(),
1399 new ConstReference(1).ToExpression(),
1400 new ConstReference(0).ToExpression())));
1402 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1403 new MethodInvocationExpression(
1404 arg1, typeof(SerializationInfo).GetMethod("SetType"),
1405 typeLocal.ToExpression())));
1407 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1408 new MethodInvocationExpression(arg1, addValueMethod,
1409 new ConstReference("__interceptors").
1410 ToExpression(),
1411 interceptorsField.ToExpression())));
1413 LocalReference interfacesLocal =
1414 getObjectData.CodeBuilder.DeclareLocal(typeof(String[]));
1416 getObjectData.CodeBuilder.AddStatement(
1417 new AssignStatement(interfacesLocal,
1418 new NewArrayExpression(interfaces.Length, typeof(String))));
1420 for(int i = 0; i < interfaces.Length; i++)
1422 getObjectData.CodeBuilder.AddStatement(new AssignArrayStatement(
1423 interfacesLocal, i,
1424 new ConstReference(interfaces[i].AssemblyQualifiedName).ToExpression()));
1427 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1428 new MethodInvocationExpression(arg1, addValueMethod,
1429 new ConstReference("__interfaces").
1430 ToExpression(),
1431 interfacesLocal.ToExpression())));
1433 getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
1434 new MethodInvocationExpression(arg1, addValueMethod,
1435 new ConstReference("__baseType").
1436 ToExpression(),
1437 new TypeTokenExpression(emitter.BaseType))));
1439 CustomizeGetObjectData(getObjectData.CodeBuilder, arg1, arg2);
1441 getObjectData.CodeBuilder.AddStatement(new ReturnStatement());
1444 protected virtual void CustomizeGetObjectData(AbstractCodeBuilder codebuilder, ArgumentReference arg1,
1445 ArgumentReference arg2)
1450 protected bool VerifyIfBaseImplementsGetObjectData(Type baseType)
1452 // If base type implements ISerializable, we have to make sure
1453 // the GetObjectData is marked as virtual
1455 if (typeof(ISerializable).IsAssignableFrom(baseType))
1457 MethodInfo getObjectDataMethod = baseType.GetMethod("GetObjectData",
1458 new Type[] { typeof(SerializationInfo), typeof(StreamingContext) });
1460 if (getObjectDataMethod==null)//explicit interface implementation
1462 return false;
1465 if (!getObjectDataMethod.IsVirtual || getObjectDataMethod.IsFinal)
1467 String message = String.Format("The type {0} implements ISerializable, but GetObjectData is not marked as virtual",
1468 baseType.FullName);
1469 throw new ArgumentException(message);
1472 methodsToSkip.Add(getObjectDataMethod);
1474 serializationConstructor = baseType.GetConstructor(
1475 BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
1476 null,
1477 new Type[] { typeof(SerializationInfo), typeof(StreamingContext) },
1478 null);
1480 if (serializationConstructor == null)
1482 String message = String.Format("The type {0} implements ISerializable, but failed to provide a deserialization constructor",
1483 baseType.FullName);
1484 throw new ArgumentException(message);
1487 return true;
1489 return false;