1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
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
36 /// Base class that exposes the common functionalities
37 /// to proxy generation.
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
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
)
71 this.targetType
= targetType
;
74 protected ModuleScope 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);
99 /// Used by dinamically implement <see cref="Core.Interceptor.IProxyTargetAccessor"/>
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
);
120 protected MethodEmitter
CreateProxiedMethod(
123 ClassEmitter emitter
,
124 NestedClassEmitter invocationImpl
,
125 FieldReference interceptorsField
,
127 ConstructorVersion version
,
128 MethodInfo methodOnTarget
)
130 MethodAttributes atts
= ObtainMethodAttributes(method
);
131 MethodEmitter methodEmitter
= emitter
.CreateMethod(method
.Name
, atts
);
134 ImplementProxiedMethod(targetType
,
145 protected void ImplementBlankInterface(
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
,
168 ConstructorVersion
.WithoutTargetMethod
);
171 foreach (MethodInfo method
in methods
)
173 if (method
.IsSpecialName
&& (method
.Name
.StartsWith("get_") || method
.Name
.StartsWith("set_")))
178 NestedClassEmitter nestedClass
= method2Invocation
[method
];
180 MethodEmitter newProxiedMethod
=
181 CreateProxiedMethod(targetType
,
187 ConstructorVersion
.WithoutTargetMethod
,
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
,
210 ConstructorVersion
.WithoutTargetMethod
,
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
,
231 ConstructorVersion
.WithoutTargetMethod
,
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
,
248 eventToGenerate
.AddMethod
,
253 ConstructorVersion
.WithoutTargetMethod
,
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
,
266 eventToGenerate
.RemoveMethod
,
271 ConstructorVersion
.WithoutTargetMethod
,
274 ReplicateNonInheritableAttributes(eventToGenerate
.RemoveMethod
, removeEmitter
);
278 protected MethodEmitter
ImplementProxiedMethod(
280 MethodEmitter methodEmitter
,
282 ClassEmitter emitter
,
283 NestedClassEmitter invocationImpl
,
284 FieldReference interceptorsField
,
286 ConstructorVersion version
,
287 MethodInfo methodOnTarget
)
289 methodEmitter
.CopyParametersAndReturnTypeFrom(method
, emitter
);
291 TypeReference
[] dereferencedArguments
= IndirectReference
.WrapIfByRef(methodEmitter
.Arguments
);
293 Type iinvocation
= invocationImpl
.TypeBuilder
;
298 if (iinvocation
.IsGenericType
)
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
;
328 // TODO: Generate code that checks the return of selector
329 // if no interceptors is returned, should we invoke the base.Method directly?
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();
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();
369 // Not in the cache: generic method
371 methodOnTargetTokenExp
= new MethodTokenExpression(methodOnTarget
);
375 new NewInstanceExpression(constructor
,
376 targetRef
.ToExpression(),
379 methodOnTargetTokenExp
,
381 new ReferencesToObjectArrayExpression(dereferencedArguments
),
382 SelfReference
.Self
.ToExpression());
387 new NewInstanceExpression(constructor
,
388 targetRef
.ToExpression(),
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
)));
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;
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
);
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
);
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());
518 /// Generates a parameters constructor that initializes the proxy
519 /// state with <see cref="StandardInterceptor"/> just to make it non-null.
521 /// This constructor is important to allow proxies to be XML serializable
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
)
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
;
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
;
601 private PropertyAttributes
ObtainPropertyAttributes(PropertyInfo property
)
603 PropertyAttributes atts
= PropertyAttributes
.None
;
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
);
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
651 /// If callbackMethod is null the InvokeOnTarget implementation
652 /// is just the code to throw an exception
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
,
664 Type targetForInvocation
,
665 MethodInfo methodInfo
,
666 MethodInfo callbackMethod
,
667 ConstructorVersion version
)
669 return BuildInvocationNestedType(emitter
, targetType
, targetForInvocation
, methodInfo
, callbackMethod
, version
, false);
673 /// If callbackMethod is null the InvokeOnTarget implementation
674 /// is just the code to throw an exception
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
,
687 Type targetForInvocation
,
688 MethodInfo methodInfo
,
689 MethodInfo callbackMethod
,
690 ConstructorVersion version
,
691 bool allowChangeTarget
)
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
),
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
);
742 CreateEmptyIInvocationInvokeOnTarget(nested
);
745 nested
.DefineCustomAttribute(new SerializableAttribute());
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
);
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
))
809 method
.CodeBuilder
.DeclareLocal(
810 callbackMethod
.ReturnType
.GetGenericTypeDefinition().MakeGenericType(
811 nested
.GetGenericArgumentsFor(callbackMethod
.ReturnType
)));
815 ret_local
= method
.CodeBuilder
.DeclareLocal(callbackMethod
.ReturnType
);
818 method
.CodeBuilder
.AddStatement(new AssignStatement(ret_local
, baseMethodInvExp
));
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
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());
857 /// Generates the constructor for the nested class that extends
858 /// <see cref="AbstractInvocation"/>
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
;
885 constructor
= nested
.CreateConstructor(cArg0
, cArg1
, cArg2
, cArg3
, cArg5
, cArg6
);
889 constructor
= nested
.CreateConstructor(cArg0
, cArg1
, cArg2
, cArg3
, cArg4
, cArg5
, cArg6
);
892 constructor
.CodeBuilder
.AddStatement(new AssignStatement(targetField
, cArg0
.ToExpression()));
896 constructor
.CodeBuilder
.InvokeBaseConstructor(Constants
.AbstractInvocationConstructorWithoutTargetMethod
,
906 constructor
.CodeBuilder
.InvokeBaseConstructor(Constants
.AbstractInvocationConstructorWithTargetMethod
,
916 constructor
.CodeBuilder
.AddStatement(new ReturnStatement());
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
);
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();
970 /// Improvement: this cache should be static. We should generate a
971 /// type constructor instead
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())));
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
));
1076 propsToGenerate
= CollectProperties(methodList
, type
, onlyVirtuals
, emitter
);
1081 /// Performs some basic screening and invokes the <see cref="IProxyGenerationHook"/>
1082 /// to select methods.
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
1093 bool isInternalsAndNotVisibleToDynamicProxy
= InternalsHelper
.IsInternal(method
)
1094 && InternalsHelper
.IsInternalToDynamicProxy(method
.DeclaringType
.Assembly
) == false;
1095 if (isInternalsAndNotVisibleToDynamicProxy
)
1098 if (onlyVirtuals
&& !method
.IsVirtual
)
1100 if (method
.DeclaringType
!= typeof(object) && method
.DeclaringType
!= typeof(MarshalByRefObject
))
1102 generationHook
.NonVirtualMemberNotification(targetType
, method
);
1108 // TODO: Only protected and public should accepted
1110 if (method
.DeclaringType
== typeof(object))
1114 if (method
.DeclaringType
== typeof(MarshalByRefObject
))
1119 return generationHook
.ShouldInterceptMethod(targetType
, method
);
1123 protected MethodInfo
[] CollectMethodsAndProperties(
1124 ClassEmitter emitter
,
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
,
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
));
1169 eventsToGenerates
= CollectEvents(methodList
, type
, onlyVirtuals
, emitter
);
1174 /// Checks if the method is public or protected.
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
)
1201 private bool NoFilter(Type type
, object filterCriteria
)
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
)
1216 AddMethodToGenerateNewSlot(method
);
1220 if (method
.IsSpecialName
)
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)
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
)
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
;
1352 protected void AddMethodToGenerateNewSlot(MethodInfo method
)
1354 generateNewSlot
.Add(method
);
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.
1361 protected bool ShouldCreateNewSlot(MethodInfo method
)
1363 string methodStr
= method
.ToString();
1364 foreach (MethodInfo candidate
in generateNewSlot
)
1366 if (candidate
.ToString() == methodStr
)
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(
1393 new MethodInvocationExpression(null,
1394 typeof(Type
).GetMethod("GetType",
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").
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(
1424 new ConstReference(interfaces
[i
].AssemblyQualifiedName
).ToExpression()));
1427 getObjectData
.CodeBuilder
.AddStatement(new ExpressionStatement(
1428 new MethodInvocationExpression(arg1
, addValueMethod
,
1429 new ConstReference("__interfaces").
1431 interfacesLocal
.ToExpression())));
1433 getObjectData
.CodeBuilder
.AddStatement(new ExpressionStatement(
1434 new MethodInvocationExpression(arg1
, addValueMethod
,
1435 new ConstReference("__baseType").
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
1465 if (!getObjectDataMethod
.IsVirtual
|| getObjectDataMethod
.IsFinal
)
1467 String message
= String
.Format("The type {0} implements ISerializable, but GetObjectData is not marked as virtual",
1469 throw new ArgumentException(message
);
1472 methodsToSkip
.Add(getObjectDataMethod
);
1474 serializationConstructor
= baseType
.GetConstructor(
1475 BindingFlags
.Instance
|BindingFlags
.Public
|BindingFlags
.NonPublic
,
1477 new Type
[] { typeof(SerializationInfo), typeof(StreamingContext) }
,
1480 if (serializationConstructor
== null)
1482 String message
= String
.Format("The type {0} implements ISerializable, but failed to provide a deserialization constructor",
1484 throw new ArgumentException(message
);