DYNPROXY-58 - Inherited interfaces - FIXED.
[castle.git] / Tools / Castle.DynamicProxy2 / Castle.DynamicProxy / Generators / InterfaceProxyWithTargetGenerator.cs
blob9829e01969279c2757067217aa7ed55486991d1d
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 using System;
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
29 #if DOTNET2
30 using System.Collections.Generic;
31 #endif
34 /// <summary>
35 ///
36 /// </summary>
37 [CLSCompliant(false)]
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");
54 Type generatedType;
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();
68 return cacheType;
71 rwlock.UpgradeToWriterLock(-1);
73 try
75 cacheType = GetFromCache(cacheKey);
77 if (cacheType != null)
79 return cacheType;
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());
108 // Custom attributes
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);
123 // Collect methods
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();
169 // Constructor
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),
178 typeInitializer);
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))
217 continue;
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)
247 // {
248 // emitter.TypeBuilder.DefineMethodOverride(newProxiedMethod.MethodBuilder, method);
249 // }*/
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);
350 finally
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);
372 /// <summary>
373 /// Finds the type of the method on target.
374 /// </summary>
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)
381 #if DOTNET2
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)
400 continue;
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];
428 #else
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);
438 #endif
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))
450 members.Add(method);
452 if (members.Count > 0 || proxyTargetType.IsClass)
453 return members;
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)
460 return members;
462 return members;
465 /// <summary>
466 /// Checks whether the given types are the same. This is
467 /// more complicated than it looks.
468 /// </summary>
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)
478 return false;
481 else
483 if (sourceType.IsGenericType != targetType.IsGenericType)
485 return false;
487 else if (sourceType.IsArray != targetType.IsArray)
489 return false;
492 if (sourceType.IsGenericType)
494 if (sourceType.GetGenericTypeDefinition() != targetType.GetGenericTypeDefinition())
496 return false;
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]))
508 return false;
512 else if (sourceType.IsArray)
514 Type sourceArrayType = sourceType.GetElementType();
515 Type targetArrayType = targetType.GetElementType();
517 if (!IsTypeEquivalent(sourceArrayType, targetArrayType))
519 return false;
522 int sourceRank = sourceType.GetArrayRank();
523 int targetRank = targetType.GetArrayRank();
525 if (sourceRank != targetRank)
527 return false;
530 else if (sourceType != targetType)
532 return false;
536 return true;
539 protected override Reference GetProxyTargetReference()
541 return targetField;
544 protected override bool CanOnlyProxyVirtual()
546 return false;
549 private static bool IsEquivalentMethod(MethodInfo methodInfo, MethodInfo methodOnInterface)
551 // Check return type equivalence
553 if (!IsTypeEquivalent(methodInfo.ReturnType, methodOnInterface.ReturnType))
555 return false;
558 // Check parameters equivalence
560 ParameterInfo[] sourceParams = methodOnInterface.GetParameters();
561 ParameterInfo[] targetParams = methodInfo.GetParameters();
563 if (sourceParams.Length != targetParams.Length)
565 return false;
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))
575 return false;
579 return true;
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; }
615 /// <summary>
616 /// This is used by the ProxyObjectReference class durin de-serialiation, to know
617 /// which generator it should use
618 /// </summary>
619 public enum InterfaceGeneratorType
621 WithTarget = 1,
622 WithoutTarget = 2,
623 WithTargetInterface = 3