// 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; } } }