// 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.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using Castle.Core.Interceptor;
using Castle.DynamicProxy.Generators.Emitters;
using Castle.DynamicProxy.Generators.Emitters.CodeBuilders;
using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
#if SILVERLIGHT
using Castle.DynamicProxy.SilverlightExtensions;
#else
using Castle.DynamicProxy.Serialization;
#endif
public enum ConstructorVersion
{
WithTargetMethod,
WithoutTargetMethod
}
///
/// Base class that exposes the common functionalities
/// to proxy generation.
///
///
/// TODO:
/// - Use the interceptor selector if provided
/// - Add tests and fixes for 'leaking this' problem
///
public abstract class BaseProxyGenerator
{
private static MethodInfo invocation_getArgumentsMethod = typeof(AbstractInvocation).GetMethod("get_Arguments");
private readonly ModuleScope scope;
private int nestedCounter, callbackCounter;
private int fieldCount = 1;
private FieldReference typeTokenField;
private Dictionary method2TokenField = new Dictionary();
private IList generateNewSlot = new List();
private ProxyGenerationOptions proxyGenerationOptions;
private FieldReference proxyGenerationOptionsField;
protected readonly Type targetType;
protected ConstructorInfo serializationConstructor;
protected IList methodsToSkip = new List();
protected Dictionary method2MixinType = new Dictionary();
protected Dictionary interface2MixinFieldReference = new Dictionary();
protected BaseProxyGenerator(ModuleScope scope, Type targetType)
{
this.scope = scope;
this.targetType = targetType;
}
public ProxyGenerationOptions ProxyGenerationOptions
{
get
{
if (proxyGenerationOptions == null)
{
throw new InvalidOperationException("ProxyGenerationOptions must be set before being retrieved.");
}
return proxyGenerationOptions;
}
}
protected void SetGenerationOptions(ProxyGenerationOptions options)
{
if (proxyGenerationOptions != null)
{
throw new InvalidOperationException("ProxyGenerationOptions can only be set once.");
}
proxyGenerationOptions = options;
}
protected void CreateOptionsField(ClassEmitter emitter)
{
proxyGenerationOptionsField = emitter.CreateStaticField("proxyGenerationOptions", typeof(ProxyGenerationOptions));
}
protected void InitializeStaticFields(Type builtType)
{
builtType.GetField(proxyGenerationOptionsField.Reference.Name).SetValue(null, ProxyGenerationOptions);
}
protected void CheckNotGenericTypeDefinition(Type type, string argumentName)
{
if (type != null && type.IsGenericTypeDefinition)
{
throw new ArgumentException("Type cannot be a generic type definition. Type: " + type.FullName, argumentName);
}
}
protected void CheckNotGenericTypeDefinitions(IEnumerable types, string argumentName)
{
if (types != null)
{
foreach (Type t in types)
{
CheckNotGenericTypeDefinition(t, argumentName);
}
}
}
protected ModuleScope Scope
{
get { return scope; }
}
protected virtual ClassEmitter BuildClassEmitter(String typeName, Type parentType, IList interfaceList)
{
CheckNotGenericTypeDefinition(parentType, "parentType");
CheckNotGenericTypeDefinitions(interfaceList, "interfaceList");
Type[] interfaces = new Type[interfaceList.Count];
interfaceList.CopyTo(interfaces, 0);
return BuildClassEmitter(typeName, parentType, interfaces);
}
protected virtual ClassEmitter BuildClassEmitter(String typeName, Type parentType, Type[] interfaces)
{
CheckNotGenericTypeDefinition(parentType, "parentType");
CheckNotGenericTypeDefinitions(interfaces, "interfaceList");
if (interfaces == null)
{
interfaces = new Type[0];
}
return new ClassEmitter(Scope, typeName, parentType, interfaces);
}
///
/// Used by dinamically implement
///
///
protected abstract Reference GetProxyTargetReference();
protected abstract bool CanOnlyProxyVirtual();
#region Cache related
protected Type GetFromCache(CacheKey key)
{
return scope.GetFromCache(key);
}
protected void AddToCache(CacheKey key, Type type)
{
scope.RegisterInCache(key, type);
}
#endregion
protected MethodEmitter CreateProxiedMethod(
Type targetType,
MethodInfo method,
ClassEmitter emitter,
NestedClassEmitter invocationImpl,
FieldReference interceptorsField,
Reference targetRef,
ConstructorVersion version,
MethodInfo methodOnTarget)
{
CheckNotGenericTypeDefinition(targetType, "targetType");
MethodAttributes atts = ObtainMethodAttributes(method);
MethodEmitter methodEmitter = emitter.CreateMethod(method.Name, atts);
return
ImplementProxiedMethod(targetType,
methodEmitter,
method,
emitter,
invocationImpl,
interceptorsField,
targetRef,
version,
methodOnTarget);
}
protected void ImplementBlankInterface(
Type targetType,
Type _interface,
ClassEmitter emitter,
FieldReference interceptorsField,
ConstructorEmitter typeInitializerConstructor)
{
ImplementBlankInterface(targetType, _interface, emitter, interceptorsField, typeInitializerConstructor, false);
}
protected void ImplementBlankInterface(
Type targetType,
Type _interface,
ClassEmitter emitter,
FieldReference interceptorsField,
ConstructorEmitter typeInitializerConstructor,
bool allowChangeTarget)
{
CheckNotGenericTypeDefinition(targetType, "targetType");
CheckNotGenericTypeDefinition(_interface, "_interface");
PropertyToGenerate[] propsToGenerate;
EventToGenerate[] eventsToGenerate;
MethodInfo[] methods =
CollectMethodsAndProperties(emitter, _interface, false, out propsToGenerate, out eventsToGenerate);
Dictionary method2Invocation = new Dictionary();
foreach (MethodInfo method in methods)
{
AddFieldToCacheMethodTokenAndStatementsToInitialize(method, typeInitializerConstructor, emitter);
method2Invocation[method] =
BuildInvocationNestedType(emitter,
targetType,
allowChangeTarget ? _interface : emitter.TypeBuilder,
method,
allowChangeTarget ? method : null,
ConstructorVersion.WithoutTargetMethod,
allowChangeTarget);
}
foreach (MethodInfo method in methods)
{
if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")))
{
continue;
}
NestedClassEmitter nestedClass = method2Invocation[method];
MethodEmitter newProxiedMethod =
CreateProxiedMethod(targetType,
method,
emitter,
nestedClass,
interceptorsField,
SelfReference.Self,
ConstructorVersion.WithoutTargetMethod,
null);
ReplicateNonInheritableAttributes(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);
ImplementProxiedMethod(targetType,
getEmitter,
propToGen.GetMethod,
emitter,
nestedClass,
interceptorsField,
SelfReference.Self,
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);
ImplementProxiedMethod(targetType,
setEmitter,
propToGen.SetMethod,
emitter,
nestedClass,
interceptorsField,
SelfReference.Self,
ConstructorVersion.WithoutTargetMethod,
null);
ReplicateNonInheritableAttributes(propToGen.SetMethod, setEmitter);
}
}
foreach (EventToGenerate eventToGenerate in eventsToGenerate)
{
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,
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,
SelfReference.Self,
ConstructorVersion.WithoutTargetMethod,
null);
ReplicateNonInheritableAttributes(eventToGenerate.RemoveMethod, removeEmitter);
}
}
protected MethodEmitter ImplementProxiedMethod(
Type targetType,
MethodEmitter methodEmitter,
MethodInfo method,
ClassEmitter emitter,
NestedClassEmitter invocationImpl,
FieldReference interceptorsField,
Reference targetRef,
ConstructorVersion version,
MethodInfo methodOnTarget)
{
CheckNotGenericTypeDefinition(targetType, "targetType");
methodEmitter.CopyParametersAndReturnTypeFrom(method, emitter);
TypeReference[] dereferencedArguments = IndirectReference.WrapIfByRef(methodEmitter.Arguments);
Type iinvocation = invocationImpl.TypeBuilder;
Trace.Assert(method.IsGenericMethod == iinvocation.IsGenericTypeDefinition);
bool isGenericInvocationClass = false;
Type[] genericMethodArgs = new Type[0];
if (method.IsGenericMethod)
{
// bind generic method arguments to invocation's type arguments
genericMethodArgs = methodEmitter.MethodBuilder.GetGenericArguments();
iinvocation = iinvocation.MakeGenericType(genericMethodArgs);
isGenericInvocationClass = true;
}
LocalReference invocationImplLocal = methodEmitter.CodeBuilder.DeclareLocal(iinvocation);
// TODO: Initialize iinvocation instance
// with ordinary arguments and in and out arguments
Expression interceptors;
// if (useSelector)
{
// TODO: Generate code that checks the return of selector
// if no interceptors is returned, should we invoke the base.Method directly?
}
// else
{
interceptors = interceptorsField.ToExpression();
}
Expression typeTokenFieldExp = typeTokenField.ToExpression();
Expression methodInfoTokenExp;
if (method2TokenField.ContainsKey(method)) // Token is in the cache
{
methodInfoTokenExp = ((FieldReference)method2TokenField[method]).ToExpression();
}
else
{
// Not in the cache: generic method
methodInfoTokenExp = new MethodTokenExpression(method.MakeGenericMethod(genericMethodArgs));
}
ConstructorInfo constructor = invocationImpl.Constructors[0].ConstructorBuilder;
if (isGenericInvocationClass)
{
constructor = TypeBuilder.GetConstructor(iinvocation, invocationImpl.Constructors[0].ConstructorBuilder);
}
NewInstanceExpression newInvocImpl;
if (version == ConstructorVersion.WithTargetMethod)
{
Expression methodOnTargetTokenExp;
if (method2TokenField.ContainsKey(methodOnTarget)) // Token is in the cache
{
methodOnTargetTokenExp = ((FieldReference)method2TokenField[methodOnTarget]).ToExpression();
}
else
{
// Not in the cache: generic method
methodOnTargetTokenExp = new MethodTokenExpression(methodOnTarget.MakeGenericMethod(genericMethodArgs));
}
newInvocImpl =
new NewInstanceExpression(constructor,
targetRef.ToExpression(),
interceptors,
typeTokenFieldExp,
methodOnTargetTokenExp,
methodInfoTokenExp,
new ReferencesToObjectArrayExpression(dereferencedArguments),
SelfReference.Self.ToExpression());
}
else
{
newInvocImpl =
new NewInstanceExpression(constructor,
targetRef.ToExpression(),
interceptors,
typeTokenFieldExp,
methodInfoTokenExp,
new ReferencesToObjectArrayExpression(dereferencedArguments),
SelfReference.Self.ToExpression());
}
methodEmitter.CodeBuilder.AddStatement(new AssignStatement(invocationImplLocal, newInvocImpl));
if (method.ContainsGenericParameters)
{
EmitLoadGenricMethodArguments(methodEmitter, method.MakeGenericMethod(genericMethodArgs), invocationImplLocal);
}
methodEmitter.CodeBuilder.AddStatement(
new ExpressionStatement(new MethodInvocationExpression(invocationImplLocal, Constants.AbstractInvocationProceed)));
CopyOutAndRefParameters(dereferencedArguments, invocationImplLocal, method, methodEmitter);
if (method.ReturnType != typeof(void))
{
// Emit code to return with cast from ReturnValue
MethodInvocationExpression getRetVal =
new MethodInvocationExpression(invocationImplLocal, typeof(AbstractInvocation).GetMethod("get_ReturnValue"));
methodEmitter.CodeBuilder.AddStatement(
new ReturnStatement(new ConvertExpression(methodEmitter.ReturnType, getRetVal)));
}
else
{
methodEmitter.CodeBuilder.AddStatement(new ReturnStatement());
}
return methodEmitter;
}
private void EmitLoadGenricMethodArguments(MethodEmitter methodEmitter, MethodInfo method,
LocalReference invocationImplLocal)
{
#if SILVERLIGHT
Type[] genericParameters =
Castle.Core.Extensions.SilverlightExtensions.FindAll(method.GetGenericArguments(), delegate(Type t) { return t.IsGenericParameter; });
#else
Type[] genericParameters =
Array.FindAll(method.GetGenericArguments(), delegate(Type t) { return t.IsGenericParameter; });
#endif
LocalReference genericParamsArrayLocal = methodEmitter.CodeBuilder.DeclareLocal(typeof(Type[]));
methodEmitter.CodeBuilder.AddStatement(
new AssignStatement(genericParamsArrayLocal, new NewArrayExpression(genericParameters.Length, typeof(Type))));
for (int i = 0; i < genericParameters.Length; ++i)
{
methodEmitter.CodeBuilder.AddStatement(
new AssignArrayStatement(genericParamsArrayLocal, i, new TypeTokenExpression(genericParameters[i])));
}
MethodInfo setGenericsArgs =
typeof(AbstractInvocation).GetMethod("SetGenericMethodArguments", new Type[] { typeof(Type[]) });
methodEmitter.CodeBuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(invocationImplLocal, setGenericsArgs,
new ReferenceExpression(
genericParamsArrayLocal))));
}
private static void CopyOutAndRefParameters(
TypeReference[] dereferencedArguments, LocalReference invocationImplLocal, MethodInfo method,
MethodEmitter methodEmitter)
{
ParameterInfo[] parameters = method.GetParameters();
bool hasByRefParam = false;
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].ParameterType.IsByRef)
hasByRefParam = true;
}
if (!hasByRefParam)
return; //saving the need to create locals if there is no need
LocalReference invocationArgs = methodEmitter.CodeBuilder.DeclareLocal(typeof(object[]));
methodEmitter.CodeBuilder.AddStatement(
new AssignStatement(invocationArgs,
new MethodInvocationExpression(invocationImplLocal, invocation_getArgumentsMethod)
)
);
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].ParameterType.IsByRef)
{
methodEmitter.CodeBuilder.AddStatement(
new AssignStatement(dereferencedArguments[i],
new ConvertExpression(dereferencedArguments[i].Type,
new LoadRefArrayElementExpression(i, invocationArgs)
)
));
}
}
}
protected void GenerateConstructor(ClassEmitter emitter, params FieldReference[] fields)
{
GenerateConstructor(emitter, null, fields);
}
protected void GenerateConstructor(
ClassEmitter emitter, ConstructorInfo baseConstructor, params FieldReference[] fields)
{
ArgumentReference[] args;
ParameterInfo[] baseConstructorParams = null;
if (baseConstructor != null)
{
baseConstructorParams = baseConstructor.GetParameters();
}
if (baseConstructorParams != null && baseConstructorParams.Length != 0)
{
args = new ArgumentReference[fields.Length + baseConstructorParams.Length];
int offset = fields.Length;
for (int i = offset; i < offset + baseConstructorParams.Length; i++)
{
ParameterInfo paramInfo = baseConstructorParams[i - offset];
args[i] = new ArgumentReference(paramInfo.ParameterType);
}
}
else
{
args = new ArgumentReference[fields.Length];
}
for (int i = 0; i < fields.Length; i++)
{
args[i] = new ArgumentReference(fields[i].Reference.FieldType);
}
ConstructorEmitter constructor = emitter.CreateConstructor(args);
for (int i = 0; i < fields.Length; i++)
{
constructor.CodeBuilder.AddStatement(new AssignStatement(fields[i], args[i].ToExpression()));
}
// Invoke base constructor
if (baseConstructor != null)
{
ArgumentReference[] slice = new ArgumentReference[baseConstructorParams.Length];
Array.Copy(args, fields.Length, slice, 0, baseConstructorParams.Length);
constructor.CodeBuilder.InvokeBaseConstructor(baseConstructor, slice);
}
else
{
constructor.CodeBuilder.InvokeBaseConstructor();
}
// Invoke initialize method
// constructor.CodeBuilder.AddStatement(
// new ExpressionStatement(new MethodInvocationExpression(SelfReference.Self, initCacheMethod)));
constructor.CodeBuilder.AddStatement(new ReturnStatement());
}
///
/// Generates a parameters constructor that initializes the proxy
/// state with just to make it non-null.
///
/// This constructor is important to allow proxies to be XML serializable
///
///
protected void GenerateParameterlessConstructor(ClassEmitter emitter, Type baseClass, FieldReference interceptorField)
{
// Check if the type actually has a default constructor
ConstructorInfo defaultConstructor = baseClass.GetConstructor(BindingFlags.Public, null, Type.EmptyTypes, null);
if (defaultConstructor == null)
{
defaultConstructor = baseClass.GetConstructor(BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (defaultConstructor == null || defaultConstructor.IsPrivate)
{
return;
}
}
ConstructorEmitter constructor = emitter.CreateConstructor();
// initialize fields with an empty interceptor
constructor.CodeBuilder.AddStatement(
new AssignStatement(interceptorField, new NewArrayExpression(1, typeof(IInterceptor))));
constructor.CodeBuilder.AddStatement(
new AssignArrayStatement(interceptorField, 0, new NewInstanceExpression(typeof(StandardInterceptor), new Type[0])));
// Invoke base constructor
constructor.CodeBuilder.InvokeBaseConstructor(defaultConstructor);
constructor.CodeBuilder.AddStatement(new ReturnStatement());
}
#region First level attributes
protected MethodAttributes ObtainMethodAttributes(MethodInfo method)
{
MethodAttributes atts = MethodAttributes.Virtual;
if (ShouldCreateNewSlot(method))
{
atts |= MethodAttributes.NewSlot;
}
if (method.IsPublic)
{
atts |= MethodAttributes.Public;
}
if (method.IsHideBySig)
{
atts |= MethodAttributes.HideBySig;
}
if (InternalsHelper.IsInternal(method) && InternalsHelper.IsInternalToDynamicProxy(method.DeclaringType.Assembly))
{
atts |= MethodAttributes.Assembly;
}
if (method.IsFamilyAndAssembly)
{
atts |= MethodAttributes.FamANDAssem;
}
else if (method.IsFamilyOrAssembly)
{
atts |= MethodAttributes.FamORAssem;
}
else if (method.IsFamily)
{
atts |= MethodAttributes.Family;
}
if (method.Name.StartsWith("set_") || method.Name.StartsWith("get_"))
{
atts |= MethodAttributes.SpecialName;
}
return atts;
}
private PropertyAttributes ObtainPropertyAttributes(PropertyInfo property)
{
PropertyAttributes atts = PropertyAttributes.None;
return atts;
}
#endregion
protected MethodBuilder CreateCallbackMethod(ClassEmitter emitter, MethodInfo methodInfo, MethodInfo methodOnTarget)
{
MethodInfo targetMethod = methodOnTarget != null ? methodOnTarget : methodInfo;
if (targetMethod.IsAbstract)
return null;
// MethodBuild creation
MethodAttributes atts = MethodAttributes.Family;
String name = methodInfo.Name + "_callback_" + ++callbackCounter;
MethodEmitter callBackMethod = emitter.CreateMethod(name, atts);
callBackMethod.CopyParametersAndReturnTypeFrom(targetMethod, emitter);
// Generic definition
if (targetMethod.IsGenericMethod)
{
targetMethod = targetMethod.MakeGenericMethod(callBackMethod.GenericTypeParams);
}
// Parameters exp
Expression[] exps = new Expression[callBackMethod.Arguments.Length];
for (int i = 0; i < callBackMethod.Arguments.Length; i++)
{
exps[i] = callBackMethod.Arguments[i].ToExpression();
}
// invocation on base class
callBackMethod.CodeBuilder.AddStatement(
new ReturnStatement(new MethodInvocationExpression(GetProxyTargetReference(), targetMethod, exps)));
return callBackMethod.MethodBuilder;
}
#region IInvocation related
///
/// If callbackMethod is null the InvokeOnTarget implementation
/// is just the code to throw an exception
///
///
///
///
///
///
///
///
protected NestedClassEmitter BuildInvocationNestedType(
ClassEmitter emitter,
Type targetType,
Type targetForInvocation,
MethodInfo methodInfo,
MethodInfo callbackMethod,
ConstructorVersion version)
{
CheckNotGenericTypeDefinition(targetType, "targetType");
CheckNotGenericTypeDefinition(targetForInvocation, "targetForInvocation");
return
BuildInvocationNestedType(emitter, targetType, targetForInvocation, methodInfo, callbackMethod, version, false);
}
///
/// If callbackMethod is null the InvokeOnTarget implementation
/// is just the code to throw an exception
///
///
///
///
///
///
///
/// If true the invocation will implement the IChangeProxyTarget interface
///
protected NestedClassEmitter BuildInvocationNestedType(
ClassEmitter emitter,
Type targetType,
Type targetForInvocation,
MethodInfo methodInfo,
MethodInfo callbackMethod,
ConstructorVersion version,
bool allowChangeTarget)
{
CheckNotGenericTypeDefinition(targetType, "targetType");
CheckNotGenericTypeDefinition(targetForInvocation, "targetForInvocation");
nestedCounter++;
Type[] interfaces = new Type[0];
if (allowChangeTarget)
{
interfaces = new Type[] { typeof(IChangeProxyTarget) };
}
NestedClassEmitter nested =
new NestedClassEmitter(emitter,
"Invocation" + methodInfo.Name + "_" + nestedCounter.ToString(),
typeof(AbstractInvocation),
interfaces);
// invocation only needs to mirror the generic parameters of the MethodInfo
// targetType cannot be a generic type definition
nested.CreateGenericParameters(methodInfo.GetGenericArguments());
// Create the invocation fields
FieldReference targetRef = nested.CreateField("target", targetForInvocation);
// Create constructor
CreateIInvocationConstructor(targetForInvocation, nested, targetRef, version);
if (allowChangeTarget)
{
ArgumentReference argument1 = new ArgumentReference(typeof(object));
MethodEmitter methodEmitter =
nested.CreateMethod("ChangeInvocationTarget", MethodAttributes.Public | MethodAttributes.Virtual,
typeof(void), argument1);
methodEmitter.CodeBuilder.AddStatement(
new AssignStatement(targetRef,
new ConvertExpression(targetForInvocation, argument1.ToExpression())
)
);
methodEmitter.CodeBuilder.AddStatement(new ReturnStatement());
}
// InvokeMethodOnTarget implementation
if (callbackMethod != null)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
CreateIInvocationInvokeOnTarget(emitter, nested, parameters, targetRef, callbackMethod);
}
else if (IsMixinMethod(methodInfo))
{
ParameterInfo[] parameters = methodInfo.GetParameters();
CreateIInvocationInvokeOnTarget(emitter, nested, parameters, targetRef, methodInfo);
}
else
{
CreateEmptyIInvocationInvokeOnTarget(nested);
}
#if !SILVERLIGHT
nested.DefineCustomAttribute(new SerializableAttribute());
#endif
return nested;
}
protected void CreateIInvocationInvokeOnTarget(
ClassEmitter targetTypeEmitter,
NestedClassEmitter nested,
ParameterInfo[] parameters,
FieldReference targetField,
MethodInfo callbackMethod)
{
const MethodAttributes methodAtts = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual;
MethodEmitter method =
nested.CreateMethod("InvokeMethodOnTarget", methodAtts, typeof(void));
Expression[] args = new Expression[parameters.Length];
// Idea: instead of grab parameters one by one
// we should grab an array
Dictionary byRefArguments = new Dictionary();
for (int i = 0; i < parameters.Length; i++)
{
ParameterInfo param = parameters[i];
Type paramType = param.ParameterType;
if (HasGenericParameters(paramType))
{
paramType = paramType.GetGenericTypeDefinition().MakeGenericType(nested.GetGenericArgumentsFor(paramType));
}
else if (paramType.IsGenericParameter)
{
paramType = nested.GetGenericArgument(paramType.Name);
}
if (paramType.IsByRef)
{
LocalReference localReference = method.CodeBuilder.DeclareLocal(paramType.GetElementType());
method.CodeBuilder.AddStatement(
new AssignStatement(localReference,
new ConvertExpression(paramType.GetElementType(),
new MethodInvocationExpression(SelfReference.Self,
typeof(AbstractInvocation).GetMethod(
"GetArgumentValue"),
new LiteralIntExpression(i)))));
ByRefReference byRefReference = new ByRefReference(localReference);
args[i] = new ReferenceExpression(byRefReference);
byRefArguments[i] = localReference;
}
else
{
args[i] =
new ConvertExpression(paramType,
new MethodInvocationExpression(SelfReference.Self,
typeof(AbstractInvocation).GetMethod("GetArgumentValue"),
new LiteralIntExpression(i)));
}
}
MethodInvocationExpression baseMethodInvExp;
if (callbackMethod.IsGenericMethod)
{
callbackMethod = callbackMethod.MakeGenericMethod(nested.GetGenericArgumentsFor(callbackMethod));
}
baseMethodInvExp = new MethodInvocationExpression(targetField, callbackMethod, args);
baseMethodInvExp.VirtualCall = true;
LocalReference ret_local = null;
if (callbackMethod.ReturnType != typeof(void))
{
if (callbackMethod.ReturnType.IsGenericParameter)
{
ret_local = method.CodeBuilder.DeclareLocal(nested.GetGenericArgument(callbackMethod.ReturnType.Name));
}
else if (HasGenericParameters(callbackMethod.ReturnType))
{
ret_local =
method.CodeBuilder.DeclareLocal(
callbackMethod.ReturnType.GetGenericTypeDefinition().MakeGenericType(
nested.GetGenericArgumentsFor(callbackMethod.ReturnType)));
}
else
{
ret_local = method.CodeBuilder.DeclareLocal(callbackMethod.ReturnType);
}
method.CodeBuilder.AddStatement(new AssignStatement(ret_local, baseMethodInvExp));
}
else
{
method.CodeBuilder.AddStatement(new ExpressionStatement(baseMethodInvExp));
}
foreach (KeyValuePair byRefArgument in byRefArguments)
{
int index = (int)byRefArgument.Key;
LocalReference localReference = (LocalReference)byRefArgument.Value;
method.CodeBuilder.AddStatement(
new ExpressionStatement(
new MethodInvocationExpression(SelfReference.Self,
typeof(AbstractInvocation).GetMethod("SetArgumentValue"),
new LiteralIntExpression(index),
new ConvertExpression(typeof(object), localReference.Type,
new ReferenceExpression(localReference)))
));
}
if (callbackMethod.ReturnType != typeof(void))
{
MethodInvocationExpression setRetVal =
new MethodInvocationExpression(SelfReference.Self,
typeof(AbstractInvocation).GetMethod("set_ReturnValue"),
new ConvertExpression(typeof(object), ret_local.Type, ret_local.ToExpression()));
method.CodeBuilder.AddStatement(new ExpressionStatement(setRetVal));
}
method.CodeBuilder.AddStatement(new ReturnStatement());
}
protected void CreateEmptyIInvocationInvokeOnTarget(NestedClassEmitter nested)
{
const MethodAttributes methodAtts = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual;
MethodEmitter method =
nested.CreateMethod("InvokeMethodOnTarget", methodAtts, typeof(void));
// TODO: throw exception
String message =
String.Format("This is a DynamicProxy2 error: the interceptor attempted " +
"to 'Proceed' for a method without a target, for example, an interface method or an abstract method");
method.CodeBuilder.AddStatement(new ThrowStatement(typeof(NotImplementedException), message));
method.CodeBuilder.AddStatement(new ReturnStatement());
}
///
/// Generates the constructor for the nested class that extends
///
///
///
///
///
///
protected void CreateIInvocationConstructor(
Type targetFieldType, NestedClassEmitter nested, FieldReference targetField, ConstructorVersion version)
{
ArgumentReference cArg0 = new ArgumentReference(targetFieldType);
ArgumentReference cArg1 = new ArgumentReference(typeof(IInterceptor[]));
ArgumentReference cArg2 = new ArgumentReference(typeof(Type));
ArgumentReference cArg3 = new ArgumentReference(typeof(MethodInfo));
ArgumentReference cArg4 = null;
ArgumentReference cArg6 = new ArgumentReference(typeof(object));
if (version == ConstructorVersion.WithTargetMethod)
{
cArg4 = new ArgumentReference(typeof(MethodInfo));
}
ArgumentReference cArg5 = new ArgumentReference(typeof(object[]));
ConstructorEmitter constructor;
if (cArg4 == null)
{
constructor = nested.CreateConstructor(cArg0, cArg1, cArg2, cArg3, cArg5, cArg6);
}
else
{
constructor = nested.CreateConstructor(cArg0, cArg1, cArg2, cArg3, cArg4, cArg5, cArg6);
}
constructor.CodeBuilder.AddStatement(new AssignStatement(targetField, cArg0.ToExpression()));
if (cArg4 == null)
{
constructor.CodeBuilder.InvokeBaseConstructor(Constants.AbstractInvocationConstructorWithoutTargetMethod,
cArg0,
cArg6,
cArg1,
cArg2,
cArg3,
cArg5);
}
else
{
constructor.CodeBuilder.InvokeBaseConstructor(Constants.AbstractInvocationConstructorWithTargetMethod,
cArg0,
cArg6,
cArg1,
cArg2,
cArg3,
cArg4,
cArg5);
}
constructor.CodeBuilder.AddStatement(new ReturnStatement());
}
#endregion
#region Custom Attribute handling
protected void ReplicateNonInheritableAttributes(Type targetType, ClassEmitter emitter)
{
object[] attrs = targetType.GetCustomAttributes(false);
foreach (Attribute attribute in attrs)
{
if (ShouldSkipAttributeReplication(attribute)) continue;
emitter.DefineCustomAttribute(attribute);
}
}
protected void ReplicateNonInheritableAttributes(MethodInfo method, MethodEmitter emitter)
{
object[] attrs = method.GetCustomAttributes(false);
foreach (Attribute attribute in attrs)
{
if (ShouldSkipAttributeReplication(attribute)) continue;
emitter.DefineCustomAttribute(attribute);
}
}
#endregion
#region Type tokens related operations
protected void GenerateConstructors(ClassEmitter emitter, Type baseType, params FieldReference[] fields)
{
ConstructorInfo[] constructors =
baseType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (ConstructorInfo constructor in constructors)
{
if (constructor.IsPublic || constructor.IsFamily || constructor.IsFamilyOrAssembly
|| (constructor.IsAssembly && InternalsHelper.IsInternalToDynamicProxy(constructor.DeclaringType.Assembly)))
GenerateConstructor(emitter, constructor, fields);
}
}
protected ConstructorEmitter GenerateStaticConstructor(ClassEmitter emitter)
{
return emitter.CreateTypeConstructor();
}
///
/// Improvement: this cache should be static. We should generate a
/// type constructor instead
///
protected void CreateInitializeCacheMethodBody(
Type targetType, MethodInfo[] methods, ClassEmitter classEmitter, ConstructorEmitter typeInitializerConstructor)
{
typeTokenField = classEmitter.CreateStaticField("typeTokenCache", typeof(Type));
typeInitializerConstructor.CodeBuilder.AddStatement(
new AssignStatement(typeTokenField, new TypeTokenExpression(targetType)));
CacheMethodTokens(classEmitter, methods, typeInitializerConstructor);
}
protected void CacheMethodTokens(
ClassEmitter classEmitter, MethodInfo[] methods, ConstructorEmitter typeInitializerConstructor)
{
foreach (MethodInfo method in methods)
{
// Aparently we cannot cache generic methods
if (method.IsGenericMethod) continue;
AddFieldToCacheMethodTokenAndStatementsToInitialize(method, typeInitializerConstructor, classEmitter);
}
}
protected void AddFieldToCacheMethodTokenAndStatementsToInitialize(
MethodInfo method, ConstructorEmitter typeInitializerConstructor, ClassEmitter classEmitter)
{
if (!method2TokenField.ContainsKey(method))
{
FieldReference fieldCache =
classEmitter.CreateStaticField("tokenCache" + fieldCount++, typeof(MethodInfo));
method2TokenField.Add(method, fieldCache);
typeInitializerConstructor.CodeBuilder.AddStatement(
new AssignStatement(fieldCache, new MethodTokenExpression(method)));
}
}
protected void CompleteInitCacheMethod(ConstructorCodeBuilder constCodeBuilder)
{
constCodeBuilder.AddStatement(new ReturnStatement());
}
protected void AddDefaultInterfaces(IList interfaceList)
{
if (!interfaceList.Contains(typeof(IProxyTargetAccessor)))
{
interfaceList.Add(typeof(IProxyTargetAccessor));
}
}
protected void ImplementProxyTargetAccessor(Type targetType, ClassEmitter emitter, FieldReference interceptorsField)
{
MethodAttributes attributes = MethodAttributes.Virtual | MethodAttributes.Public;
MethodEmitter DynProxyGetTarget =
emitter.CreateMethod("DynProxyGetTarget", attributes, typeof(object));
DynProxyGetTarget.CodeBuilder.AddStatement(
new ReturnStatement(new ConvertExpression(typeof(object), targetType, GetProxyTargetReference().ToExpression())));
MethodEmitter GetInterceptors =
emitter.CreateMethod("GetInterceptors", attributes, typeof(IInterceptor[]));
GetInterceptors.CodeBuilder.AddStatement(
new ReturnStatement(interceptorsField)
);
}
#endregion
#region Utility methods
protected void CollectMethodsToProxy(List methodList, Type type, bool onlyVirtuals)
{
CollectMethods(methodList, type, onlyVirtuals);
if (type.IsInterface)
{
Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
foreach (Type interType in typeChain)
{
CollectMethods(methodList, interType, onlyVirtuals);
}
}
}
protected void CollectPropertyMethodsToProxy(
List methodList, Type type, bool onlyVirtuals, ClassEmitter emitter, out PropertyToGenerate[] propsToGenerate)
{
if (type.IsInterface)
{
List toGenerateList = new List();
toGenerateList.AddRange(CollectProperties(methodList, type, onlyVirtuals, emitter));
Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
foreach (Type interType in typeChain)
{
toGenerateList.AddRange(CollectProperties(methodList, interType, onlyVirtuals, emitter));
}
propsToGenerate = toGenerateList.ToArray();
}
else
{
propsToGenerate = CollectProperties(methodList, type, onlyVirtuals, emitter);
}
}
///
/// Performs some basic screening and invokes the
/// to select methods.
///
///
///
///
protected bool AcceptMethod(MethodInfo method, bool onlyVirtuals)
{
// we can never intercept a sealed (final) method
if (method.IsFinal)
return false;
bool isInternalsAndNotVisibleToDynamicProxy = InternalsHelper.IsInternal(method);
if (isInternalsAndNotVisibleToDynamicProxy)
{
isInternalsAndNotVisibleToDynamicProxy = InternalsHelper.IsInternalToDynamicProxy(method.DeclaringType.Assembly) ==
false;
}
if (isInternalsAndNotVisibleToDynamicProxy)
return false;
if (onlyVirtuals && !method.IsVirtual)
{
#if SILVERLIGHT
if (method.DeclaringType != typeof(object))
#else
if (method.DeclaringType != typeof (object) && method.DeclaringType != typeof (MarshalByRefObject))
#endif
{
ProxyGenerationOptions.Hook.NonVirtualMemberNotification(targetType, method);
}
return false;
}
//can only proxy methods that are public or protected (or internals that have already been checked above)
if ((method.IsPublic || method.IsFamily || method.IsAssembly || method.IsFamilyOrAssembly) == false)
return false;
if (method.DeclaringType == typeof(object))
{
return false;
}
#if !SILVERLIGHT
if (method.DeclaringType == typeof (MarshalByRefObject))
{
return false;
}
#endif
return ProxyGenerationOptions.Hook.ShouldInterceptMethod(targetType, method);
;
}
protected MethodInfo[] CollectMethodsAndProperties(
ClassEmitter emitter,
Type targetType,
out PropertyToGenerate[] propsToGenerate,
out EventToGenerate[] eventsToGenerate)
{
bool onlyVirtuals = CanOnlyProxyVirtual();
return CollectMethodsAndProperties(emitter, targetType, onlyVirtuals, out propsToGenerate, out eventsToGenerate);
}
protected MethodInfo[] CollectMethodsAndProperties(
ClassEmitter emitter,
Type targetType,
bool onlyVirtuals,
out PropertyToGenerate[] propsToGenerate,
out EventToGenerate[] eventsToGenerate)
{
List methodsList = new List();
CollectMethodsToProxy(methodsList, targetType, onlyVirtuals);
CollectPropertyMethodsToProxy(methodsList, targetType, onlyVirtuals, emitter, out propsToGenerate);
CollectEventMethodsToProxy(methodsList, targetType, onlyVirtuals, emitter, out eventsToGenerate);
return methodsList.ToArray();
}
private void CollectEventMethodsToProxy(
List methodList, Type type, bool onlyVirtuals, ClassEmitter emitter, out EventToGenerate[] eventsToGenerates)
{
if (type.IsInterface)
{
List toGenerateList = new List();
toGenerateList.AddRange(CollectEvents(methodList, type, onlyVirtuals, emitter));
Type[] typeChain = type.FindInterfaces(new TypeFilter(NoFilter), null);
foreach (Type interType in typeChain)
{
toGenerateList.AddRange(CollectEvents(methodList, interType, onlyVirtuals, emitter));
}
eventsToGenerates = toGenerateList.ToArray();
}
else
{
eventsToGenerates = CollectEvents(methodList, type, onlyVirtuals, emitter);
}
}
///
/// Checks if the method is public or protected.
///
///
///
private bool IsAccessible(MethodInfo method)
{
if (method.IsPublic
|| method.IsFamily
|| method.IsFamilyAndAssembly
|| method.IsFamilyOrAssembly)
{
return true;
}
if (InternalsHelper.IsInternalToDynamicProxy(method.DeclaringType.Assembly)
&& method.IsAssembly)
{
return true;
}
return false;
}
private bool HasGenericParameters(Type type)
{
if (type.IsGenericType)
{
Type[] genTypes = type.GetGenericArguments();
foreach (Type genType in genTypes)
{
if (genType.IsGenericParameter)
{
return true;
}
}
}
return false;
}
private bool NoFilter(Type type, object filterCriteria)
{
return true;
}
private void CollectMethods(List methodsList, Type type, bool onlyVirtuals)
{
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
MethodInfo[] methods = MethodFinder.GetAllInstanceMethods(type, flags);
foreach (MethodInfo method in methods)
{
if (method.IsFinal)
{
AddMethodToGenerateNewSlot(method);
continue;
}
if (method.IsSpecialName
// This is here so we can proxy COM Types built in VB6, where properties
// are let_Foo and set_Foo.
&& method.Name.StartsWith("let_") == false)
{
continue;
}
if (AcceptMethod(method, onlyVirtuals))
{
methodsList.Add(method);
}
}
}
private EventToGenerate[] CollectEvents(List methodList, Type type, bool onlyVirtuals, ClassEmitter emitter)
{
List toGenerateList = new List();
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
EventInfo[] events = type.GetEvents(flags);
foreach (EventInfo eventInfo in events)
{
MethodInfo addMethod = eventInfo.GetAddMethod(true);
MethodInfo removeMethod = eventInfo.GetRemoveMethod(true);
bool shouldGenerate = false;
if (addMethod != null && IsAccessible(addMethod) && AcceptMethod(addMethod, onlyVirtuals))
{
shouldGenerate = true;
methodList.Add(addMethod);
}
if (removeMethod != null && IsAccessible(removeMethod) && AcceptMethod(removeMethod, onlyVirtuals))
{
shouldGenerate = true;
methodList.Add(removeMethod);
}
if (shouldGenerate == false)
continue;
EventAttributes atts = ObtainEventAttributes(eventInfo);
EventEmitter eventEmitter = emitter.CreateEvent(eventInfo.Name, atts, eventInfo.EventHandlerType);
EventToGenerate eventToGenerate = new EventToGenerate(eventEmitter, addMethod, removeMethod, atts);
toGenerateList.Add(eventToGenerate);
}
return toGenerateList.ToArray();
}
private EventAttributes ObtainEventAttributes(EventInfo eventInfo)
{
return EventAttributes.None;
}
private PropertyToGenerate[] CollectProperties(
List methodList, Type type, bool onlyVirtuals, ClassEmitter emitter)
{
List toGenerateList = new List();
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
PropertyInfo[] properties = type.GetProperties(flags);
foreach (PropertyInfo propInfo in properties)
{
bool generateReadable, generateWritable;
generateWritable = generateReadable = false;
MethodInfo setMethod, getMethod;
setMethod = getMethod = null;
if (propInfo.CanRead)
{
getMethod = propInfo.GetGetMethod(true);
if (IsAccessible(getMethod) && AcceptMethod(getMethod, onlyVirtuals))
{
methodList.Add(getMethod);
generateReadable = true;
}
}
if (propInfo.CanWrite)
{
setMethod = propInfo.GetSetMethod(true);
if (IsAccessible(setMethod) && AcceptMethod(setMethod, onlyVirtuals))
{
methodList.Add(setMethod);
generateWritable = true;
}
}
if (!generateWritable && !generateReadable)
{
continue;
}
PropertyAttributes atts = ObtainPropertyAttributes(propInfo);
PropertyEmitter propEmitter = emitter.CreateProperty(propInfo.Name, atts, propInfo.PropertyType);
PropertyToGenerate propToGenerate =
new PropertyToGenerate(generateReadable, generateWritable, propEmitter, getMethod, setMethod);
toGenerateList.Add(propToGenerate);
}
return toGenerateList.ToArray();
}
///
/// Attributes should be replicated if they are non-inheritable,
/// but there are some special cases where the attributes means
/// something to the CLR, where they should be skipped.
///
private bool ShouldSkipAttributeReplication(Attribute attribute)
{
if (SpecialCaseAttributThatShouldNotBeReplicated(attribute))
return true;
object[] attrs = attribute.GetType()
.GetCustomAttributes(typeof(AttributeUsageAttribute), true);
if (attrs.Length != 0)
{
AttributeUsageAttribute usage = (AttributeUsageAttribute)attrs[0];
return usage.Inherited;
}
return true;
}
#endregion
protected void AddMethodToGenerateNewSlot(MethodInfo method)
{
generateNewSlot.Add(method);
}
///
/// Checks if the method has the same signature as a method that was marked as
/// one that should generate a new vtable slot.
///
protected bool ShouldCreateNewSlot(MethodInfo method)
{
string methodStr = method.ToString();
foreach (MethodInfo candidate in generateNewSlot)
{
if (candidate.ToString() == methodStr)
return true;
}
return false;
}
#if !SILVERLIGHT
protected virtual void ImplementGetObjectData(ClassEmitter emitter, FieldReference interceptorsField,
FieldReference[] mixinFields,
Type[] interfaces)
{
if (interfaces == null)
{
interfaces = new Type[0];
}
Type[] get_type_args = new Type[] {typeof (String), typeof (bool), typeof (bool)};
Type[] key_and_object = new Type[] {typeof (String), typeof (Object)};
MethodInfo addValueMethod = typeof (SerializationInfo).GetMethod("AddValue", key_and_object);
ArgumentReference arg1 = new ArgumentReference(typeof (SerializationInfo));
ArgumentReference arg2 = new ArgumentReference(typeof (StreamingContext));
MethodEmitter getObjectData = emitter.CreateMethod("GetObjectData",
typeof (void), arg1, arg2);
LocalReference typeLocal = getObjectData.CodeBuilder.DeclareLocal(typeof (Type));
getObjectData.CodeBuilder.AddStatement(new AssignStatement(
typeLocal,
new MethodInvocationExpression(null,
typeof (Type).GetMethod("GetType",
get_type_args),
new ConstReference(
typeof (ProxyObjectReference).
AssemblyQualifiedName).ToExpression(),
new ConstReference(1).ToExpression(),
new ConstReference(0).ToExpression())));
getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(
arg1, typeof (SerializationInfo).GetMethod("SetType"),
typeLocal.ToExpression())));
getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference("__interceptors").
ToExpression(),
interceptorsField.ToExpression())));
foreach (FieldReference mixinFieldReference in mixinFields)
{
getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference(
mixinFieldReference.Reference.Name).
ToExpression(),
mixinFieldReference.ToExpression())));
}
LocalReference interfacesLocal =
getObjectData.CodeBuilder.DeclareLocal(typeof (String[]));
getObjectData.CodeBuilder.AddStatement(
new AssignStatement(interfacesLocal,
new NewArrayExpression(interfaces.Length, typeof (String))));
for (int i = 0; i < interfaces.Length; i++)
{
getObjectData.CodeBuilder.AddStatement(new AssignArrayStatement(
interfacesLocal, i,
new ConstReference(interfaces[i].AssemblyQualifiedName).ToExpression()));
}
getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference("__interfaces").
ToExpression(),
interfacesLocal.ToExpression())));
getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference("__baseType").
ToExpression(),
new ConstReference(
emitter.BaseType.AssemblyQualifiedName).
ToExpression())));
getObjectData.CodeBuilder.AddStatement(new ExpressionStatement(
new MethodInvocationExpression(arg1, addValueMethod,
new ConstReference("__proxyGenerationOptions")
.
ToExpression(),
proxyGenerationOptionsField.ToExpression())));
CustomizeGetObjectData(getObjectData.CodeBuilder, arg1, arg2);
getObjectData.CodeBuilder.AddStatement(new ReturnStatement());
}
#endif
protected virtual void CustomizeGetObjectData(
AbstractCodeBuilder codebuilder, ArgumentReference arg1,
ArgumentReference arg2)
{
}
protected bool VerifyIfBaseImplementsGetObjectData(Type baseType)
{
// If base type implements ISerializable, we have to make sure
// the GetObjectData is marked as virtual
#if !SILVERLIGHT
if (typeof (ISerializable).IsAssignableFrom(baseType))
{
MethodInfo getObjectDataMethod = baseType.GetMethod("GetObjectData",
new Type[]
{typeof (SerializationInfo), typeof (StreamingContext)});
if (getObjectDataMethod == null) //explicit interface implementation
{
return false;
}
if (!getObjectDataMethod.IsVirtual || getObjectDataMethod.IsFinal)
{
String message = String.Format("The type {0} implements ISerializable, but GetObjectData is not marked as virtual",
baseType.FullName);
throw new ArgumentException(message);
}
methodsToSkip.Add(getObjectDataMethod);
serializationConstructor = baseType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null,
new Type[] {typeof (SerializationInfo), typeof (StreamingContext)},
null);
if (serializationConstructor == null)
{
String message =
String.Format("The type {0} implements ISerializable, but failed to provide a deserialization constructor",
baseType.FullName);
throw new ArgumentException(message);
}
return true;
}
#endif
return false;
}
private static bool SpecialCaseAttributThatShouldNotBeReplicated(Attribute attribute)
{
return AttributesToAvoidReplicating.Contains(attribute.GetType());
}
protected void RegisterMixinMethodsAndProperties(ClassEmitter emitter, ref MethodInfo[] methods,
ref PropertyToGenerate[] propsToGenerate,
ref EventToGenerate[] eventsToGenerate)
{
List withMixinMethods = new List(methods);
List withMixinProperties = null;
List withMixinEvents = null;
foreach (Type mixinInterface in ProxyGenerationOptions.MixinData.MixinInterfacesAndPositions.Keys)
{
PropertyToGenerate[] mixinPropsToGenerate;
EventToGenerate[] mixinEventsToGenerate;
MethodInfo[] mixinMethods = CollectMethodsAndProperties(emitter, mixinInterface, false,
out mixinPropsToGenerate, out mixinEventsToGenerate);
foreach (MethodInfo mixinMethod in mixinMethods)
{
if (!method2MixinType.ContainsKey(mixinMethod))
{
method2MixinType[mixinMethod] = mixinInterface;
withMixinMethods.Add(mixinMethod);
}
}
if (mixinPropsToGenerate.Length > 0)
{
if (withMixinProperties == null)
{
withMixinProperties = new List(propsToGenerate);
}
withMixinProperties.AddRange(mixinPropsToGenerate);
}
if (mixinEventsToGenerate.Length > 0)
{
if (withMixinEvents == null)
{
withMixinEvents = new List(eventsToGenerate);
}
withMixinEvents.AddRange(mixinEventsToGenerate);
}
}
if (withMixinMethods != null)
{
methods = withMixinMethods.ToArray();
}
if (withMixinProperties != null)
{
propsToGenerate = withMixinProperties.ToArray();
}
if (withMixinEvents != null)
{
eventsToGenerate = withMixinEvents.ToArray();
}
}
protected FieldReference[] AddMixinFields(ClassEmitter emitter)
{
List mixins = new List();
foreach (Type type in ProxyGenerationOptions.MixinData.MixinInterfacesAndPositions.Keys)
{
FieldReference fieldReference = emitter.CreateField("__mixin_" + type.FullName.Replace(".", "_"), type);
interface2MixinFieldReference[type] = fieldReference;
mixins.Add(fieldReference);
}
return mixins.ToArray();
}
protected void AddMixinInterfaces(List interfaceList)
{
interfaceList.AddRange(ProxyGenerationOptions.MixinData.MixinInterfacesAndPositions.Keys);
}
protected Reference GetTargetRef(MethodInfo method, FieldReference[] mixinFields, Reference targetRef)
{
if (IsMixinMethod(method))
{
Type interfaceType = method2MixinType[method];
int mixinIndex = ProxyGenerationOptions.MixinData.MixinInterfacesAndPositions[interfaceType];
targetRef = mixinFields[mixinIndex];
}
return targetRef;
}
protected bool IsMixinMethod(MethodInfo methodInfo)
{
return method2MixinType.ContainsKey(methodInfo);
}
}
}