// Copyright 2004-2008 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace Castle.DynamicProxy.Generators
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Threading;
#if !SILVERLIGHT
using System.Xml.Serialization;
#endif
using Castle.Core.Interceptor;
using Castle.DynamicProxy.Generators.Emitters;
using Castle.DynamicProxy.Generators.Emitters.CodeBuilders;
using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
using Castle.Core.Internal;
///
///
///
public class ClassProxyGenerator : BaseProxyGenerator
{
#if !SILVERLIGHT
private bool delegateToBaseGetObjectData = false;
#endif
public ClassProxyGenerator(ModuleScope scope, Type targetType) : base(scope, targetType)
{
CheckNotGenericTypeDefinition(targetType, "targetType");
}
public Type GenerateCode(Type[] interfaces, ProxyGenerationOptions options)
{
// make sure ProxyGenerationOptions is initialized
options.Initialize();
CheckNotGenericTypeDefinitions(interfaces, "interfaces");
Type type;
SlimReaderWriterLock rwlock = Scope.RWLock;
rwlock.EnterReadLock();
CacheKey cacheKey = new CacheKey(targetType, interfaces, options);
Type cacheType = GetFromCache(cacheKey);
if (cacheType != null)
{
rwlock.ExitReadLock();
return cacheType;
}
rwlock.EnterWriteLock();
try
{
cacheType = GetFromCache(cacheKey);
if (cacheType != null)
{
return cacheType;
}
SetGenerationOptions(options);
String newName = targetType.Name + "Proxy" + Guid.NewGuid().ToString("N");
// Add Interfaces that the proxy implements
List interfaceList = new List();
if (interfaces != null)
{
interfaceList.AddRange(interfaces);
}
AddMixinInterfaces(interfaceList);
AddDefaultInterfaces(interfaceList);
#if !SILVERLIGHT
if (targetType.IsSerializable)
{
delegateToBaseGetObjectData = VerifyIfBaseImplementsGetObjectData(targetType);
if (!interfaceList.Contains(typeof(ISerializable)))
{
interfaceList.Add(typeof(ISerializable));
}
}
#endif
ClassEmitter emitter = BuildClassEmitter(newName, targetType, interfaceList);
CreateOptionsField(emitter);
#if !SILVERLIGHT
emitter.DefineCustomAttribute(new XmlIncludeAttribute(targetType));
#endif
// Custom attributes
ReplicateNonInheritableAttributes(targetType, emitter);
// Fields generations
FieldReference interceptorsField =
emitter.CreateField("__interceptors", typeof (IInterceptor[]));
// Implement builtin Interfaces
ImplementProxyTargetAccessor(targetType, emitter, interceptorsField);
#if !SILVERLIGHT
emitter.DefineCustomAttributeFor(interceptorsField, new XmlIgnoreAttribute());
#endif
// Collect methods
PropertyToGenerate[] propsToGenerate;
EventToGenerate[] eventToGenerates;
MethodInfo[] methods = CollectMethodsAndProperties(emitter, targetType, out propsToGenerate, out eventToGenerates);
RegisterMixinMethodsAndProperties(emitter, ref methods, ref propsToGenerate, ref eventToGenerates);
options.Hook.MethodsInspected();
// Constructor
ConstructorEmitter typeInitializer = GenerateStaticConstructor(emitter);
FieldReference[] mixinFields = AddMixinFields(emitter);
// constructor arguments
List constructorArguments = new List(mixinFields);
constructorArguments.Add(interceptorsField);
CreateInitializeCacheMethodBody(targetType, methods, emitter, typeInitializer);
GenerateConstructors(emitter, targetType, constructorArguments.ToArray());
GenerateParameterlessConstructor(emitter, targetType, interceptorsField);
#if !SILVERLIGHT
if (delegateToBaseGetObjectData)
{
GenerateSerializationConstructor(emitter, interceptorsField, mixinFields);
}
#endif
// Implement interfaces
if (interfaces != null && interfaces.Length != 0)
{
foreach (Type inter in interfaces)
{
ImplementBlankInterface(targetType, inter, emitter, interceptorsField, typeInitializer);
}
}
// Create callback methods
Dictionary method2Callback = new Dictionary();
foreach (MethodInfo method in methods)
{
method2Callback[method] = CreateCallbackMethod(emitter, method, method);
}
// Create invocation types
Dictionary method2Invocation = new Dictionary();
foreach (MethodInfo method in methods)
{
MethodBuilder callbackMethod = method2Callback[method];
method2Invocation[method] = BuildInvocationNestedType(emitter, targetType,
IsMixinMethod(method)
? method.DeclaringType
: emitter.TypeBuilder,
method, callbackMethod,
ConstructorVersion.WithoutTargetMethod);
}
// Create methods overrides
Dictionary method2Emitter = new Dictionary();
foreach (MethodInfo method in methods)
{
if (method.IsSpecialName &&
(method.Name.StartsWith("get_") || method.Name.StartsWith("set_") ||
method.Name.StartsWith("add_") || method.Name.StartsWith("remove_")) ||
methodsToSkip.Contains(method))
{
continue;
}
NestedClassEmitter nestedClass = method2Invocation[method];
Reference targetRef = GetTargetRef(method, mixinFields, SelfReference.Self);
MethodEmitter newProxiedMethod = CreateProxiedMethod(
targetType, method, emitter, nestedClass, interceptorsField, targetRef,
ConstructorVersion.WithoutTargetMethod, null);
ReplicateNonInheritableAttributes(method, newProxiedMethod);
method2Emitter[method] = newProxiedMethod;
}
foreach (PropertyToGenerate propToGen in propsToGenerate)
{
if (propToGen.CanRead)
{
NestedClassEmitter nestedClass = method2Invocation[propToGen.GetMethod];
MethodAttributes atts = ObtainMethodAttributes(propToGen.GetMethod);
MethodEmitter getEmitter = propToGen.Emitter.CreateGetMethod(atts);
Reference targetRef = GetTargetRef(propToGen.GetMethod, mixinFields, SelfReference.Self);
ImplementProxiedMethod(targetType, getEmitter,
propToGen.GetMethod, emitter,
nestedClass, interceptorsField, targetRef,
ConstructorVersion.WithoutTargetMethod, null);
ReplicateNonInheritableAttributes(propToGen.GetMethod, getEmitter);
}
if (propToGen.CanWrite)
{
NestedClassEmitter nestedClass = method2Invocation[propToGen.SetMethod];
MethodAttributes atts = ObtainMethodAttributes(propToGen.SetMethod);
MethodEmitter setEmitter = propToGen.Emitter.CreateSetMethod(atts);
Reference targetRef = GetTargetRef(propToGen.SetMethod, mixinFields, SelfReference.Self);
ImplementProxiedMethod(targetType, setEmitter,
propToGen.SetMethod, emitter,
nestedClass, interceptorsField, targetRef,
ConstructorVersion.WithoutTargetMethod, null);
ReplicateNonInheritableAttributes(propToGen.SetMethod, setEmitter);
}
}
foreach (EventToGenerate eventToGenerate in eventToGenerates)
{
NestedClassEmitter add_nestedClass = method2Invocation[eventToGenerate.AddMethod];
MethodAttributes add_atts = ObtainMethodAttributes(eventToGenerate.AddMethod);
MethodEmitter addEmitter = eventToGenerate.Emitter.CreateAddMethod(add_atts);
ImplementProxiedMethod(targetType, addEmitter,
eventToGenerate.AddMethod, emitter,
add_nestedClass, interceptorsField,
GetTargetRef(eventToGenerate.AddMethod, mixinFields, SelfReference.Self),
ConstructorVersion.WithoutTargetMethod, null);
ReplicateNonInheritableAttributes(eventToGenerate.AddMethod, addEmitter);
NestedClassEmitter remove_nestedClass = method2Invocation[eventToGenerate.RemoveMethod];
MethodAttributes remove_atts = ObtainMethodAttributes(eventToGenerate.RemoveMethod);
MethodEmitter removeEmitter = eventToGenerate.Emitter.CreateRemoveMethod(remove_atts);
ImplementProxiedMethod(targetType, removeEmitter,
eventToGenerate.RemoveMethod, emitter,
remove_nestedClass, interceptorsField,
GetTargetRef(eventToGenerate.RemoveMethod, mixinFields, SelfReference.Self),
ConstructorVersion.WithoutTargetMethod, null);
ReplicateNonInheritableAttributes(eventToGenerate.RemoveMethod, removeEmitter);
}
#if !SILVERLIGHT
ImplementGetObjectData(emitter, interceptorsField, mixinFields, interfaces);
#endif
// Complete type initializer code body
CompleteInitCacheMethod(typeInitializer.CodeBuilder);
// Build type
type = emitter.BuildType();
InitializeStaticFields(type);
AddToCache(cacheKey, type);
}
finally
{
rwlock.ExitWriteLock();
}
return type;
}
protected override Reference GetProxyTargetReference()
{
return SelfReference.Self;
}
protected override bool CanOnlyProxyVirtual()
{
return true;
}
#if !SILVERLIGHT
protected void GenerateSerializationConstructor(ClassEmitter emitter, FieldReference interceptorField,
FieldReference[] mixinFields)
{
ArgumentReference arg1 = new ArgumentReference(typeof (SerializationInfo));
ArgumentReference arg2 = new ArgumentReference(typeof (StreamingContext));
ConstructorEmitter constr = emitter.CreateConstructor(arg1, arg2);
constr.CodeBuilder.AddStatement(
new ConstructorInvocationStatement(serializationConstructor,
arg1.ToExpression(), arg2.ToExpression()));
Type[] object_arg = new Type[] {typeof (String), typeof (Type)};
MethodInfo getValueMethod = typeof (SerializationInfo).GetMethod("GetValue", object_arg);
MethodInvocationExpression getInterceptorInvocation =
new MethodInvocationExpression(arg1, getValueMethod,
new ConstReference("__interceptors").ToExpression(),
new TypeTokenExpression(typeof (IInterceptor[])));
constr.CodeBuilder.AddStatement(new AssignStatement(
interceptorField,
new ConvertExpression(typeof (IInterceptor[]), typeof (object),
getInterceptorInvocation)));
// mixins
foreach (FieldReference mixinFieldReference in mixinFields)
{
MethodInvocationExpression getMixinInvocation =
new MethodInvocationExpression(arg1, getValueMethod,
new ConstReference(mixinFieldReference.Reference.Name).ToExpression(),
new TypeTokenExpression(mixinFieldReference.Reference.FieldType));
constr.CodeBuilder.AddStatement(new AssignStatement(
mixinFieldReference,
new ConvertExpression(mixinFieldReference.Reference.FieldType, typeof (object),
getMixinInvocation)));
}
constr.CodeBuilder.AddStatement(new ReturnStatement());
}
protected override void CustomizeGetObjectData(AbstractCodeBuilder codebuilder,
ArgumentReference arg1, ArgumentReference arg2)
{
Type[] key_and_object = new Type[] {typeof (String), typeof (Object)};
Type[] key_and_bool = new Type[] {typeof (String), typeof (bool)};
MethodInfo addValueMethod = typeof (SerializationInfo).GetMethod("AddValue", key_and_object);
MethodInfo addValueBoolMethod = typeof (SerializationInfo).GetMethod("AddValue", key_and_bool);
codebuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueBoolMethod,
new ConstReference("__delegateToBase").ToExpression(),
new ConstReference(delegateToBaseGetObjectData ? 1 : 0).
ToExpression())));
if (delegateToBaseGetObjectData)
{
MethodInfo baseGetObjectData = targetType.GetMethod("GetObjectData",
new Type[]
{typeof (SerializationInfo), typeof (StreamingContext)});
codebuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(baseGetObjectData,
arg1.ToExpression(), arg2.ToExpression())));
}
else
{
LocalReference members_ref = codebuilder.DeclareLocal(typeof (MemberInfo[]));
LocalReference data_ref = codebuilder.DeclareLocal(typeof (object[]));
MethodInfo getSerMembers = typeof (FormatterServices).GetMethod("GetSerializableMembers",
new Type[] {typeof (Type)});
MethodInfo getObjData = typeof (FormatterServices).GetMethod("GetObjectData",
new Type[] {typeof (object), typeof (MemberInfo[])});
codebuilder.AddStatement(new AssignStatement(members_ref,
new MethodInvocationExpression(null, getSerMembers,
new TypeTokenExpression(targetType))));
codebuilder.AddStatement(new AssignStatement(data_ref,
new MethodInvocationExpression(null, getObjData,
SelfReference.Self.ToExpression(),
members_ref.ToExpression())));
codebuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference("__data").ToExpression(),
data_ref.ToExpression())));
}
}
#endif
}
}