// 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.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 InterfaceProxyWithTargetGenerator : BaseProxyGenerator
{
private FieldReference targetField;
protected Dictionary method2Invocation = new Dictionary();
protected Dictionary method2methodOnTarget = new Dictionary();
public InterfaceProxyWithTargetGenerator(ModuleScope scope, Type theInterface)
: base(scope, theInterface)
{
CheckNotGenericTypeDefinition(theInterface, "theInterface");
}
public Type GenerateCode(Type proxyTargetType, Type[] interfaces, ProxyGenerationOptions options)
{
// make sure ProxyGenerationOptions is initialized
options.Initialize();
CheckNotGenericTypeDefinition(proxyTargetType, "proxyTargetType");
CheckNotGenericTypeDefinitions(interfaces, "interfaces");
Type generatedType;
SlimReaderWriterLock rwlock = Scope.RWLock;
rwlock.EnterReadLock();
CacheKey cacheKey = new CacheKey(proxyTargetType, 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();
interfaceList.Add(targetType);
if (interfaces != null)
{
interfaceList.AddRange(interfaces);
}
#if SILVERLIGHT
#warning What to do?
#else
if (!interfaceList.Contains(typeof(ISerializable)))
interfaceList.Add(typeof(ISerializable));
#endif
AddMixinInterfaces(interfaceList);
AddDefaultInterfaces(interfaceList);
Type baseType = options.BaseTypeForInterfaceProxy;
ClassEmitter emitter = BuildClassEmitter(newName, baseType, interfaceList);
CreateOptionsField(emitter);
#if SILVERLIGHT
#warning XmlIncludeAttribute is in silverlight, do we want to explore this?
#else
emitter.DefineCustomAttribute(new XmlIncludeAttribute(targetType));
emitter.DefineCustomAttribute(new SerializableAttribute());
#endif
// Custom attributes
ReplicateNonInheritableAttributes(targetType, emitter);
// Fields generations
FieldReference interceptorsField = emitter.CreateField("__interceptors", typeof(IInterceptor[]));
targetField = emitter.CreateField("__target", proxyTargetType);
#if SILVERLIGHT
#warning XmlIncludeAttribute is in silverlight, do we want to explore this?
#else
emitter.DefineCustomAttributeFor(interceptorsField, new XmlIgnoreAttribute());
emitter.DefineCustomAttributeFor(targetField, new XmlIgnoreAttribute());
#endif
// Implement builtin Interfaces
ImplementProxyTargetAccessor(targetType, emitter, interceptorsField);
// Collect methods
PropertyToGenerate[] propsToGenerate;
EventToGenerate[] eventToGenerates;
MethodInfo[] methods = CollectMethodsAndProperties(emitter, targetType, out propsToGenerate, out eventToGenerates);
if (interfaces != null && interfaces.Length != 0)
{
List tmpInterfaces = new List(interfaces);
foreach (Type inter in interfaces)
{
if (inter.IsAssignableFrom(proxyTargetType))
{
PropertyToGenerate[] tempPropsToGenerate;
EventToGenerate[] tempEventToGenerates;
MethodInfo[] methodsTemp =
CollectMethodsAndProperties(emitter, inter, out tempPropsToGenerate, out tempEventToGenerates);
PropertyToGenerate[] newPropsToGenerate =
new PropertyToGenerate[tempPropsToGenerate.Length + propsToGenerate.Length];
MethodInfo[] newMethods = new MethodInfo[methodsTemp.Length + methods.Length];
EventToGenerate[] newEvents = new EventToGenerate[eventToGenerates.Length + tempEventToGenerates.Length];
Array.Copy(methods, newMethods, methods.Length);
Array.Copy(methodsTemp, 0, newMethods, methods.Length, methodsTemp.Length);
Array.Copy(propsToGenerate, newPropsToGenerate, propsToGenerate.Length);
Array.Copy(tempPropsToGenerate, 0, newPropsToGenerate, propsToGenerate.Length, tempPropsToGenerate.Length);
Array.Copy(eventToGenerates, newEvents, eventToGenerates.Length);
Array.Copy(tempEventToGenerates, 0, newEvents, eventToGenerates.Length, tempEventToGenerates.Length);
methods = newMethods;
propsToGenerate = newPropsToGenerate;
eventToGenerates = newEvents;
tmpInterfaces.Remove(inter);
}
}
interfaces = tmpInterfaces.ToArray();
}
RegisterMixinMethodsAndProperties(emitter, ref methods, ref propsToGenerate, ref eventToGenerates);
options.Hook.MethodsInspected();
// Constructor
ConstructorEmitter typeInitializer = GenerateStaticConstructor(emitter);
if (!proxyTargetType.IsInterface)
{
CacheMethodTokens(emitter, MethodFinder.GetAllInstanceMethods(proxyTargetType,
BindingFlags.Public | BindingFlags.Instance),
typeInitializer);
}
CreateInitializeCacheMethodBody(proxyTargetType, methods, emitter, typeInitializer);
FieldReference[] mixinFields = AddMixinFields(emitter);
List fields = new List(mixinFields);
fields.Add(interceptorsField);
fields.Add(targetField);
GenerateConstructors(emitter, baseType, fields.ToArray());
// GenerateParameterlessConstructor(emitter, interceptorsField, baseType);
// Implement interfaces
if (interfaces != null && interfaces.Length != 0)
{
foreach (Type inter in interfaces)
{
ImplementBlankInterface(targetType, inter, emitter, interceptorsField, typeInitializer, AllowChangeTarget);
}
}
// Create invocation types
foreach (MethodInfo method in methods)
{
CreateInvocationForMethod(emitter, method, proxyTargetType);
}
// 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 = (NestedClassEmitter)method2Invocation[method];
MethodEmitter newProxiedMethod = CreateProxiedMethod(
targetType, method, emitter, nestedClass, interceptorsField, GetTargetRef(method, mixinFields, targetField),
ConstructorVersion.WithTargetMethod, (MethodInfo)method2methodOnTarget[method]);
ReplicateNonInheritableAttributes(method, newProxiedMethod);
method2Emitter[method] = newProxiedMethod;
}
foreach (PropertyToGenerate propToGen in propsToGenerate)
{
if (propToGen.CanRead)
{
NestedClassEmitter nestedClass = (NestedClassEmitter)method2Invocation[propToGen.GetMethod];
MethodAttributes atts = ObtainMethodAttributes(propToGen.GetMethod);
MethodEmitter getEmitter = propToGen.Emitter.CreateGetMethod(atts);
ImplementProxiedMethod(targetType, getEmitter,
propToGen.GetMethod, emitter,
nestedClass, interceptorsField, GetTargetRef(propToGen.GetMethod, mixinFields, targetField),
ConstructorVersion.WithTargetMethod,
(MethodInfo)method2methodOnTarget[propToGen.GetMethod]);
ReplicateNonInheritableAttributes(propToGen.GetMethod, getEmitter);
// emitter.TypeBuilder.DefineMethodOverride(getEmitter.MethodBuilder, propToGen.GetMethod);
}
if (propToGen.CanWrite)
{
NestedClassEmitter nestedClass = (NestedClassEmitter)method2Invocation[propToGen.SetMethod];
MethodAttributes atts = ObtainMethodAttributes(propToGen.SetMethod);
MethodEmitter setEmitter = propToGen.Emitter.CreateSetMethod(atts);
ImplementProxiedMethod(targetType, setEmitter,
propToGen.SetMethod, emitter,
nestedClass, interceptorsField, GetTargetRef(propToGen.SetMethod, mixinFields, targetField),
ConstructorVersion.WithTargetMethod,
(MethodInfo)method2methodOnTarget[propToGen.SetMethod]);
ReplicateNonInheritableAttributes(propToGen.SetMethod, setEmitter);
// emitter.TypeBuilder.DefineMethodOverride(setEmitter.MethodBuilder, propToGen.SetMethod);
}
}
foreach (EventToGenerate eventToGenerate in eventToGenerates)
{
NestedClassEmitter add_nestedClass = (NestedClassEmitter)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, targetField),
ConstructorVersion.WithTargetMethod,
(MethodInfo)method2methodOnTarget[eventToGenerate.AddMethod]);
ReplicateNonInheritableAttributes(eventToGenerate.AddMethod, addEmitter);
NestedClassEmitter remove_nestedClass = (NestedClassEmitter)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, targetField),
ConstructorVersion.WithTargetMethod,
(MethodInfo)method2methodOnTarget[eventToGenerate.RemoveMethod]);
ReplicateNonInheritableAttributes(eventToGenerate.RemoveMethod, removeEmitter);
}
#if SILVERLIGHT
#warning What to do?
#else
ImplementGetObjectData(emitter, interceptorsField, mixinFields, interfaces);
#endif
// Complete Initialize
CompleteInitCacheMethod(typeInitializer.CodeBuilder);
// Crosses fingers and build type
generatedType = emitter.BuildType();
InitializeStaticFields(generatedType);
/*foreach (MethodInfo m in TypeFinder.GetMethods(generatedType, BindingFlags.Instance | BindingFlags.Public))
{
ParameterInfo[] parameters = m.GetParameters();
// Console.WriteLine(m.Name);
for (int i = 0; i < parameters.Length; i++)
{
ParameterInfo paramInfo = parameters[i];
// Console.WriteLine("{0} {1} {2} {3}", paramInfo.Name, paramInfo.ParameterType, paramInfo.Attributes, paramInfo.Position);
// Console.WriteLine("{0} {1} {2} {3}", paramInfo2.Name, paramInfo2.ParameterType, paramInfo2.Attributes, paramInfo2.Position);
}
}
*/
AddToCache(cacheKey, generatedType);
}
finally
{
rwlock.ExitWriteLock();
}
return generatedType;
}
protected virtual bool AllowChangeTarget
{
get { return false; }
}
protected virtual void CreateInvocationForMethod(ClassEmitter emitter, MethodInfo method, Type proxyTargetType)
{
MethodInfo methodOnTarget = FindMethodOnTargetType(method, proxyTargetType, true);
method2methodOnTarget[method] = methodOnTarget;
method2Invocation[method] = BuildInvocationNestedType(emitter, proxyTargetType,
IsMixinMethod(method) ? method.DeclaringType : proxyTargetType,
method, methodOnTarget,
ConstructorVersion.WithTargetMethod,
AllowChangeTarget);
}
///
/// Finds the type of the method on target.
///
/// The method on interface.
/// Type of the proxy target.
/// /// if set to true will check implementation on mixins.
///
protected MethodInfo FindMethodOnTargetType(MethodInfo methodOnInterface, Type proxyTargetType, bool checkMixins)
{
// The code below assumes that the target
// class uses the same generic arguments
// as the interface generic arguments
MemberInfo[] members = proxyTargetType.FindMembers(MemberTypes.Method,
BindingFlags.Public | BindingFlags.Instance,
delegate(MemberInfo mi, object criteria)
{
if (mi.Name != criteria.ToString()) return false;
MethodInfo methodInfo = (MethodInfo)mi;
return IsEquivalentMethod(methodInfo, methodOnInterface);
}, methodOnInterface.Name);
if (members.Length == 0)
{
// Before throwing an exception, we look for an explicit
// interface method implementation
MethodInfo[] privateMethods =
MethodFinder.GetAllInstanceMethods(proxyTargetType, BindingFlags.NonPublic | BindingFlags.Instance);
foreach (MethodInfo methodInfo in privateMethods)
{
// We make sure it is a method used for explicit implementation
if (!methodInfo.IsFinal || !methodInfo.IsVirtual || !methodInfo.IsHideBySig)
{
continue;
}
if (IsEquivalentMethod(methodInfo, methodOnInterface))
{
throw new GeneratorException(String.Format("DynamicProxy cannot create an interface (with target) " +
"proxy for '{0}' as the target '{1}' has an explicit implementation of one of the methods exposed by the interface. " +
"The runtime prevents use from invoking the private method on the target. Method {2}",
methodOnInterface.DeclaringType.Name, methodInfo.DeclaringType.Name,
methodInfo.Name));
}
}
}
if (members.Length > 1)
{
throw new GeneratorException("Found more than one method on target " + proxyTargetType.FullName + " matching " +
methodOnInterface.Name);
}
else if (members.Length == 0)
{
if (checkMixins && IsMixinMethod(methodOnInterface))
{
return FindMethodOnTargetType(methodOnInterface, method2MixinType[methodOnInterface], false);
}
throw new GeneratorException("Could not find a matching method on " + proxyTargetType.FullName + ". Method " +
methodOnInterface.Name);
}
return (MethodInfo)members[0];
}
///
/// Checks whether the given types are the same. This is
/// more complicated than it looks.
///
///
///
///
public static bool IsTypeEquivalent(Type sourceType, Type targetType)
{
if (sourceType.IsGenericParameter)
{
if (sourceType.Name != targetType.Name)
{
return false;
}
}
else
{
if (sourceType.IsGenericType != targetType.IsGenericType)
{
return false;
}
else if (sourceType.IsArray != targetType.IsArray)
{
return false;
}
if (sourceType.IsGenericType)
{
if (sourceType.GetGenericTypeDefinition() != targetType.GetGenericTypeDefinition())
{
return false;
}
// Compare generic arguments
Type[] sourceGenArgs = sourceType.GetGenericArguments();
Type[] targetGenArgs = targetType.GetGenericArguments();
for (int i = 0; i < sourceGenArgs.Length; i++)
{
if (!IsTypeEquivalent(sourceGenArgs[i], targetGenArgs[i]))
{
return false;
}
}
}
else if (sourceType.IsArray)
{
Type sourceArrayType = sourceType.GetElementType();
Type targetArrayType = targetType.GetElementType();
if (!IsTypeEquivalent(sourceArrayType, targetArrayType))
{
return false;
}
int sourceRank = sourceType.GetArrayRank();
int targetRank = targetType.GetArrayRank();
if (sourceRank != targetRank)
{
return false;
}
}
else if (sourceType != targetType)
{
return false;
}
}
return true;
}
protected override Reference GetProxyTargetReference()
{
return targetField;
}
protected override bool CanOnlyProxyVirtual()
{
return false;
}
private static bool IsEquivalentMethod(MethodInfo methodInfo, MethodInfo methodOnInterface)
{
// Check return type equivalence
if (!IsTypeEquivalent(methodInfo.ReturnType, methodOnInterface.ReturnType))
{
return false;
}
// Check parameters equivalence
ParameterInfo[] sourceParams = methodOnInterface.GetParameters();
ParameterInfo[] targetParams = methodInfo.GetParameters();
if (sourceParams.Length != targetParams.Length)
{
return false;
}
for (int i = 0; i < sourceParams.Length; i++)
{
Type sourceParamType = sourceParams[i].ParameterType;
Type targetParamType = targetParams[i].ParameterType;
if (!IsTypeEquivalent(sourceParamType, targetParamType))
{
return false;
}
}
return true;
}
#if SILVERLIGHT
#warning What to do?
#else
protected override void CustomizeGetObjectData(AbstractCodeBuilder codebuilder, ArgumentReference arg1,
ArgumentReference arg2)
{
Type[] key_and_object = new Type[] { typeof(String), typeof(Object) };
Type[] key_and_int = new Type[] { typeof(String), typeof(int) };
Type[] key_and_string = new Type[] { typeof(String), typeof(string) };
MethodInfo addValueMethod = typeof(SerializationInfo).GetMethod("AddValue", key_and_object);
MethodInfo addIntMethod = typeof(SerializationInfo).GetMethod("AddValue", key_and_int);
MethodInfo addStringMethod = typeof(SerializationInfo).GetMethod("AddValue", key_and_string);
codebuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference("__target").ToExpression(),
targetField.ToExpression())));
codebuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference("__targetFieldType").ToExpression(),
new ConstReference(
targetField.Reference.FieldType.AssemblyQualifiedName).
ToExpression())));
codebuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addIntMethod,
new ConstReference("__interface_generator_type").
ToExpression(),
new ConstReference((int)GeneratorType).ToExpression())));
codebuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addStringMethod,
new ConstReference("__theInterface").ToExpression(),
new ConstReference(targetType.AssemblyQualifiedName).
ToExpression())));
}
#endif
protected virtual InterfaceGeneratorType GeneratorType
{
get { return InterfaceGeneratorType.WithTarget; }
}
}
///
/// This is used by the ProxyObjectReference class durin de-serialiation, to know
/// which generator it should use
///
public enum InterfaceGeneratorType
{
WithTarget = 1,
WithoutTarget = 2,
WithTargetInterface = 3
}
}