DYNPROXY-56 - workaround for CLR serialization bug, applied patch from Fabian Schmied
[castle.git] / Tools / Castle.DynamicProxy2 / Castle.DynamicProxy / Generators / ClassProxyGenerator.cs
blobfaa40fec624f55677c0e6678057d8a1ec1f2f4b0
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.Collections.Generic;
17 namespace Castle.DynamicProxy.Generators
19 using System;
20 using System.Collections;
21 #if DOTNET2
22 using System.Collections.Generic;
23 #endif
24 using System.Reflection;
25 using System.Reflection.Emit;
26 using System.Threading;
27 using System.Xml.Serialization;
28 using Castle.Core.Interceptor;
29 using Castle.DynamicProxy.Generators.Emitters;
30 using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
31 using System.Collections.Specialized;
32 using System.Runtime.Serialization;
33 using Castle.DynamicProxy.Generators.Emitters.CodeBuilders;
34 using Castle.DynamicProxy.Serialization;
36 /// <summary>
37 ///
38 /// </summary>
39 [CLSCompliant(false)]
40 public class ClassProxyGenerator : BaseProxyGenerator
42 bool delegateToBaseGetObjectData = false;
44 public ClassProxyGenerator(ModuleScope scope, Type targetType)
45 : base(scope, targetType)
47 CheckNotGenericTypeDefinition (targetType, "targetType");
50 public Type GenerateCode(Type[] interfaces, ProxyGenerationOptions options)
52 CheckNotGenericTypeDefinitions (interfaces, "interfaces");
53 Type type;
55 ReaderWriterLock rwlock = Scope.RWLock;
57 rwlock.AcquireReaderLock(-1);
59 CacheKey cacheKey = new CacheKey(targetType, interfaces, options);
61 Type cacheType = GetFromCache(cacheKey);
63 if (cacheType != null)
65 rwlock.ReleaseReaderLock();
67 return cacheType;
70 rwlock.UpgradeToWriterLock(-1);
72 try
74 cacheType = GetFromCache(cacheKey);
76 if (cacheType != null)
78 return cacheType;
81 generationHook = options.Hook;
83 String newName = targetType.Name + "Proxy" + Guid.NewGuid().ToString("N");
85 // Add Interfaces that the proxy implements
87 ArrayList interfaceList = new ArrayList();
89 if (interfaces != null)
91 interfaceList.AddRange(interfaces);
94 AddMixinInterfaces(options, interfaceList);
96 AddDefaultInterfaces(interfaceList);
97 if (targetType.IsSerializable)
99 delegateToBaseGetObjectData = VerifyIfBaseImplementsGetObjectData(targetType);
100 if (!interfaceList.Contains(typeof(ISerializable)))
101 interfaceList.Add(typeof(ISerializable));
104 ClassEmitter emitter = BuildClassEmitter(newName, targetType, interfaceList);
105 emitter.DefineCustomAttribute(new XmlIncludeAttribute(targetType));
107 // Custom attributes
109 ReplicateNonInheritableAttributes(targetType, emitter);
111 // Fields generations
113 FieldReference interceptorsField =
114 emitter.CreateField("__interceptors", typeof(IInterceptor[]));
116 // Implement builtin Interfaces
117 ImplementProxyTargetAccessor(targetType, emitter,interceptorsField);
120 emitter.DefineCustomAttributeFor(interceptorsField, new XmlIgnoreAttribute());
122 // Collect methods
124 PropertyToGenerate[] propsToGenerate;
125 EventToGenerate[] eventToGenerates;
126 MethodInfo[] methods = CollectMethodsAndProperties(emitter, targetType, out propsToGenerate, out eventToGenerates);
128 methods = RegisterMixinMethodsAndProperties(emitter, options, methods, ref propsToGenerate, ref eventToGenerates);
130 options.Hook.MethodsInspected();
132 // Constructor
134 ConstructorEmitter typeInitializer = GenerateStaticConstructor(emitter);
136 FieldReference[] mixinFields = AddMixinFields(options, emitter);
138 List<FieldReference> fields = new List<FieldReference>(mixinFields);
139 fields.Add(interceptorsField);
140 FieldReference[] ctorArgs = fields.ToArray();
142 CreateInitializeCacheMethodBody(targetType, methods, emitter, typeInitializer);
143 GenerateConstructors(emitter, targetType, ctorArgs);
144 GenerateParameterlessConstructor(emitter, targetType, interceptorsField);
146 if (delegateToBaseGetObjectData)
148 GenerateSerializationConstructor(emitter, interceptorsField, delegateToBaseGetObjectData);
151 // Implement interfaces
152 if (interfaces != null && interfaces.Length != 0)
154 foreach (Type inter in interfaces)
156 ImplementBlankInterface(targetType, inter, emitter, interceptorsField, typeInitializer);
160 // Create callback methods
162 Dictionary<MethodInfo, MethodBuilder> method2Callback = new Dictionary<MethodInfo, MethodBuilder>();
164 foreach (MethodInfo method in methods)
166 method2Callback[method] = CreateCallbackMethod(emitter, method, method);
169 // Create invocation types
171 Dictionary<MethodInfo, NestedClassEmitter> method2Invocation = new Dictionary<MethodInfo, NestedClassEmitter>();
173 foreach (MethodInfo method in methods)
175 MethodBuilder callbackMethod = method2Callback[method];
177 method2Invocation[method] = BuildInvocationNestedType(emitter, targetType,
178 GetMethodTargetType(method),
179 method, callbackMethod,
180 ConstructorVersion.WithoutTargetMethod);
183 // Create methods overrides
185 Dictionary<MethodInfo, MethodEmitter> method2Emitter = new Dictionary<MethodInfo, MethodEmitter>();
187 foreach (MethodInfo method in methods)
189 if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")) || methodsToSkip.Contains(method))
191 continue;
194 NestedClassEmitter nestedClass = method2Invocation[method];
196 Reference targetRef = GetTargetRef(method, mixinFields, SelfReference.Self);
197 MethodEmitter newProxiedMethod = CreateProxiedMethod(
198 targetType, method, emitter, nestedClass, interceptorsField, targetRef,
199 ConstructorVersion.WithoutTargetMethod, null);
201 ReplicateNonInheritableAttributes(method, newProxiedMethod);
203 method2Emitter[method] = newProxiedMethod;
206 foreach (PropertyToGenerate propToGen in propsToGenerate)
208 if (propToGen.CanRead)
210 NestedClassEmitter nestedClass = method2Invocation[propToGen.GetMethod];
212 MethodAttributes atts = ObtainMethodAttributes(propToGen.GetMethod);
214 MethodEmitter getEmitter = propToGen.Emitter.CreateGetMethod(atts);
216 Reference targetRef = GetTargetRef(propToGen.GetMethod, mixinFields, SelfReference.Self);
218 ImplementProxiedMethod(targetType, getEmitter,
219 propToGen.GetMethod, emitter,
220 nestedClass, interceptorsField, targetRef,
221 ConstructorVersion.WithoutTargetMethod, null);
223 ReplicateNonInheritableAttributes(propToGen.GetMethod, getEmitter);
226 if (propToGen.CanWrite)
228 NestedClassEmitter nestedClass = method2Invocation[propToGen.SetMethod];
230 MethodAttributes atts = ObtainMethodAttributes(propToGen.SetMethod);
232 MethodEmitter setEmitter = propToGen.Emitter.CreateSetMethod(atts);
234 Reference targetRef = GetTargetRef(propToGen.SetMethod, mixinFields, SelfReference.Self);
236 ImplementProxiedMethod(targetType, setEmitter,
237 propToGen.SetMethod, emitter,
238 nestedClass, interceptorsField, targetRef,
239 ConstructorVersion.WithoutTargetMethod, null);
241 ReplicateNonInheritableAttributes(propToGen.SetMethod, setEmitter);
245 foreach (EventToGenerate eventToGenerate in eventToGenerates)
248 NestedClassEmitter add_nestedClass = method2Invocation[eventToGenerate.AddMethod];
250 MethodAttributes add_atts = ObtainMethodAttributes(eventToGenerate.AddMethod);
252 MethodEmitter addEmitter = eventToGenerate.Emitter.CreateAddMethod(add_atts);
254 Reference targetRef = GetTargetRef(eventToGenerate.AddMethod, mixinFields, SelfReference.Self);
256 ImplementProxiedMethod(targetType, addEmitter,
257 eventToGenerate.AddMethod, emitter,
258 add_nestedClass, interceptorsField, targetRef,
259 ConstructorVersion.WithoutTargetMethod, null);
261 ReplicateNonInheritableAttributes(eventToGenerate.AddMethod, addEmitter);
263 NestedClassEmitter remove_nestedClass = method2Invocation[eventToGenerate.RemoveMethod];
265 MethodAttributes remove_atts = ObtainMethodAttributes(eventToGenerate.RemoveMethod);
267 MethodEmitter removeEmitter = eventToGenerate.Emitter.CreateRemoveMethod(remove_atts);
269 ImplementProxiedMethod(targetType, removeEmitter,
270 eventToGenerate.RemoveMethod, emitter,
271 remove_nestedClass, interceptorsField, targetRef,
272 ConstructorVersion.WithoutTargetMethod, null);
274 ReplicateNonInheritableAttributes(eventToGenerate.RemoveMethod, removeEmitter);
279 ImplementGetObjectData(emitter, interceptorsField, interfaces);
281 // Complete type initializer code body
283 CompleteInitCacheMethod(typeInitializer.CodeBuilder);
285 // Build type
287 type = emitter.BuildType();
289 AddToCache(cacheKey, type);
291 finally
293 rwlock.ReleaseWriterLock();
296 Scope.SaveAssembly();
298 return type;
302 protected override Reference GetProxyTargetReference()
304 return SelfReference.Self;
307 protected override bool CanOnlyProxyVirtual()
309 return true;
312 protected void GenerateSerializationConstructor(ClassEmitter emitter, FieldReference interceptorField, bool delegateToBaseGetObjectData)
314 ArgumentReference arg1 = new ArgumentReference(typeof(SerializationInfo));
315 ArgumentReference arg2 = new ArgumentReference(typeof(StreamingContext));
317 ConstructorEmitter constr = emitter.CreateConstructor(arg1, arg2);
319 constr.CodeBuilder.AddStatement(
320 new ConstructorInvocationStatement(serializationConstructor,
321 arg1.ToExpression(), arg2.ToExpression()));
323 Type[] object_arg = new Type[] { typeof(String), typeof(Type) };
324 MethodInfo getValueMethod = typeof(SerializationInfo).GetMethod("GetValue", object_arg);
326 MethodInvocationExpression getInterceptorInvocation =
327 new MethodInvocationExpression(arg1, getValueMethod,
328 new ConstReference("__interceptors").ToExpression(),
329 new TypeTokenExpression(typeof(IInterceptor[])));
331 constr.CodeBuilder.AddStatement(new AssignStatement(
332 interceptorField, getInterceptorInvocation));
334 constr.CodeBuilder.AddStatement(new ReturnStatement());
337 protected override void CustomizeGetObjectData(AbstractCodeBuilder codebuilder,
338 ArgumentReference arg1, ArgumentReference arg2)
340 codebuilder.AddStatement (new ExpressionStatement (new MethodInvocationExpression (null,
341 typeof (ProxySerializer).GetMethod ("SerializeClassProxyData"), arg1.ToExpression (),
342 new ConstReference (delegateToBaseGetObjectData).ToExpression (), new TypeTokenExpression (targetType),
343 SelfReference.Self.ToExpression())));
345 if (delegateToBaseGetObjectData)
347 MethodInfo baseGetObjectData = targetType.GetMethod("GetObjectData",
348 new Type[] { typeof(SerializationInfo), typeof(StreamingContext) });
350 codebuilder.AddStatement( new ExpressionStatement(
351 new MethodInvocationExpression( baseGetObjectData,
352 arg1.ToExpression(), arg2.ToExpression() )) );