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
.Runtime
.Serialization
;
22 using System
.Threading
;
23 using System
.Xml
.Serialization
;
24 using Castle
.Core
.Interceptor
;
25 using Castle
.DynamicProxy
.Generators
.Emitters
;
26 using Castle
.DynamicProxy
.Generators
.Emitters
.CodeBuilders
;
27 using Castle
.DynamicProxy
.Generators
.Emitters
.SimpleAST
;
32 public class InterfaceProxyWithTargetGenerator
: BaseProxyGenerator
34 private FieldReference targetField
;
35 protected Hashtable method2Invocation
= new Hashtable();
36 protected Hashtable method2methodOnTarget
= new Hashtable();
38 public InterfaceProxyWithTargetGenerator(ModuleScope scope
, Type theInterface
)
39 : base(scope
, theInterface
)
41 CheckNotGenericTypeDefinition(theInterface
, "theInterface");
44 public Type
GenerateCode(Type proxyTargetType
, Type
[] interfaces
, ProxyGenerationOptions options
)
46 CheckNotGenericTypeDefinition(proxyTargetType
, "proxyTargetType");
47 CheckNotGenericTypeDefinitions(interfaces
, "interfaces");
50 ReaderWriterLock rwlock
= Scope
.RWLock
;
52 rwlock
.AcquireReaderLock(-1);
54 CacheKey cacheKey
= new CacheKey(proxyTargetType
, targetType
, interfaces
, options
);
56 Type cacheType
= GetFromCache(cacheKey
);
58 if (cacheType
!= null)
60 rwlock
.ReleaseReaderLock();
65 rwlock
.UpgradeToWriterLock(-1);
69 cacheType
= GetFromCache(cacheKey
);
71 if (cacheType
!= null)
76 String newName
= targetType
.Name
+ "Proxy" + Guid
.NewGuid().ToString("N");
78 // Add Interfaces that the proxy implements
80 ArrayList interfaceList
= new ArrayList();
82 interfaceList
.Add(targetType
);
84 if (interfaces
!= null)
86 interfaceList
.AddRange(interfaces
);
88 if (!interfaceList
.Contains(typeof(ISerializable
)))
89 interfaceList
.Add(typeof(ISerializable
));
91 AddDefaultInterfaces(interfaceList
);
93 Type baseType
= options
.BaseTypeForInterfaceProxy
;
95 ClassEmitter emitter
= BuildClassEmitter(newName
, baseType
, interfaceList
);
96 SetGenerationOptions (options
, emitter
);
98 emitter
.DefineCustomAttribute(new XmlIncludeAttribute(targetType
));
99 emitter
.DefineCustomAttribute(new SerializableAttribute());
102 ReplicateNonInheritableAttributes(targetType
, emitter
);
104 // Fields generations
106 FieldReference interceptorsField
= emitter
.CreateField("__interceptors", typeof(IInterceptor
[]));
107 targetField
= emitter
.CreateField("__target", proxyTargetType
);
109 emitter
.DefineCustomAttributeFor(interceptorsField
, new XmlIgnoreAttribute());
110 emitter
.DefineCustomAttributeFor(targetField
, new XmlIgnoreAttribute());
112 // Implement builtin Interfaces
113 ImplementProxyTargetAccessor(targetType
, emitter
,interceptorsField
);
117 PropertyToGenerate
[] propsToGenerate
;
118 EventToGenerate
[] eventToGenerates
;
119 MethodInfo
[] methods
= CollectMethodsAndProperties(emitter
, targetType
, out propsToGenerate
, out eventToGenerates
);
121 if (interfaces
!= null && interfaces
.Length
!= 0)
123 ArrayList tmpInterfaces
= new ArrayList(interfaces
);
125 foreach(Type inter
in interfaces
)
127 if (inter
.IsAssignableFrom(proxyTargetType
))
129 PropertyToGenerate
[] tempPropsToGenerate
;
130 EventToGenerate
[] tempEventToGenerates
;
131 MethodInfo
[] methodsTemp
=
132 CollectMethodsAndProperties(emitter
, inter
, out tempPropsToGenerate
, out tempEventToGenerates
);
134 PropertyToGenerate
[] newPropsToGenerate
=
135 new PropertyToGenerate
[tempPropsToGenerate
.Length
+ propsToGenerate
.Length
];
136 MethodInfo
[] newMethods
= new MethodInfo
[methodsTemp
.Length
+ methods
.Length
];
137 EventToGenerate
[] newEvents
= new EventToGenerate
[eventToGenerates
.Length
+ tempEventToGenerates
.Length
];
139 Array
.Copy(methods
, newMethods
, methods
.Length
);
140 Array
.Copy(methodsTemp
, 0, newMethods
, methods
.Length
, methodsTemp
.Length
);
142 Array
.Copy(propsToGenerate
, newPropsToGenerate
, propsToGenerate
.Length
);
143 Array
.Copy(tempPropsToGenerate
, 0, newPropsToGenerate
, propsToGenerate
.Length
, tempPropsToGenerate
.Length
);
145 Array
.Copy(eventToGenerates
, newEvents
, eventToGenerates
.Length
);
146 Array
.Copy(tempEventToGenerates
, 0, newEvents
, eventToGenerates
.Length
, tempEventToGenerates
.Length
);
148 methods
= newMethods
;
149 propsToGenerate
= newPropsToGenerate
;
150 eventToGenerates
= newEvents
;
152 tmpInterfaces
.Remove(inter
);
156 interfaces
= (Type
[]) tmpInterfaces
.ToArray(typeof(Type
));
159 options
.Hook
.MethodsInspected();
163 ConstructorEmitter typeInitializer
= GenerateStaticConstructor(emitter
);
165 if (!proxyTargetType
.IsInterface
)
167 CacheMethodTokens(emitter
, MethodFinder
.GetAllInstanceMethods(proxyTargetType
,
168 BindingFlags
.Public
| BindingFlags
.Instance
),
172 CreateInitializeCacheMethodBody(proxyTargetType
, methods
, emitter
, typeInitializer
);
173 GenerateConstructors(emitter
, baseType
, interceptorsField
, targetField
);
174 // GenerateParameterlessConstructor(emitter, interceptorsField, baseType);
176 // Implement interfaces
178 if (interfaces
!= null && interfaces
.Length
!= 0)
180 foreach(Type inter
in interfaces
)
182 ImplementBlankInterface(targetType
, inter
, emitter
, interceptorsField
, typeInitializer
);
186 // Create invocation types
188 foreach(MethodInfo method
in methods
)
190 CreateInvocationForMethod(emitter
, method
, proxyTargetType
);
193 // Create methods overrides
195 Dictionary
<MethodInfo
, MethodEmitter
> method2Emitter
= new Dictionary
<MethodInfo
, MethodEmitter
>();
197 foreach(MethodInfo method
in methods
)
199 if (method
.IsSpecialName
&&
200 (method
.Name
.StartsWith("get_") || method
.Name
.StartsWith("set_") ||
201 method
.Name
.StartsWith("add_") || method
.Name
.StartsWith("remove_")) ||
202 methodsToSkip
.Contains(method
))
207 NestedClassEmitter nestedClass
= (NestedClassEmitter
) method2Invocation
[method
];
209 // TODO: Should the targetType be a generic definition or instantiation
211 MethodEmitter newProxiedMethod
= CreateProxiedMethod(
212 targetType
, method
, emitter
, nestedClass
, interceptorsField
, targetField
,
213 ConstructorVersion
.WithTargetMethod
, (MethodInfo
) method2methodOnTarget
[method
]);
215 ReplicateNonInheritableAttributes(method
, newProxiedMethod
);
217 method2Emitter
[method
] = newProxiedMethod
;
219 ParameterInfo
[] parameters
= method
.GetParameters();
220 // ParameterInfo[] parametersProxy = newProxiedMethod.MethodBuilder.GetParameters();
222 // bool useDefineOverride = true;
224 for (int i = 0; i < parameters.Length; i++)
226 // ParameterInfo paramInfo = parameters[i];
227 // ParameterInfo paramInfo2 = parametersProxy[i];
229 // Console.WriteLine("{0} {1} {2} {3}", paramInfo.Name, paramInfo.ParameterType, paramInfo.Attributes, paramInfo.Position);
230 // Console.WriteLine("{0} {1} {2} {3}", paramInfo2.Name, paramInfo2.ParameterType, paramInfo2.Attributes, paramInfo2.Position);
233 // if (useDefineOverride)
235 // emitter.TypeBuilder.DefineMethodOverride(newProxiedMethod.MethodBuilder, method);
239 foreach(PropertyToGenerate propToGen
in propsToGenerate
)
241 if (propToGen
.CanRead
)
243 NestedClassEmitter nestedClass
= (NestedClassEmitter
) method2Invocation
[propToGen
.GetMethod
];
245 MethodAttributes atts
= ObtainMethodAttributes(propToGen
.GetMethod
);
247 MethodEmitter getEmitter
= propToGen
.Emitter
.CreateGetMethod(atts
);
249 ImplementProxiedMethod(targetType
, getEmitter
,
250 propToGen
.GetMethod
, emitter
,
251 nestedClass
, interceptorsField
, targetField
,
252 ConstructorVersion
.WithTargetMethod
,
253 (MethodInfo
) method2methodOnTarget
[propToGen
.GetMethod
]);
255 ReplicateNonInheritableAttributes(propToGen
.GetMethod
, getEmitter
);
257 // emitter.TypeBuilder.DefineMethodOverride(getEmitter.MethodBuilder, propToGen.GetMethod);
260 if (propToGen
.CanWrite
)
262 NestedClassEmitter nestedClass
= (NestedClassEmitter
) method2Invocation
[propToGen
.SetMethod
];
264 MethodAttributes atts
= ObtainMethodAttributes(propToGen
.SetMethod
);
266 MethodEmitter setEmitter
= propToGen
.Emitter
.CreateSetMethod(atts
);
268 ImplementProxiedMethod(targetType
, setEmitter
,
269 propToGen
.SetMethod
, emitter
,
270 nestedClass
, interceptorsField
, targetField
,
271 ConstructorVersion
.WithTargetMethod
,
272 (MethodInfo
) method2methodOnTarget
[propToGen
.SetMethod
]);
274 ReplicateNonInheritableAttributes(propToGen
.SetMethod
, setEmitter
);
276 // emitter.TypeBuilder.DefineMethodOverride(setEmitter.MethodBuilder, propToGen.SetMethod);
281 foreach(EventToGenerate eventToGenerate
in eventToGenerates
)
283 NestedClassEmitter add_nestedClass
= (NestedClassEmitter
) method2Invocation
[eventToGenerate
.AddMethod
];
285 MethodAttributes add_atts
= ObtainMethodAttributes(eventToGenerate
.AddMethod
);
287 MethodEmitter addEmitter
= eventToGenerate
.Emitter
.CreateAddMethod(add_atts
);
289 ImplementProxiedMethod(targetType
, addEmitter
,
290 eventToGenerate
.AddMethod
, emitter
,
291 add_nestedClass
, interceptorsField
, targetField
,
292 ConstructorVersion
.WithTargetMethod
,
293 (MethodInfo
) method2methodOnTarget
[eventToGenerate
.AddMethod
]);
295 ReplicateNonInheritableAttributes(eventToGenerate
.AddMethod
, addEmitter
);
298 NestedClassEmitter remove_nestedClass
= (NestedClassEmitter
) method2Invocation
[eventToGenerate
.RemoveMethod
];
300 MethodAttributes remove_atts
= ObtainMethodAttributes(eventToGenerate
.RemoveMethod
);
302 MethodEmitter removeEmitter
= eventToGenerate
.Emitter
.CreateRemoveMethod(remove_atts
);
304 ImplementProxiedMethod(targetType
, removeEmitter
,
305 eventToGenerate
.RemoveMethod
, emitter
,
306 remove_nestedClass
, interceptorsField
, targetField
,
307 ConstructorVersion
.WithTargetMethod
,
308 (MethodInfo
) method2methodOnTarget
[eventToGenerate
.RemoveMethod
]);
310 ReplicateNonInheritableAttributes(eventToGenerate
.RemoveMethod
, removeEmitter
);
313 ImplementGetObjectData(emitter
, interceptorsField
, interfaces
);
315 // Complete Initialize
317 CompleteInitCacheMethod(typeInitializer
.CodeBuilder
);
319 // Crosses fingers and build type
321 generatedType
= emitter
.BuildType();
322 InitializeStaticFields (generatedType
);
324 /*foreach (MethodInfo m in TypeFinder.GetMethods(generatedType, BindingFlags.Instance | BindingFlags.Public))
326 ParameterInfo[] parameters = m.GetParameters();
328 // Console.WriteLine(m.Name);
330 for (int i = 0; i < parameters.Length; i++)
332 ParameterInfo paramInfo = parameters[i];
334 // Console.WriteLine("{0} {1} {2} {3}", paramInfo.Name, paramInfo.ParameterType, paramInfo.Attributes, paramInfo.Position);
335 // Console.WriteLine("{0} {1} {2} {3}", paramInfo2.Name, paramInfo2.ParameterType, paramInfo2.Attributes, paramInfo2.Position);
340 AddToCache(cacheKey
, generatedType
);
344 rwlock
.ReleaseWriterLock();
347 Scope
.SaveAssembly();
349 return generatedType
;
352 protected virtual void CreateInvocationForMethod(ClassEmitter emitter
, MethodInfo method
, Type proxyTargetType
)
354 MethodInfo methodOnTarget
= FindMethodOnTargetType(method
, proxyTargetType
);
356 method2methodOnTarget
[method
] = methodOnTarget
;
358 method2Invocation
[method
] = BuildInvocationNestedType(emitter
, proxyTargetType
,
360 method
, methodOnTarget
,
361 ConstructorVersion
.WithTargetMethod
);
365 /// Finds the type of the method on target.
367 /// <param name="methodOnInterface">The method on interface.</param>
368 /// <param name="proxyTargetType">Type of the proxy target.</param>
369 /// <returns></returns>
370 protected static MethodInfo
FindMethodOnTargetType(MethodInfo methodOnInterface
, Type proxyTargetType
)
372 // The code below assumes that the target
373 // class uses the same generic arguments
374 // as the interface generic arguments
376 MemberInfo
[] members
= proxyTargetType
.FindMembers(MemberTypes
.Method
,
377 BindingFlags
.Public
| BindingFlags
.Instance
,
378 delegate(MemberInfo mi
, object criteria
)
380 if (mi
.Name
!= criteria
.ToString()) return false;
382 MethodInfo methodInfo
= (MethodInfo
) mi
;
384 return IsEquivalentMethod(methodInfo
, methodOnInterface
);
385 }, methodOnInterface
.Name
);
388 if (members
.Length
== 0)
390 // Before throwing an exception, we look for an explicit
391 // interface method implementation
393 MethodInfo
[] privateMethods
=
394 MethodFinder
.GetAllInstanceMethods(proxyTargetType
, BindingFlags
.NonPublic
| BindingFlags
.Instance
);
396 foreach(MethodInfo methodInfo
in privateMethods
)
398 // We make sure it is a method used for explicit implementation
400 if (!methodInfo
.IsFinal
|| !methodInfo
.IsVirtual
|| !methodInfo
.IsHideBySig
)
405 if (IsEquivalentMethod(methodInfo
, methodOnInterface
))
407 throw new GeneratorException(String
.Format("DynamicProxy cannot create an interface (with target) " +
408 "proxy for '{0}' as the target '{1}' has an explicit implementation of one of the methods exposed by the interface. " +
409 "The runtime prevents use from invoking the private method on the target. Method {2}",
410 methodOnInterface
.DeclaringType
.Name
, methodInfo
.DeclaringType
.Name
,
416 if (members
.Length
> 1)
418 throw new GeneratorException("Found more than one method on target " + proxyTargetType
.FullName
+ " matching " +
419 methodOnInterface
.Name
);
421 else if (members
.Length
== 0)
423 throw new GeneratorException("Could not find a matching method on " + proxyTargetType
.FullName
+ ". Method " +
424 methodOnInterface
.Name
);
427 return (MethodInfo
) members
[0];
431 /// Checks whether the given types are the same. This is
432 /// more complicated than it looks.
434 /// <param name="sourceType"></param>
435 /// <param name="targetType"></param>
436 /// <returns></returns>
437 public static bool IsTypeEquivalent(Type sourceType
, Type targetType
)
439 if (sourceType
.IsGenericParameter
)
441 if (sourceType
.Name
!= targetType
.Name
)
448 if (sourceType
.IsGenericType
!= targetType
.IsGenericType
)
452 else if (sourceType
.IsArray
!= targetType
.IsArray
)
457 if (sourceType
.IsGenericType
)
459 if (sourceType
.GetGenericTypeDefinition() != targetType
.GetGenericTypeDefinition())
464 // Compare generic arguments
466 Type
[] sourceGenArgs
= sourceType
.GetGenericArguments();
467 Type
[] targetGenArgs
= targetType
.GetGenericArguments();
469 for(int i
= 0; i
< sourceGenArgs
.Length
; i
++)
471 if (!IsTypeEquivalent(sourceGenArgs
[i
], targetGenArgs
[i
]))
477 else if (sourceType
.IsArray
)
479 Type sourceArrayType
= sourceType
.GetElementType();
480 Type targetArrayType
= targetType
.GetElementType();
482 if (!IsTypeEquivalent(sourceArrayType
, targetArrayType
))
487 int sourceRank
= sourceType
.GetArrayRank();
488 int targetRank
= targetType
.GetArrayRank();
490 if (sourceRank
!= targetRank
)
495 else if (sourceType
!= targetType
)
504 protected override Reference
GetProxyTargetReference()
509 protected override bool CanOnlyProxyVirtual()
514 private static bool IsEquivalentMethod(MethodInfo methodInfo
, MethodInfo methodOnInterface
)
516 // Check return type equivalence
518 if (!IsTypeEquivalent(methodInfo
.ReturnType
, methodOnInterface
.ReturnType
))
523 // Check parameters equivalence
525 ParameterInfo
[] sourceParams
= methodOnInterface
.GetParameters();
526 ParameterInfo
[] targetParams
= methodInfo
.GetParameters();
528 if (sourceParams
.Length
!= targetParams
.Length
)
533 for(int i
= 0; i
< sourceParams
.Length
; i
++)
535 Type sourceParamType
= sourceParams
[i
].ParameterType
;
536 Type targetParamType
= targetParams
[i
].ParameterType
;
538 if (!IsTypeEquivalent(sourceParamType
, targetParamType
))
547 protected override void CustomizeGetObjectData(AbstractCodeBuilder codebuilder
, ArgumentReference arg1
,
548 ArgumentReference arg2
)
550 Type
[] key_and_object
= new Type
[] {typeof(String), typeof(Object)}
;
551 Type
[] key_and_int
= new Type
[] {typeof(String), typeof(int)}
;
552 Type
[] key_and_string
= new Type
[] {typeof(String), typeof(string)}
;
553 MethodInfo addValueMethod
= typeof(SerializationInfo
).GetMethod("AddValue", key_and_object
);
554 MethodInfo addIntMethod
= typeof(SerializationInfo
).GetMethod("AddValue", key_and_int
);
555 MethodInfo addStringMethod
= typeof(SerializationInfo
).GetMethod("AddValue", key_and_string
);
557 codebuilder
.AddStatement(new ExpressionStatement(
558 new MethodInvocationExpression(arg1
, addValueMethod
,
559 new ConstReference("__target").ToExpression(),
560 targetField
.ToExpression())));
562 codebuilder
.AddStatement (new ExpressionStatement (
563 new MethodInvocationExpression (arg1
, addValueMethod
,
564 new ConstReference ("__targetFieldType").ToExpression (),
565 new ConstReference (targetField
.Reference
.FieldType
.AssemblyQualifiedName
).ToExpression())));
567 codebuilder
.AddStatement(new ExpressionStatement(
568 new MethodInvocationExpression(arg1
, addIntMethod
,
569 new ConstReference("__interface_generator_type").
571 new ConstReference((int) GeneratorType
).ToExpression())));
573 codebuilder
.AddStatement(new ExpressionStatement(
574 new MethodInvocationExpression(arg1
, addStringMethod
,
575 new ConstReference("__theInterface").ToExpression(),
576 new ConstReference(targetType
.AssemblyQualifiedName
).
580 protected virtual InterfaceGeneratorType GeneratorType
582 get { return InterfaceGeneratorType.WithTarget; }
587 /// This is used by the ProxyObjectReference class durin de-serialiation, to know
588 /// which generator it should use
590 public enum InterfaceGeneratorType
594 WithTargetInterface
= 3