Added container accessor to Castle.Core
[castle.git] / Tools / Castle.DynamicProxy2 / Castle.DynamicProxy / Generators / InterfaceProxyWithTargetGenerator.cs
bloba1ddfc35bac813370605de50f11f100471f30050
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 namespace Castle.DynamicProxy.Generators
17 using System;
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;
29 /// <summary>
30 ///
31 /// </summary>
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");
48 Type generatedType;
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();
62 return cacheType;
65 rwlock.UpgradeToWriterLock(-1);
67 try
69 cacheType = GetFromCache(cacheKey);
71 if (cacheType != null)
73 return cacheType;
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());
101 // Custom attributes
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);
115 // Collect methods
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();
161 // Constructor
163 ConstructorEmitter typeInitializer = GenerateStaticConstructor(emitter);
165 if (!proxyTargetType.IsInterface)
167 CacheMethodTokens(emitter, MethodFinder.GetAllInstanceMethods(proxyTargetType,
168 BindingFlags.Public | BindingFlags.Instance),
169 typeInitializer);
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))
204 continue;
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)
234 // {
235 // emitter.TypeBuilder.DefineMethodOverride(newProxiedMethod.MethodBuilder, method);
236 // }*/
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);
342 finally
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,
359 proxyTargetType,
360 method, methodOnTarget,
361 ConstructorVersion.WithTargetMethod);
364 /// <summary>
365 /// Finds the type of the method on target.
366 /// </summary>
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)
402 continue;
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,
411 methodInfo.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];
430 /// <summary>
431 /// Checks whether the given types are the same. This is
432 /// more complicated than it looks.
433 /// </summary>
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)
443 return false;
446 else
448 if (sourceType.IsGenericType != targetType.IsGenericType)
450 return false;
452 else if (sourceType.IsArray != targetType.IsArray)
454 return false;
457 if (sourceType.IsGenericType)
459 if (sourceType.GetGenericTypeDefinition() != targetType.GetGenericTypeDefinition())
461 return false;
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]))
473 return false;
477 else if (sourceType.IsArray)
479 Type sourceArrayType = sourceType.GetElementType();
480 Type targetArrayType = targetType.GetElementType();
482 if (!IsTypeEquivalent(sourceArrayType, targetArrayType))
484 return false;
487 int sourceRank = sourceType.GetArrayRank();
488 int targetRank = targetType.GetArrayRank();
490 if (sourceRank != targetRank)
492 return false;
495 else if (sourceType != targetType)
497 return false;
501 return true;
504 protected override Reference GetProxyTargetReference()
506 return targetField;
509 protected override bool CanOnlyProxyVirtual()
511 return false;
514 private static bool IsEquivalentMethod(MethodInfo methodInfo, MethodInfo methodOnInterface)
516 // Check return type equivalence
518 if (!IsTypeEquivalent(methodInfo.ReturnType, methodOnInterface.ReturnType))
520 return false;
523 // Check parameters equivalence
525 ParameterInfo[] sourceParams = methodOnInterface.GetParameters();
526 ParameterInfo[] targetParams = methodInfo.GetParameters();
528 if (sourceParams.Length != targetParams.Length)
530 return false;
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))
540 return false;
544 return true;
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").
570 ToExpression(),
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).
577 ToExpression())));
580 protected virtual InterfaceGeneratorType GeneratorType
582 get { return InterfaceGeneratorType.WithTarget; }
586 /// <summary>
587 /// This is used by the ProxyObjectReference class durin de-serialiation, to know
588 /// which generator it should use
589 /// </summary>
590 public enum InterfaceGeneratorType
592 WithTarget = 1,
593 WithoutTarget = 2,
594 WithTargetInterface = 3