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
.Collections
.Generic
;
18 using System
.Reflection
;
19 using System
.Runtime
.Serialization
;
20 using System
.Threading
;
21 using System
.Xml
.Serialization
;
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
;
27 namespace Castle
.DynamicProxy
.Generators
30 using System
.Collections
.Generic
;
38 public class InterfaceProxyWithTargetGenerator
: BaseProxyGenerator
40 private FieldReference targetField
;
41 protected Hashtable method2Invocation
= new Hashtable();
42 protected Hashtable method2methodOnTarget
= new Hashtable();
44 public InterfaceProxyWithTargetGenerator(ModuleScope scope
, Type theInterface
)
45 : base(scope
, theInterface
)
47 CheckNotGenericTypeDefinition(theInterface
, "theInterface");
50 public Type
GenerateCode(Type proxyTargetType
, Type
[] interfaces
, ProxyGenerationOptions options
)
52 CheckNotGenericTypeDefinition(proxyTargetType
, "proxyTargetType");
53 CheckNotGenericTypeDefinitions(interfaces
, "interfaces");
56 ReaderWriterLock rwlock
= Scope
.RWLock
;
58 rwlock
.AcquireReaderLock(-1);
60 CacheKey cacheKey
= new CacheKey(targetType
, interfaces
, options
);
62 Type cacheType
= GetFromCache(cacheKey
);
64 if (cacheType
!= null)
66 rwlock
.ReleaseReaderLock();
71 rwlock
.UpgradeToWriterLock(-1);
75 cacheType
= GetFromCache(cacheKey
);
77 if (cacheType
!= null)
82 generationHook
= options
.Hook
;
84 String newName
= targetType
.Name
+ "Proxy" + Guid
.NewGuid().ToString("N");
86 // Add Interfaces that the proxy implements
88 ArrayList interfaceList
= new ArrayList();
90 interfaceList
.Add(targetType
);
92 if (interfaces
!= null)
94 interfaceList
.AddRange(interfaces
);
96 if (!interfaceList
.Contains(typeof (ISerializable
)))
97 interfaceList
.Add(typeof (ISerializable
));
99 AddMixinInterfaces(options
, interfaceList
);
100 AddDefaultInterfaces(interfaceList
);
102 Type baseType
= options
.BaseTypeForInterfaceProxy
;
104 ClassEmitter emitter
= BuildClassEmitter(newName
, baseType
, interfaceList
);
105 emitter
.DefineCustomAttribute(new XmlIncludeAttribute(targetType
));
106 emitter
.DefineCustomAttribute(new SerializableAttribute());
109 ReplicateNonInheritableAttributes(targetType
, emitter
);
111 // Fields generations
113 FieldReference interceptorsField
= emitter
.CreateField("__interceptors", typeof (IInterceptor
[]));
114 targetField
= emitter
.CreateField("__target", proxyTargetType
);
116 emitter
.DefineCustomAttributeFor(interceptorsField
, new XmlIgnoreAttribute());
117 emitter
.DefineCustomAttributeFor(targetField
, new XmlIgnoreAttribute());
119 // Implement builtin Interfaces
121 ImplementProxyTargetAccessor(targetType
, emitter
, interceptorsField
);
125 PropertyToGenerate
[] propsToGenerate
;
126 EventToGenerate
[] eventsToGenerate
;
127 MethodInfo
[] methods
= CollectMethodsAndProperties(emitter
, targetType
, out propsToGenerate
, out eventsToGenerate
);
129 if (interfaces
!= null && interfaces
.Length
!= 0)
131 ArrayList tmpInterfaces
= new ArrayList(interfaces
);
133 foreach (Type inter
in interfaces
)
135 if (inter
.IsAssignableFrom(proxyTargetType
))
137 PropertyToGenerate
[] tempPropsToGenerate
;
138 EventToGenerate
[] tempEventToGenerates
;
139 MethodInfo
[] methodsTemp
= CollectMethodsAndProperties(emitter
, inter
, out tempPropsToGenerate
, out tempEventToGenerates
);
141 PropertyToGenerate
[] newPropsToGenerate
= new PropertyToGenerate
[tempPropsToGenerate
.Length
+ propsToGenerate
.Length
];
142 MethodInfo
[] newMethods
= new MethodInfo
[methodsTemp
.Length
+ methods
.Length
];
143 EventToGenerate
[] newEvents
= new EventToGenerate
[eventsToGenerate
.Length
+ tempEventToGenerates
.Length
];
145 Array
.Copy(methods
, newMethods
, methods
.Length
);
146 Array
.Copy(methodsTemp
, 0, newMethods
, methods
.Length
, methodsTemp
.Length
);
148 Array
.Copy(propsToGenerate
, newPropsToGenerate
, propsToGenerate
.Length
);
149 Array
.Copy(tempPropsToGenerate
, 0, newPropsToGenerate
, propsToGenerate
.Length
, tempPropsToGenerate
.Length
);
151 Array
.Copy(eventsToGenerate
, newEvents
, eventsToGenerate
.Length
);
152 Array
.Copy(tempEventToGenerates
, 0, newEvents
, eventsToGenerate
.Length
, tempEventToGenerates
.Length
);
154 methods
= newMethods
;
155 propsToGenerate
= newPropsToGenerate
;
156 eventsToGenerate
= newEvents
;
158 tmpInterfaces
.Remove(inter
);
162 interfaces
= (Type
[]) tmpInterfaces
.ToArray(typeof (Type
));
165 methods
= RegisterMixinMethodsAndProperties(emitter
, options
, methods
, ref propsToGenerate
, ref eventsToGenerate
);
167 options
.Hook
.MethodsInspected();
171 ConstructorEmitter typeInitializer
= GenerateStaticConstructor(emitter
);
173 // InterfaceProxyGeneratorWithTargetInterface gets duplicate method exceptions from BaseProxyGenerator with this... -jlewalle
174 if (!proxyTargetType
.IsInterface
)
176 CacheMethodTokens(emitter
,
177 proxyTargetType
.GetMethods(BindingFlags
.Public
| BindingFlags
.Instance
),
181 CreateInitializeCacheMethodBody(proxyTargetType
, methods
, emitter
, typeInitializer
);
182 FieldReference
[] mixinFields
= AddMixinFields(options
, emitter
);
183 List
<FieldReference
> fields
= new List
<FieldReference
>(mixinFields
);
184 fields
.Add(interceptorsField
);
185 fields
.Add(targetField
);
186 GenerateConstructors(emitter
, baseType
, fields
.ToArray());
187 // GenerateParameterlessConstructor(emitter, interceptorsField, baseType);
189 // Implement interfaces
191 if (interfaces
!= null && interfaces
.Length
!= 0)
193 foreach (Type inter
in interfaces
)
195 ImplementBlankInterface(targetType
, inter
, emitter
, interceptorsField
, typeInitializer
);
199 // Create invocation types
201 foreach (MethodInfo method
in methods
)
203 CreateInvocationForMethod(emitter
, method
, proxyTargetType
);
206 // Create methods overrides
208 Dictionary
<MethodInfo
, MethodEmitter
> method2Emitter
= new Dictionary
<MethodInfo
, MethodEmitter
>();
210 foreach (MethodInfo method
in methods
)
212 if (method
.IsSpecialName
&&
213 (method
.Name
.StartsWith("get_") || method
.Name
.StartsWith("set_") ||
214 method
.Name
.StartsWith("add_") || method
.Name
.StartsWith("remove_")) ||
215 methodsToSkip
.Contains(method
))
220 NestedClassEmitter nestedClass
= (NestedClassEmitter
) method2Invocation
[method
];
222 // TODO: Should the targetType be a generic definition or instantiation
224 MethodEmitter newProxiedMethod
= CreateProxiedMethod(
225 targetType
, method
, emitter
, nestedClass
, interceptorsField
, GetTargetRef(method
, mixinFields
, targetField
),
226 ConstructorVersion
.WithTargetMethod
, (MethodInfo
) method2methodOnTarget
[method
]);
228 ReplicateNonInheritableAttributes(method
, newProxiedMethod
);
230 method2Emitter
[method
] = newProxiedMethod
;
232 ParameterInfo
[] parameters
= method
.GetParameters();
233 // ParameterInfo[] parametersProxy = newProxiedMethod.MethodBuilder.GetParameters();
235 // bool useDefineOverride = true;
237 for (int i = 0; i < parameters.Length; i++)
239 // ParameterInfo paramInfo = parameters[i];
240 // ParameterInfo paramInfo2 = parametersProxy[i];
242 // Console.WriteLine("{0} {1} {2} {3}", paramInfo.Name, paramInfo.ParameterType, paramInfo.Attributes, paramInfo.Position);
243 // Console.WriteLine("{0} {1} {2} {3}", paramInfo2.Name, paramInfo2.ParameterType, paramInfo2.Attributes, paramInfo2.Position);
246 // if (useDefineOverride)
248 // emitter.TypeBuilder.DefineMethodOverride(newProxiedMethod.MethodBuilder, method);
252 foreach (PropertyToGenerate propToGen
in propsToGenerate
)
254 if (propToGen
.CanRead
)
256 NestedClassEmitter nestedClass
= (NestedClassEmitter
) method2Invocation
[propToGen
.GetMethod
];
258 MethodAttributes atts
= ObtainMethodAttributes(propToGen
.GetMethod
);
260 MethodEmitter getEmitter
= propToGen
.Emitter
.CreateGetMethod(atts
);
262 ImplementProxiedMethod(targetType
, getEmitter
,
263 propToGen
.GetMethod
, emitter
,
264 nestedClass
, interceptorsField
, targetField
,
265 ConstructorVersion
.WithTargetMethod
, (MethodInfo
) method2methodOnTarget
[propToGen
.GetMethod
]);
267 ReplicateNonInheritableAttributes(propToGen
.GetMethod
, getEmitter
);
269 // emitter.TypeBuilder.DefineMethodOverride(getEmitter.MethodBuilder, propToGen.GetMethod);
272 if (propToGen
.CanWrite
)
274 NestedClassEmitter nestedClass
= (NestedClassEmitter
) method2Invocation
[propToGen
.SetMethod
];
276 MethodAttributes atts
= ObtainMethodAttributes(propToGen
.SetMethod
);
278 MethodEmitter setEmitter
= propToGen
.Emitter
.CreateSetMethod(atts
);
280 ImplementProxiedMethod(targetType
, setEmitter
,
281 propToGen
.SetMethod
, emitter
,
282 nestedClass
, interceptorsField
, targetField
,
283 ConstructorVersion
.WithTargetMethod
, (MethodInfo
) method2methodOnTarget
[propToGen
.SetMethod
]);
285 ReplicateNonInheritableAttributes(propToGen
.SetMethod
, setEmitter
);
287 // emitter.TypeBuilder.DefineMethodOverride(setEmitter.MethodBuilder, propToGen.SetMethod);
292 foreach (EventToGenerate eventToGenerate
in eventsToGenerate
)
294 NestedClassEmitter add_nestedClass
= (NestedClassEmitter
) method2Invocation
[eventToGenerate
.AddMethod
];
296 MethodAttributes add_atts
= ObtainMethodAttributes(eventToGenerate
.AddMethod
);
298 MethodEmitter addEmitter
= eventToGenerate
.Emitter
.CreateAddMethod(add_atts
);
300 ImplementProxiedMethod(targetType
, addEmitter
,
301 eventToGenerate
.AddMethod
, emitter
,
302 add_nestedClass
, interceptorsField
, targetField
,
303 ConstructorVersion
.WithTargetMethod
, (MethodInfo
) method2methodOnTarget
[eventToGenerate
.AddMethod
]);
305 ReplicateNonInheritableAttributes(eventToGenerate
.AddMethod
, addEmitter
);
308 NestedClassEmitter remove_nestedClass
= (NestedClassEmitter
) method2Invocation
[eventToGenerate
.RemoveMethod
];
310 MethodAttributes remove_atts
= ObtainMethodAttributes(eventToGenerate
.RemoveMethod
);
312 MethodEmitter removeEmitter
= eventToGenerate
.Emitter
.CreateRemoveMethod(remove_atts
);
314 ImplementProxiedMethod(targetType
, removeEmitter
,
315 eventToGenerate
.RemoveMethod
, emitter
,
316 remove_nestedClass
, interceptorsField
, targetField
,
317 ConstructorVersion
.WithTargetMethod
, (MethodInfo
) method2methodOnTarget
[eventToGenerate
.RemoveMethod
]);
319 ReplicateNonInheritableAttributes(eventToGenerate
.RemoveMethod
, removeEmitter
);
322 ImplementGetObjectData(emitter
, interceptorsField
, interfaces
);
324 // Complete Initialize
326 CompleteInitCacheMethod(typeInitializer
.CodeBuilder
);
328 // Crosses fingers and build type
330 generatedType
= emitter
.BuildType();
332 /*foreach (MethodInfo m in generatedType.GetMethods())
334 ParameterInfo[] parameters = m.GetParameters();
336 // Console.WriteLine(m.Name);
338 for (int i = 0; i < parameters.Length; i++)
340 ParameterInfo paramInfo = parameters[i];
342 // Console.WriteLine("{0} {1} {2} {3}", paramInfo.Name, paramInfo.ParameterType, paramInfo.Attributes, paramInfo.Position);
343 // Console.WriteLine("{0} {1} {2} {3}", paramInfo2.Name, paramInfo2.ParameterType, paramInfo2.Attributes, paramInfo2.Position);
348 AddToCache(cacheKey
, generatedType
);
352 rwlock
.ReleaseWriterLock();
355 Scope
.SaveAssembly();
357 return generatedType
;
360 protected virtual void CreateInvocationForMethod(ClassEmitter emitter
, MethodInfo method
, Type proxyTargetType
)
362 MethodInfo methodOnTarget
= FindMethodOnTargetType(method
, proxyTargetType
, true);
364 method2methodOnTarget
[method
] = methodOnTarget
;
366 method2Invocation
[method
] = BuildInvocationNestedType(emitter
, proxyTargetType
,
367 GetMethodTargetType(methodOnTarget
),
368 method
, methodOnTarget
,
369 ConstructorVersion
.WithTargetMethod
);
373 /// Finds the type of the method on target.
375 /// <param name="methodOnInterface">The method on interface.</param>
376 /// <param name="proxyTargetType">Type of the proxy target.</param>
377 /// <param name="checkMixins">if set to <c>true</c> will check implementation on mixins.</param>
378 /// <returns></returns>
379 protected MethodInfo
FindMethodOnTargetType(MethodInfo methodOnInterface
, Type proxyTargetType
, bool checkMixins
)
382 // The code below assumes that the target
383 // class uses the same generic arguments
384 // as the interface generic arguments
385 List
<MethodInfo
> members
= GetMembers(methodOnInterface
, proxyTargetType
);
387 if (members
.Count
== 0)
389 // Before throwing an exception, we look for an explicit
390 // interface method implementation
392 MethodInfo
[] privateMethods
= proxyTargetType
.GetMethods(BindingFlags
.NonPublic
| BindingFlags
.Instance
);
394 foreach (MethodInfo methodInfo
in privateMethods
)
396 // We make sure it is a method used for explicit implementation
398 if (!methodInfo
.IsFinal
|| !methodInfo
.IsVirtual
|| !methodInfo
.IsHideBySig
)
403 if (IsEquivalentMethod(methodInfo
, methodOnInterface
))
405 throw new GeneratorException(String
.Format("DynamicProxy cannot create an interface (with target) " +
406 "proxy for '{0}' as the target '{1}' has an explicit implementation of one of the methods exposed by the interface. " +
407 "The runtime prevents use from invoking the private method on the target. Method {2}",
408 methodOnInterface
.DeclaringType
.Name
, methodInfo
.DeclaringType
.Name
, methodInfo
.Name
));
413 if (members
.Count
> 1)
415 throw new GeneratorException("Found more than one method on target " + proxyTargetType
.FullName
+ " matching " + methodOnInterface
.Name
);
417 else if (members
.Count
== 0)
419 if (checkMixins
&& IsMixinMethod(methodOnInterface
))
421 return FindMethodOnTargetType(methodOnInterface
, method2MixinType
[methodOnInterface
], false);
423 throw new GeneratorException("Could not find a matching method on " + proxyTargetType
.FullName
+ ". Method " + methodOnInterface
.Name
);
426 return (MethodInfo
) members
[0];
429 ParameterInfo
[] parameters
= methodOnInterface
.GetParameters();
430 Type
[] argTypes
= new Type
[parameters
.Length
];
432 for(int i
=0; i
< parameters
.Length
; i
++)
434 argTypes
[i
] = parameters
[i
].ParameterType
;
437 return proxyTargetType
.GetMethod(methodOnInterface
.Name
, argTypes
);
441 private static List
<MethodInfo
> GetMembers(MethodInfo methodOnInterface
, Type proxyTargetType
)
444 List
<MethodInfo
> members
= new List
<MethodInfo
>();
445 foreach (MethodInfo method
in proxyTargetType
.GetMethods(BindingFlags
.Public
| BindingFlags
.Instance
))
447 if (method
.Name
!= methodOnInterface
.Name
) continue;
449 if (IsEquivalentMethod(method
, methodOnInterface
))
452 if (members
.Count
> 0 || proxyTargetType
.IsClass
)
454 //didn't find it here, and we are looking at an interface, so we want to try the
455 // base interfaces as well
456 foreach (Type type
in proxyTargetType
.GetInterfaces())
458 members
.AddRange(GetMembers(methodOnInterface
, type
));
459 if (members
.Count
> 0)
466 /// Checks whether the given types are the same. This is
467 /// more complicated than it looks.
469 /// <param name="sourceType"></param>
470 /// <param name="targetType"></param>
471 /// <returns></returns>
472 public static bool IsTypeEquivalent(Type sourceType
, Type targetType
)
474 if (sourceType
.IsGenericParameter
)
476 if (sourceType
.Name
!= targetType
.Name
)
483 if (sourceType
.IsGenericType
!= targetType
.IsGenericType
)
487 else if (sourceType
.IsArray
!= targetType
.IsArray
)
492 if (sourceType
.IsGenericType
)
494 if (sourceType
.GetGenericTypeDefinition() != targetType
.GetGenericTypeDefinition())
499 // Compare generic arguments
501 Type
[] sourceGenArgs
= sourceType
.GetGenericArguments();
502 Type
[] targetGenArgs
= targetType
.GetGenericArguments();
504 for (int i
= 0; i
< sourceGenArgs
.Length
; i
++)
506 if (!IsTypeEquivalent(sourceGenArgs
[i
], targetGenArgs
[i
]))
512 else if (sourceType
.IsArray
)
514 Type sourceArrayType
= sourceType
.GetElementType();
515 Type targetArrayType
= targetType
.GetElementType();
517 if (!IsTypeEquivalent(sourceArrayType
, targetArrayType
))
522 int sourceRank
= sourceType
.GetArrayRank();
523 int targetRank
= targetType
.GetArrayRank();
525 if (sourceRank
!= targetRank
)
530 else if (sourceType
!= targetType
)
539 protected override Reference
GetProxyTargetReference()
544 protected override bool CanOnlyProxyVirtual()
549 private static bool IsEquivalentMethod(MethodInfo methodInfo
, MethodInfo methodOnInterface
)
551 // Check return type equivalence
553 if (!IsTypeEquivalent(methodInfo
.ReturnType
, methodOnInterface
.ReturnType
))
558 // Check parameters equivalence
560 ParameterInfo
[] sourceParams
= methodOnInterface
.GetParameters();
561 ParameterInfo
[] targetParams
= methodInfo
.GetParameters();
563 if (sourceParams
.Length
!= targetParams
.Length
)
568 for (int i
= 0; i
< sourceParams
.Length
; i
++)
570 Type sourceParamType
= sourceParams
[i
].ParameterType
;
571 Type targetParamType
= targetParams
[i
].ParameterType
;
573 if (!IsTypeEquivalent(sourceParamType
, targetParamType
))
582 protected override void CustomizeGetObjectData(
583 AbstractCodeBuilder codebuilder
, ArgumentReference arg1
,
584 ArgumentReference arg2
)
586 Type
[] key_and_object
= new Type
[] {typeof (String), typeof (Object)}
;
587 Type
[] key_and_int
= new Type
[] {typeof (String), typeof (int)}
;
588 Type
[] key_and_string
= new Type
[] {typeof (String), typeof (string)}
;
589 MethodInfo addValueMethod
= typeof (SerializationInfo
).GetMethod("AddValue", key_and_object
);
590 MethodInfo addIntMethod
= typeof (SerializationInfo
).GetMethod("AddValue", key_and_int
);
591 MethodInfo addStringMethod
= typeof (SerializationInfo
).GetMethod("AddValue", key_and_string
);
593 codebuilder
.AddStatement(new ExpressionStatement(
594 new MethodInvocationExpression(arg1
, addValueMethod
,
595 new ConstReference("__target").ToExpression(),
596 targetField
.ToExpression())));
598 codebuilder
.AddStatement(new ExpressionStatement(
599 new MethodInvocationExpression(arg1
, addIntMethod
,
600 new ConstReference("__interface_generator_type").ToExpression(),
601 new ConstReference((int) GeneratorType
).ToExpression())));
603 codebuilder
.AddStatement(new ExpressionStatement(
604 new MethodInvocationExpression(arg1
, addStringMethod
,
605 new ConstReference("__theInterface").ToExpression(),
606 new ConstReference(targetType
.AssemblyQualifiedName
).ToExpression())));
609 protected virtual InterfaceGeneratorType GeneratorType
611 get { return InterfaceGeneratorType.WithTarget; }
616 /// This is used by the ProxyObjectReference class durin de-serialiation, to know
617 /// which generator it should use
619 public enum InterfaceGeneratorType
623 WithTargetInterface
= 3