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.
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
29 using System
.Collections
.Generic
;
32 public enum ConstructorVersion
39 /// Base class that exposes the common functionalities
40 /// to proxy generation.
44 /// - Use the interceptor selector if provided
45 /// - Add tests and fixes for 'leaking this' problem
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
)
68 this.targetType
= targetType
;
71 protected ModuleScope 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);
96 /// Used by dinamically implement <see cref="Core.Interceptor.IProxyTargetAccessor"/>
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
);
117 protected MethodEmitter
CreateProxiedMethod(
120 ClassEmitter emitter
,
121 NestedClassEmitter invocationImpl
,
122 FieldReference interceptorsField
,
124 ConstructorVersion version
,
125 MethodInfo methodOnTarget
)
127 MethodAttributes atts
= ObtainMethodAttributes(method
);
128 MethodEmitter methodEmitter
= emitter
.CreateMethod(method
.Name
, atts
);
131 ImplementProxiedMethod(targetType
,
142 protected void ImplementBlankInterface(
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
,
165 ConstructorVersion
.WithoutTargetMethod
);
168 foreach (MethodInfo method
in methods
)
170 if (method
.IsSpecialName
&& (method
.Name
.StartsWith("get_") || method
.Name
.StartsWith("set_")))
175 NestedClassEmitter nestedClass
= method2Invocation
[method
];
177 MethodEmitter newProxiedMethod
=
178 CreateProxiedMethod(targetType
,
184 ConstructorVersion
.WithoutTargetMethod
,
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
,
207 ConstructorVersion
.WithoutTargetMethod
,
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
,
228 ConstructorVersion
.WithoutTargetMethod
,
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
,
245 eventToGenerate
.AddMethod
,
250 ConstructorVersion
.WithoutTargetMethod
,
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
,
263 eventToGenerate
.RemoveMethod
,
268 ConstructorVersion
.WithoutTargetMethod
,
271 ReplicateNonInheritableAttributes(eventToGenerate
.RemoveMethod
, removeEmitter
);
275 protected MethodEmitter
ImplementProxiedMethod(
277 MethodEmitter methodEmitter
,
279 ClassEmitter emitter
,
280 NestedClassEmitter invocationImpl
,
281 FieldReference interceptorsField
,
283 ConstructorVersion version
,
284 MethodInfo methodOnTarget
)
286 methodEmitter
.CopyParametersAndReturnTypeFrom(method
, emitter
);
288 TypeReference
[] dereferencedArguments
= IndirectReference
.WrapIfByRef(methodEmitter
.Arguments
);
290 Type iinvocation
= invocationImpl
.TypeBuilder
;
295 if (iinvocation
.IsGenericType
)
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
;
325 // TODO: Generate code that checks the return of selector
326 // if no interceptors is returned, should we invoke the base.Method directly?
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();
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();
366 // Not in the cache: generic method
368 methodOnTargetTokenExp
= new MethodTokenExpression(methodOnTarget
);
372 new NewInstanceExpression(constructor
,
373 targetRef
.ToExpression(),
376 methodOnTargetTokenExp
,
378 new ReferencesToObjectArrayExpression(dereferencedArguments
),
379 SelfReference
.Self
.ToExpression());
384 new NewInstanceExpression(constructor
,
385 targetRef
.ToExpression(),
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
)));
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;
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
);
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
);
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());
516 /// Generates a parameters constructor that initializes the proxy
517 /// state with <see cref="StandardInterceptor"/> just to make it non-null.
519 /// This constructor is important to allow proxies to be XML serializable
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
)
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
;
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
;
599 private PropertyAttributes
ObtainPropertyAttributes(PropertyInfo property
)
601 PropertyAttributes atts
= PropertyAttributes
.None
;
608 protected MethodBuilder
CreateCallbackMethod(ClassEmitter emitter
, MethodInfo methodInfo
, MethodInfo methodOnTarget
)
610 MethodInfo targetMethod
= methodOnTarget
!= null ? methodOnTarget
: methodInfo
;
612 if (targetMethod
.IsAbstract
)
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
);
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
652 /// If callbackMethod is null the InvokeOnTarget implementation
653 /// is just the code to throw an exception
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
,
665 Type targetForInvocation
,
666 MethodInfo methodInfo
,
667 MethodInfo callbackMethod
,
668 ConstructorVersion version
)
670 return BuildInvocationNestedType(emitter
, targetType
, targetForInvocation
, methodInfo
, callbackMethod
, version
, false);
674 /// If callbackMethod is null the InvokeOnTarget implementation
675 /// is just the code to throw an exception
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
,
688 Type targetForInvocation
,
689 MethodInfo methodInfo
,
690 MethodInfo callbackMethod
,
691 ConstructorVersion version
,
692 bool allowChangeTarget
)
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
),
708 Type
[] genTypes
= TypeUtil
.Union(targetType
.GetGenericArguments(), methodInfo
.GetGenericArguments());
710 nested
.CreateGenericParameters(genTypes
);
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
);
743 CreateEmptyIInvocationInvokeOnTarget(nested
);
746 nested
.DefineCustomAttribute(new SerializableAttribute());
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
;
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
))
826 method
.CodeBuilder
.DeclareLocal(
827 callbackMethod
.ReturnType
.GetGenericTypeDefinition().MakeGenericType(
828 nested
.GetGenericArgumentsFor(callbackMethod
.ReturnType
)));
832 ret_local
= method
.CodeBuilder
.DeclareLocal(callbackMethod
.ReturnType
);
835 method
.CodeBuilder
.AddStatement(new AssignStatement(ret_local
, baseMethodInvExp
));
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
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());
887 /// Generates the constructor for the nested class that extends
888 /// <see cref="AbstractInvocation"/>
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
;
915 constructor
= nested
.CreateConstructor(cArg0
, cArg1
, cArg2
, cArg3
, cArg5
, cArg6
);
919 constructor
= nested
.CreateConstructor(cArg0
, cArg1
, cArg2
, cArg3
, cArg4
, cArg5
, cArg6
);
922 constructor
.CodeBuilder
.AddStatement(new AssignStatement(targetField
, cArg0
.ToExpression()));
926 constructor
.CodeBuilder
.InvokeBaseConstructor(Constants
.AbstractInvocationConstructorWithoutTargetMethod
,
936 constructor
.CodeBuilder
.InvokeBaseConstructor(Constants
.AbstractInvocationConstructorWithTargetMethod
,
946 constructor
.CodeBuilder
.AddStatement(new ReturnStatement());
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
);
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();
1000 /// Improvement: this cache should be static. We should generate a
1001 /// type constructor instead
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())));
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
));
1104 propsToGenerate
= CollectProperties(methodList
, type
, onlyVirtuals
, emitter
);
1109 /// Performs some basic screening and invokes the <see cref="IProxyGenerationHook"/>
1110 /// to select methods.
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
1121 bool isInternalsAndNotVisibleToDynamicProxy
= InternalsHelper
.IsInternal(method
)
1122 && InternalsHelper
.IsInternalToDynamicProxy(method
.DeclaringType
.Assembly
) == false;
1123 if (isInternalsAndNotVisibleToDynamicProxy
)
1126 if (onlyVirtuals
&& !method
.IsVirtual
)
1128 if (method
.DeclaringType
!= typeof (object) && method
.DeclaringType
!= typeof (MarshalByRefObject
))
1130 generationHook
.NonVirtualMemberNotification(targetType
, method
);
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)
1140 if (method
.DeclaringType
== typeof (object))
1144 if (method
.DeclaringType
== typeof (MarshalByRefObject
))
1149 return generationHook
.ShouldInterceptMethod(targetType
, method
);
1153 protected MethodInfo
[] CollectMethodsAndProperties(
1154 ClassEmitter emitter
,
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
,
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
));
1199 eventsToGenerates
= CollectEvents(methodList
, type
, onlyVirtuals
, emitter
);
1204 /// Checks if the method is public or protected.
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
)
1231 private bool NoFilter(Type type
, object filterCriteria
)
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
)
1246 AddMethodToGenerateNewSlot(method
);
1250 if (method
.IsSpecialName
)
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)
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
)
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
;
1382 protected void AddMethodToGenerateNewSlot(MethodInfo method
)
1384 generateNewSlot
.Add(method
);
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.
1391 protected bool ShouldCreateNewSlot(MethodInfo method
)
1393 string methodStr
= method
.ToString();
1394 foreach (MethodInfo candidate
in generateNewSlot
)
1396 if (candidate
.ToString() == methodStr
)
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(
1423 new MethodInvocationExpression(null,
1424 typeof (Type
).GetMethod("GetType",
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").
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(
1454 new ConstReference(interfaces
[i
].AssemblyQualifiedName
).ToExpression()));
1457 getObjectData
.CodeBuilder
.AddStatement(new ExpressionStatement(
1458 new MethodInvocationExpression(arg1
, addValueMethod
,
1459 new ConstReference("__interfaces").
1461 interfacesLocal
.ToExpression())));
1463 getObjectData
.CodeBuilder
.AddStatement(new ExpressionStatement(
1464 new MethodInvocationExpression(arg1
, addValueMethod
,
1465 new ConstReference("__baseType").
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
1496 if (!getObjectDataMethod
.IsVirtual
|| getObjectDataMethod
.IsFinal
)
1498 String message
= String
.Format("The type {0} implements ISerializable, but GetObjectData is not marked as virtual",
1500 throw new ArgumentException(message
);
1503 methodsToSkip
.Add(getObjectDataMethod
);
1505 serializationConstructor
= baseType
.GetConstructor(
1506 BindingFlags
.Instance
| BindingFlags
.Public
| BindingFlags
.NonPublic
,
1508 new Type
[] {typeof (SerializationInfo), typeof (StreamingContext)}
,
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
);