// Copyright 2004 The Apache Software Foundation
//
// 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 Apache.Avalon.DynamicProxy
{
using System;
using System.Reflection;
using System.Reflection.Emit;
///
/// Generates a Java style proxy. This overrides the .Net proxy requirements
/// that forces one to extend MarshalByRefObject or (for a different purpose)
/// ContextBoundObject to have a Proxiable class.
///
///
/// The should be used to generate a class
/// implementing the specified interfaces. The class implementation will
/// only call the internal instance.
///
///
///
/// MyInvocationHandler handler = ...
/// IInterfaceExposed proxy =
/// ProxyGenerator.CreateProxy( new Type[] { typeof(IInterfaceExposed) }, handler );
///
///
public class ProxyGenerator
{
///
/// Private construtor
///
private ProxyGenerator()
{
}
///
/// Generates a proxy implementing all the specified interfaces and
/// redirecting method invocations to the specifed handler.
///
/// Array of interfaces to be implemented
/// instance of
/// Proxy instance
public static object CreateProxy(Type[] interfaces, IInvocationHandler handler)
{
if (interfaces == null)
{
throw new ArgumentNullException("interfaces");
}
if (handler == null)
{
throw new ArgumentNullException("handler");
}
if (interfaces.Length == 0)
{
throw new ArgumentException("Can't handle an empty interface array");
}
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "DynamicAssemblyProxyGen";
AssemblyBuilder assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule( assemblyName.Name, true );
TypeBuilder typeBuilder = moduleBuilder.DefineType(
"ProxyType", TypeAttributes.Public|TypeAttributes.Class, null, interfaces);
FieldBuilder handlerField = GenerateField( typeBuilder );
ConstructorBuilder constr = GenerateConstructor( typeBuilder, handlerField );
GenerateInterfaceImplementation( typeBuilder, interfaces, handlerField );
Type generatedType = typeBuilder.CreateType();
return Activator.CreateInstance( generatedType, new object[] { handler } );
}
///
///
///
///
///
private static void GenerateInterfaceImplementation( TypeBuilder typeBuilder,
Type[] interfaces, FieldBuilder handlerField )
{
foreach(Type inter in interfaces)
{
GenerateInterfaceImplementation( typeBuilder, inter, handlerField );
}
}
///
/// Iterates over the interfaces and generate implementation
/// for each method in it.
///
/// being constructed.
/// Interface type
private static void GenerateInterfaceImplementation( TypeBuilder typeBuilder,
Type inter, FieldBuilder handlerField )
{
if (!inter.IsInterface)
{
throw new ArgumentException("Type array expects interfaces only.");
}
Type[] baseInterfaces = inter.FindInterfaces( new TypeFilter( NoFilterImpl ), inter );
GenerateInterfaceImplementation( typeBuilder, baseInterfaces, handlerField );
PropertyInfo[] properties = inter.GetProperties();
PropertyBuilder[] propertiesBuilder = new PropertyBuilder[properties.Length];
for(int i=0; i < properties.Length; i++)
{
GeneratePropertyImplementation( typeBuilder, properties[i], ref propertiesBuilder[i] );
}
MethodInfo[] methods = inter.GetMethods();
foreach(MethodInfo method in methods)
{
GenerateMethodImplementation( typeBuilder, method,
propertiesBuilder, handlerField, inter );
}
}
///
/// Generates a public field holding the
///
/// being constructed.
/// instance
private static FieldBuilder GenerateField( TypeBuilder typeBuilder )
{
return typeBuilder.DefineField( "handler",
typeof(IInvocationHandler), FieldAttributes.Public );
}
///
/// Generates one public constructor receiving
/// the instance.
///
/// being constructed.
/// instance representing the handler field
/// instance
private static ConstructorBuilder GenerateConstructor(
TypeBuilder typeBuilder, FieldBuilder handlerField )
{
ConstructorBuilder consBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { typeof(IInvocationHandler) } );
ILGenerator ilGenerator = consBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, typeof(Object).GetConstructor(new Type[0]));
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Stfld, handlerField);
ilGenerator.Emit(OpCodes.Ret);
return consBuilder;
}
///
/// Generate property implementation
///
/// being constructed.
///
///
private static void GeneratePropertyImplementation(
TypeBuilder typeBuilder, PropertyInfo property, ref PropertyBuilder propertyBuilder )
{
propertyBuilder = typeBuilder.DefineProperty(
property.Name, property.Attributes, property.PropertyType, null);
}
///
/// Generates implementation for each method.
///
/// being constructed.
///
///
private static void GenerateMethodImplementation(
TypeBuilder typeBuilder, MethodInfo method,
PropertyBuilder[] properties, FieldBuilder handlerField, Type inter )
{
ParameterInfo[] parameterInfo = method.GetParameters();
System.Type[] parameters = new System.Type[parameterInfo.Length];
for (int i=0; i
/// Writes the stack for the method implementation. This
/// method generates the IL stack for property get/set method and
/// ordinary methods.
///
///
/// The method implementation would be as simple as:
///
/// public void SomeMethod( int parameter )
/// {
/// MethodBase method = MethodBase.GetCurrentMethod();
/// handler.Invoke( this, method, new object[] { parameter } );
/// }
///
///
/// being constructed.
///
///
private static void WriteILForMethod( MethodBuilder builder,
System.Type[] parameters, FieldBuilder handlerField )
{
int arrayPositionInStack = 1;
ILGenerator ilGenerator = builder.GetILGenerator();
ilGenerator.DeclareLocal( typeof( MethodBase ) );
if (builder.ReturnType != typeof(void))
{
ilGenerator.DeclareLocal(builder.ReturnType);
arrayPositionInStack = 2;
}
ilGenerator.DeclareLocal( typeof(object[]) );
ilGenerator.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod") );
ilGenerator.Emit(OpCodes.Stloc_0);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, handlerField );
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4, parameters.Length);
ilGenerator.Emit(OpCodes.Newarr, typeof(object) );
if (parameters.Length != 0)
{
ilGenerator.Emit(OpCodes.Stloc, arrayPositionInStack);
ilGenerator.Emit(OpCodes.Ldloc, arrayPositionInStack);
}
for (int c=0; c
/// Converts a Value type to a correspondent OpCode of
///
///
///
private static OpCode ConvertTypeToOpCode( Type type )
{
if (type.IsEnum)
{
System.Enum baseType = (System.Enum) Activator.CreateInstance( type );
TypeCode code = baseType.GetTypeCode();
switch(code)
{
case TypeCode.Byte:
type = typeof(Byte);
break;
case TypeCode.Int16:
type = typeof(Int16);
break;
case TypeCode.Int32:
type = typeof(Int32);
break;
case TypeCode.Int64:
type = typeof(Int64);
break;
}
return ConvertTypeToOpCode( type );
}
if ( type.Equals( typeof(Int32) ) )
{
return OpCodes.Ldind_I4;
}
else if ( type.Equals( typeof(Int16) ) )
{
return OpCodes.Ldind_I2;
}
else if ( type.Equals( typeof(Int64) ) )
{
return OpCodes.Ldind_I8;
}
else if ( type.Equals( typeof(Single) ) )
{
return OpCodes.Ldind_R4;
}
else if ( type.Equals( typeof(Double) ) )
{
return OpCodes.Ldind_R8;
}
else if ( type.Equals( typeof(UInt16) ) )
{
return OpCodes.Ldind_U2;
}
else if ( type.Equals( typeof(UInt32) ) )
{
return OpCodes.Ldind_U4;
}
else if ( type.Equals( typeof(Boolean) ) )
{
return OpCodes.Ldind_I4;
}
else
{
throw new ArgumentException("Type " + type + " could not be converted to a OpCode");
}
}
public static bool NoFilterImpl( Type type, object criteria )
{
return true;
}
}
}