Author: hammett Date: Mon Sep 6 13:43:16 2004 New Revision: 43424 Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/BaseCodeGenerator.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/ClassProxyGenerator.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/InterfaceProxyGenerator.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/OpCodesDictionary.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/IProxyBuilder.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/ProxyBuilderImpl.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IMyInterface.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IMySecondInterface.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IServiceStatus.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/MyInterfaceImpl.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/MySecondInterfaceImpl.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/ServiceStatusImpl.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/NoVirtualMethodClass.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/SealedMethodsClass.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/ServiceClass.cs (contents, props changed) avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/SpecializedServiceClass.cs (contents, props changed) Removed: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/IMyInterface.cs avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/IMySecondInterface.cs avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/IServiceStatus.cs avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/MyInterfaceImpl.cs avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/MySecondInterfaceImpl.cs avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ServiceStatusImpl.cs Modified: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Apache.Avalon.DynamicProxy.csproj avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Apache.Avalon.DynamicProxy.Test.csproj avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ProxyGeneratorTestCase.cs avalon/trunk/central/laboratory/avalon-net/DynamicProxy/ProxyGenerator.cs Log: Supports proxying classes and interfaces now.
Modified: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Apache.Avalon.DynamicProxy.csproj ============================================================================== --- avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Apache.Avalon.DynamicProxy.csproj (original) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Apache.Avalon.DynamicProxy.csproj Mon Sep 6 13:43:16 2004 @@ -103,6 +103,36 @@ SubType = "Code" BuildAction = "Compile" /> + <File + RelPath = "Builder\IProxyBuilder.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Builder\ProxyBuilderImpl.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Builder\CodeGenerators\BaseCodeGenerator.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Builder\CodeGenerators\ClassProxyGenerator.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Builder\CodeGenerators\InterfaceProxyGenerator.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Builder\CodeGenerators\OpCodesDictionary.cs" + SubType = "Code" + BuildAction = "Compile" + /> </Include> </Files> </CSHARP> Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/BaseCodeGenerator.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/BaseCodeGenerator.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,372 @@ + // 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.Builder.CodeGenerators +{ + using System; + using System.Collections; + using System.Reflection; + using System.Reflection.Emit; + + /// <summary> + /// Summary description for BaseCodeGenerator. + /// </summary> + public abstract class BaseCodeGenerator + { + private TypeBuilder m_typeBuilder; + private FieldBuilder m_handlerField; + private IList m_generated = new ArrayList(); + + protected TypeBuilder MainTypeBuilder + { + get { return m_typeBuilder; } + } + + protected FieldBuilder HandlerFieldBuilder + { + get { return m_handlerField; } + } + + protected virtual ModuleBuilder CreateDynamicModule() + { + AssemblyName assemblyName = new AssemblyName(); + assemblyName.Name = "DynamicAssemblyProxyGen"; + + AssemblyBuilder assemblyBuilder = + AppDomain.CurrentDomain.DefineDynamicAssembly( + assemblyName, + AssemblyBuilderAccess.Run); + + return assemblyBuilder.DefineDynamicModule(assemblyName.Name, true); + } + + protected virtual TypeBuilder CreateTypeBuilder(Type baseType, Type[] interfaces) + { + ModuleBuilder moduleBuilder = CreateDynamicModule(); + + m_typeBuilder = moduleBuilder.DefineType( + "ProxyType", TypeAttributes.Public | TypeAttributes.Class, baseType, interfaces); + + m_handlerField = GenerateField(); + ConstructorBuilder constr = GenerateConstructor(); + + return m_typeBuilder; + } + + /// <summary> + /// Generates a public field holding the <see cref="IInvocationHandler"/> + /// </summary> + /// <returns><see cref="FieldBuilder"/> instance</returns> + protected FieldBuilder GenerateField() + { + return m_typeBuilder.DefineField("handler", + typeof (IInvocationHandler), FieldAttributes.Public); + } + + /// <summary> + /// Generates one public constructor receiving + /// the <see cref="IInvocationHandler"/> instance. + /// </summary> + /// <returns><see cref="ConstructorBuilder"/> instance</returns> + protected ConstructorBuilder GenerateConstructor() + { + ConstructorBuilder consBuilder = m_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, m_handlerField); + ilGenerator.Emit(OpCodes.Ret); + + return consBuilder; + } + + /// <summary> + /// + /// </summary> + /// <param name="interfaces"></param> + protected void GenerateInterfaceImplementation(Type[] interfaces) + { + foreach(Type inter in interfaces) + { + GenerateTypeImplementation(inter); + } + } + + /// <summary> + /// Iterates over the interfaces and generate implementation + /// for each method in it. + /// </summary> + /// <param name="type">Interface type</param> + protected void GenerateTypeImplementation(Type type) + { + if (m_generated.Contains(type)) + { + return; + } + else + { + m_generated.Add( type ); + } + + Type[] baseInterfaces = type.FindInterfaces(new TypeFilter(NoFilterImpl), type); + + GenerateInterfaceImplementation(baseInterfaces); + PropertyBuilder[] propertiesBuilder = GenerateProperties(type); + GenerateMethods(type, propertiesBuilder); + } + + protected virtual PropertyBuilder[] GenerateProperties( Type inter ) + { + PropertyInfo[] properties = inter.GetProperties(); + PropertyBuilder[] propertiesBuilder = new PropertyBuilder[properties.Length]; + + for(int i = 0; i < properties.Length; i++) + { + propertiesBuilder[i] = GeneratePropertyImplementation(properties[i]); + } + + return propertiesBuilder; + } + + protected virtual void GenerateMethods( Type inter, PropertyBuilder[] propertiesBuilder ) + { + MethodInfo[] methods = inter.GetMethods(); + + foreach(MethodInfo method in methods) + { + GenerateMethodImplementation(method, propertiesBuilder); + } + } + + /// <summary> + /// Generate property implementation + /// </summary> + /// <param name="property"></param> + protected PropertyBuilder GeneratePropertyImplementation(PropertyInfo property) + { + return m_typeBuilder.DefineProperty( + property.Name, property.Attributes, property.PropertyType, null); + } + + /// <summary> + /// Generates implementation for each method. + /// </summary> + /// <param name="method"></param> + /// <param name="properties"></param> + protected void GenerateMethodImplementation( + MethodInfo method, PropertyBuilder[] properties) + { + if (method.IsFinal) + { + return; + } + + ParameterInfo[] parameterInfo = method.GetParameters(); + + Type[] parameters = new Type[parameterInfo.Length]; + + for(int i = 0; i < parameterInfo.Length; i++) + { + parameters[i] = parameterInfo[i].ParameterType; + } + + MethodAttributes atts = MethodAttributes.Public | MethodAttributes.Virtual; + + if (method.Name.StartsWith("set_") || method.Name.StartsWith("get_")) + { + atts = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual; + } + + MethodBuilder methodBuilder = + m_typeBuilder.DefineMethod(method.Name, atts, CallingConventions.Standard, + method.ReturnType, parameters); + + if (method.Name.StartsWith("set_") || method.Name.StartsWith("get_")) + { + foreach(PropertyBuilder property in properties) + { + if (property == null) + { + break; + } + + if (!property.Name.Equals(method.Name.Substring(4))) + { + continue; + } + + if (methodBuilder.Name.StartsWith("set_")) + { + property.SetSetMethod(methodBuilder); + break; + } + else + { + property.SetGetMethod(methodBuilder); + break; + } + } + } + + WriteILForMethod(method, methodBuilder, parameters, HandlerFieldBuilder); + } + + /// <summary> + /// Writes the stack for the method implementation. This + /// method generates the IL stack for property get/set method and + /// ordinary methods. + /// </summary> + /// <remarks> + /// The method implementation would be as simple as: + /// <code> + /// public void SomeMethod( int parameter ) + /// { + /// MethodBase method = MethodBase.GetCurrentMethod(); + /// handler.Invoke( this, method, new object[] { parameter } ); + /// } + /// </code> + /// </remarks> + /// <param name="builder"><see cref="MethodBuilder"/> being constructed.</param> + /// <param name="parameters"></param> + /// <param name="handlerField"></param> + protected void WriteILForMethod(MethodInfo method, MethodBuilder builder, + 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.Ldtoken, method); + ilGenerator.Emit(OpCodes.Call, typeof (MethodBase).GetMethod("GetMethodFromHandle")); + + 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 < parameters.Length; c++) + { + ilGenerator.Emit(OpCodes.Ldc_I4, c); + ilGenerator.Emit(OpCodes.Ldarg, c + 1); + + if (parameters[c].IsValueType) + { + ilGenerator.Emit(OpCodes.Box, parameters[c].UnderlyingSystemType); + } + + ilGenerator.Emit(OpCodes.Stelem_Ref); + ilGenerator.Emit(OpCodes.Ldloc, arrayPositionInStack); + } + + ilGenerator.Emit(OpCodes.Callvirt, typeof (IInvocationHandler).GetMethod("Invoke")); + + if (builder.ReturnType != typeof (void)) + { + if (!builder.ReturnType.IsValueType) + { + ilGenerator.Emit(OpCodes.Castclass, builder.ReturnType); + } + else + { + ilGenerator.Emit(OpCodes.Unbox, builder.ReturnType); + ilGenerator.Emit(ConvertTypeToOpCode(builder.ReturnType)); + } + + ilGenerator.Emit(OpCodes.Stloc, 1); + + Label label = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br_S, label); + ilGenerator.MarkLabel(label); + ilGenerator.Emit(OpCodes.Ldloc, 1); + } + else + { + ilGenerator.Emit(OpCodes.Pop); + } + + ilGenerator.Emit(OpCodes.Ret); + } + + /// <summary> + /// Converts a Value type to a correspondent OpCode of + /// </summary> + /// <param name="type"></param> + /// <returns></returns> + protected virtual OpCode ConvertTypeToOpCode(Type type) + { + if (type.IsEnum) + { + Enum baseType = (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); + } + + OpCode opCode = OpCodesDictionary.Instance[ type ]; + + if (Object.ReferenceEquals(opCode, OpCodesDictionary.EmptyOpCode)) + { + throw new ArgumentException("Type " + type + " could not be converted to a OpCode"); + } + + return opCode; + } + + public static bool NoFilterImpl(Type type, object criteria) + { + return true; + } + } +} \ No newline at end of file Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/ClassProxyGenerator.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/ClassProxyGenerator.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,34 @@ +// 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.Builder.CodeGenerators +{ + using System; + + /// <summary> + /// Summary description for ClassProxyGenerator. + /// </summary> + public class ClassProxyGenerator : BaseCodeGenerator + { + public Type GenerateCode(Type baseClass) + { + // TODO: interfaces of base class + + CreateTypeBuilder( baseClass, new Type[0] ); + GenerateTypeImplementation( baseClass ); + + return MainTypeBuilder.CreateType(); + } + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/InterfaceProxyGenerator.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/InterfaceProxyGenerator.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,37 @@ + // 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.Builder.CodeGenerators +{ + using System; + using System.Reflection; + using System.Reflection.Emit; + + /// <summary> + /// Summary description for InterfaceProxyGenerator. + /// </summary> + public class InterfaceProxyGenerator : BaseCodeGenerator + { + public InterfaceProxyGenerator() + { + } + + public Type GenerateCode(Type[] interfaces) + { + CreateTypeBuilder( null, interfaces ); + GenerateInterfaceImplementation( interfaces ); + return MainTypeBuilder.CreateType(); + } + } +} \ No newline at end of file Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/OpCodesDictionary.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/CodeGenerators/OpCodesDictionary.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,64 @@ +using System.Reflection.Emit; +// 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.Builder.CodeGenerators +{ + using System; + using System.Collections; + + /// <summary> + /// Summary description for OpCodesDictionary. + /// </summary> + public sealed class OpCodesDictionary : DictionaryBase + { + private static readonly OpCodesDictionary m_dict = new OpCodesDictionary(); + + private static readonly OpCode m_emptyOpCode = new OpCode(); + + private OpCodesDictionary() : base() + { + Dictionary[ typeof (Int16) ] = OpCodes.Ldind_I2; + Dictionary[ typeof (Int32) ] = OpCodes.Ldind_I4; + Dictionary[ typeof (Int64) ] = OpCodes.Ldind_I8; + Dictionary[ typeof (float) ] = OpCodes.Ldind_R4; + Dictionary[ typeof (double) ] = OpCodes.Ldind_R8; + Dictionary[ typeof (UInt16) ] = OpCodes.Ldind_U2; + Dictionary[ typeof (UInt32) ] = OpCodes.Ldind_U4; + Dictionary[ typeof (bool) ] = OpCodes.Ldind_I4; + } + + public OpCode this[Type type] + { + get + { + if (Dictionary.Contains(type)) + { + return (OpCode) Dictionary[ type ]; + } + return EmptyOpCode; + } + } + + public static OpCodesDictionary Instance + { + get { return m_dict; } + } + + public static OpCode EmptyOpCode + { + get { return m_emptyOpCode; } + } + } +} \ No newline at end of file Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/IProxyBuilder.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/IProxyBuilder.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,28 @@ +// 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.Builder +{ + using System; + + /// <summary> + /// Summary description for IProxyBuilder. + /// </summary> + public interface IProxyBuilder + { + Type CreateInterfaceProxy( Type[] interfaces ); + + Type CreateClassProxy( Type theClass ); + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/ProxyBuilderImpl.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/Builder/ProxyBuilderImpl.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,41 @@ +using Apache.Avalon.DynamicProxy.Builder.CodeGenerators; +// 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.Builder +{ + using System; + + /// <summary> + /// Summary description for ProxyBuilderImpl. + /// </summary> + public class ProxyBuilderImpl : IProxyBuilder + { + #region IProxyBuilder Members + + public Type CreateInterfaceProxy(Type[] interfaces) + { + InterfaceProxyGenerator generator = new InterfaceProxyGenerator(); + return generator.GenerateCode( interfaces ); + } + + public Type CreateClassProxy(Type theClass) + { + ClassProxyGenerator generator = new ClassProxyGenerator(); + return generator.GenerateCode( theClass ); + } + + #endregion + } +} Modified: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Apache.Avalon.DynamicProxy.Test.csproj ============================================================================== --- avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Apache.Avalon.DynamicProxy.Test.csproj (original) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Apache.Avalon.DynamicProxy.Test.csproj Mon Sep 6 13:43:16 2004 @@ -99,37 +99,57 @@ BuildAction = "Compile" /> <File - RelPath = "IMyInterface.cs" + RelPath = "ProxyGeneratorTestCase.cs" SubType = "Code" BuildAction = "Compile" /> <File - RelPath = "IMySecondInterface.cs" + RelPath = "Classes\NoVirtualMethodClass.cs" SubType = "Code" BuildAction = "Compile" /> <File - RelPath = "IServiceStatus.cs" + RelPath = "Classes\SealedMethodsClass.cs" SubType = "Code" BuildAction = "Compile" /> <File - RelPath = "MyInterfaceImpl.cs" + RelPath = "Classes\ServiceClass.cs" SubType = "Code" BuildAction = "Compile" /> <File - RelPath = "MySecondInterfaceImpl.cs" + RelPath = "Classes\SpecializedServiceClass.cs" SubType = "Code" BuildAction = "Compile" /> <File - RelPath = "ProxyGeneratorTestCase.cs" + RelPath = "ClassInterfaces\IMyInterface.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ClassInterfaces\IMySecondInterface.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ClassInterfaces\IServiceStatus.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ClassInterfaces\MyInterfaceImpl.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ClassInterfaces\MySecondInterfaceImpl.cs" SubType = "Code" BuildAction = "Compile" /> <File - RelPath = "ServiceStatusImpl.cs" + RelPath = "ClassInterfaces\ServiceStatusImpl.cs" SubType = "Code" BuildAction = "Compile" /> Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IMyInterface.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IMyInterface.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,40 @@ +// 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.Test.ClassInterfaces +{ + using System; + + /// <summary> + /// Summary description for IMyInterface. + /// </summary> + public interface IMyInterface + { + String Name + { + get; + set; + } + + bool Started + { + get; + set; + } + + int Calc(int x, int y); + + int Calc(int x, int y, int z, Single k); + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IMySecondInterface.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IMySecondInterface.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,30 @@ +// 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.Test.ClassInterfaces +{ + using System; + + /// <summary> + /// Summary description for IMySecondInterface. + /// </summary> + public interface IMySecondInterface : IMyInterface + { + String Address + { + get; + set; + } + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IServiceStatus.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/IServiceStatus.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,45 @@ +// 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.Test.ClassInterfaces +{ + using System; + + /// <summary> + /// Simple enum declaration + /// </summary> + public enum State + { + Valid, + Invalid + } + + /// <summary> + /// Summary description for IServiceStatus. + /// </summary> + public interface IServiceStatus + { + int Requests + { + get; + } + + State ActualState + { + get; + } + + void ChangeState(State state); + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/MyInterfaceImpl.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/MyInterfaceImpl.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,69 @@ +// 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.Test.ClassInterfaces +{ + using System; + + /// <summary> + /// Summary description for MyInterfaceImpl. + /// </summary> + public class MyInterfaceImpl : IMyInterface + { + private String m_name; + private bool m_started; + + public MyInterfaceImpl() + { + } + + #region IMyInterface Members + + public String Name + { + get + { + return m_name; + } + set + { + m_name = value; + } + } + + public bool Started + { + get + { + return m_started; + } + set + { + m_started = value; + } + } + + public int Calc(int x, int y) + { + return x + y; + } + + public int Calc(int x, int y, int z, Single k) + { + return x + y + z + (int)k; + } + + #endregion + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/MySecondInterfaceImpl.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/MySecondInterfaceImpl.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,46 @@ +// 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.Test.ClassInterfaces +{ + using System; + + /// <summary> + /// Summary description for MySecondInterfaceImpl. + /// </summary> + public class MySecondInterfaceImpl : MyInterfaceImpl, IMySecondInterface + { + private String m_address; + + public MySecondInterfaceImpl() + { + } + + #region IMySecondInterface Members + + public String Address + { + get + { + return m_address; + } + set + { + m_address = value; + } + } + + #endregion + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/ServiceStatusImpl.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ClassInterfaces/ServiceStatusImpl.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,55 @@ +// 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.Test.ClassInterfaces +{ + using System; + + /// <summary> + /// Summary description for ServiceStatusImpl. + /// </summary> + public class ServiceStatusImpl : IServiceStatus + { + private State m_state = State.Invalid; + + public ServiceStatusImpl() + { + } + + #region IServiceStatus Members + + public int Requests + { + get + { + return 10; + } + } + + public State ActualState + { + get + { + return m_state; + } + } + + public void ChangeState(State state) + { + m_state = state; + } + + #endregion + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/NoVirtualMethodClass.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/NoVirtualMethodClass.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,29 @@ +// 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.Test.Classes +{ + using System; + + /// <summary> + /// Summary description for NoVirtualMethodClass. + /// </summary> + public class NoVirtualMethodClass + { + public int Sum(int b1, int b2) + { + return b1 + b2; + } + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/SealedMethodsClass.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/SealedMethodsClass.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,29 @@ +// 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.Test.Classes +{ + using System; + + /// <summary> + /// Summary description for SealedMethodsClass. + /// </summary> + public class SealedMethodsClass : ServiceClass + { + public override sealed int Sum(int b1, int b2) + { + return b1 + b2; + } + } +} Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/ServiceClass.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/ServiceClass.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,74 @@ + // 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.Test.Classes +{ + using System; + + /// <summary> + /// Summary description for ServiceClass. + /// </summary> + public class ServiceClass + { + public virtual int Sum(int b1, int b2) + { + return b1 + b2; + } + + public virtual byte Sum(byte b1, byte b2) + { + return System.Convert.ToByte( b1 + b2 ); + } + + public virtual long Sum(long b1, long b2) + { + return b1 + b2; + } + + public virtual short Sum(short b1, short b2) + { + return (short) (b1 + b2); + } + + public virtual float Sum(float b1, float b2) + { + return b1 + b2; + } + + public virtual double Sum(double b1, double b2) + { + return b1 + b2; + } + + public virtual UInt16 Sum(UInt16 b1, UInt16 b2) + { + return (UInt16) (b1 + b2); + } + + public virtual UInt32 Sum(UInt32 b1, UInt32 b2) + { + return b1 + b2; + } + + public virtual UInt64 Sum(UInt64 b1, UInt64 b2) + { + return b1 + b2; + } + + public virtual bool Valid + { + get { return true; } + } + } +} \ No newline at end of file Added: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/SpecializedServiceClass.cs ============================================================================== --- (empty file) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/Classes/SpecializedServiceClass.cs Mon Sep 6 13:43:16 2004 @@ -0,0 +1,29 @@ +// 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.Test.Classes +{ + using System; + + /// <summary> + /// Summary description for SpecializedServiceClass. + /// </summary> + public class SpecializedServiceClass : ServiceClass + { + public virtual int Subtract(int b1, int b2) + { + return b1 - b2; + } + } +} Modified: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ProxyGeneratorTestCase.cs ============================================================================== --- avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ProxyGeneratorTestCase.cs (original) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/DynamicProxyTest/ProxyGeneratorTestCase.cs Mon Sep 6 13:43:16 2004 @@ -20,6 +20,8 @@ using NUnit.Framework; using Apache.Avalon.DynamicProxy; + using Apache.Avalon.DynamicProxy.Test.Classes; + using Apache.Avalon.DynamicProxy.Test.ClassInterfaces; /// <summary> /// Summary description for ProxyGeneratorTestCase. @@ -28,6 +30,100 @@ public class ProxyGeneratorTestCase : Assertion { [Test] + public void ProxyForClass() + { + object proxy = ProxyGenerator.CreateClassProxy( + typeof(ServiceClass), new ResultModifiedInvocationHandler( new ServiceClass() ) ); + + AssertNotNull( proxy ); + Assert( typeof(ServiceClass).IsAssignableFrom( proxy.GetType() ) ); + + ServiceClass inter = (ServiceClass) proxy; + + AssertEquals( 44, inter.Sum( 20, 25 ) ); + AssertEquals( true, inter.Valid ); + } + + [Test] + public void ProxyForClassWithSuperClass() + { + object proxy = ProxyGenerator.CreateClassProxy( + typeof(SpecializedServiceClass), new ResultModifiedInvocationHandler( new SpecializedServiceClass() ) ); + + AssertNotNull( proxy ); + Assert( typeof(ServiceClass).IsAssignableFrom( proxy.GetType() ) ); + Assert( typeof(SpecializedServiceClass).IsAssignableFrom( proxy.GetType() ) ); + + SpecializedServiceClass inter = (SpecializedServiceClass) proxy; + + AssertEquals( 44, inter.Sum( 20, 25 ) ); + AssertEquals( -6, inter.Subtract( 20, 25 ) ); + AssertEquals( true, inter.Valid ); + } + + [Test] + public void ProxyingClassWithoutVirtualMethods() + { + object proxy = ProxyGenerator.CreateClassProxy( + typeof(NoVirtualMethodClass), new ResultModifiedInvocationHandler( new SpecializedServiceClass() ) ); + + AssertNotNull( proxy ); + Assert( typeof(NoVirtualMethodClass).IsAssignableFrom( proxy.GetType() ) ); + + NoVirtualMethodClass inter = (NoVirtualMethodClass) proxy; + + AssertEquals( 45, inter.Sum( 20, 25 ) ); + } + + [Test] + public void ProxyingClassWithSealedMethods() + { + object proxy = ProxyGenerator.CreateClassProxy( + typeof(SealedMethodsClass), new ResultModifiedInvocationHandler( new SpecializedServiceClass() ) ); + + AssertNotNull( proxy ); + Assert( typeof(SealedMethodsClass).IsAssignableFrom( proxy.GetType() ) ); + + SealedMethodsClass inter = (SealedMethodsClass) proxy; + + AssertEquals( 45, inter.Sum( 20, 25 ) ); + } + + [Test] + public void CreateClassProxyInvalidArguments() + { + try + { + ProxyGenerator.CreateClassProxy( + typeof(ICloneable), new StandardInvocationHandler( new SpecializedServiceClass() ) ); + } + catch(ArgumentException) + { + // Expected + } + + try + { + ProxyGenerator.CreateClassProxy( + null, new StandardInvocationHandler( new SpecializedServiceClass() ) ); + } + catch(ArgumentNullException) + { + // Expected + } + + try + { + ProxyGenerator.CreateClassProxy( + typeof(SpecializedServiceClass), null ); + } + catch(ArgumentNullException) + { + // Expected + } + } + + [Test] public void TestGenerationSimpleInterface() { object proxy = ProxyGenerator.CreateProxy( @@ -98,6 +194,22 @@ #endregion } + } + + public class ResultModifiedInvocationHandler : StandardInvocationHandler + { + public ResultModifiedInvocationHandler( object instanceDelegate ) : base(instanceDelegate) + { + } + + protected override void PostInvoke(object proxy, System.Reflection.MethodInfo method, ref object returnValue, params object[] arguments) + { + if ( returnValue != null && returnValue.GetType() == typeof(int)) + { + int value = (int) returnValue; + returnValue = --value; + } + } } } Modified: avalon/trunk/central/laboratory/avalon-net/DynamicProxy/ProxyGenerator.cs ============================================================================== --- avalon/trunk/central/laboratory/avalon-net/DynamicProxy/ProxyGenerator.cs (original) +++ avalon/trunk/central/laboratory/avalon-net/DynamicProxy/ProxyGenerator.cs Mon Sep 6 13:43:16 2004 @@ -1,437 +1,116 @@ -// 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; - - /// <summary> - /// 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. - /// </summary> - /// <remarks> - /// The <see cref="ProxyGenerator"/> should be used to generate a class - /// implementing the specified interfaces. The class implementation will - /// only call the internal <see cref="IInvocationHandler"/> instance. - /// </remarks> - /// <remarks> - /// This proxy implementation currently doesn't not supports ref and out arguments - /// in methods. - /// </remarks> - /// <example> - /// <code> - /// MyInvocationHandler handler = ... - /// IInterfaceExposed proxy = - /// ProxyGenerator.CreateProxy( new Type[] { typeof(IInterfaceExposed) }, handler ); - /// </code> - /// </example> - public class ProxyGenerator - { - /// <summary> - /// Private construtor - /// </summary> - private ProxyGenerator() - { - } - - /// <summary> - /// Generates a proxy implementing all the specified interfaces and - /// redirecting method invocations to the specifed handler. - /// </summary> - /// <param name="theInterface">Interface to be implemented</param> - /// <param name="handler">instance of <see cref="IInvocationHandler"/></param> - /// <returns>Proxy instance</returns> - public static object CreateProxy(Type theInterface, IInvocationHandler handler) - { - return CreateProxy(new Type[] { theInterface }, handler ); - } - - /// <summary> - /// Generates a proxy implementing all the specified interfaces and - /// redirecting method invocations to the specifed handler. - /// </summary> - /// <param name="interfaces">Array of interfaces to be implemented</param> - /// <param name="handler">instance of <see cref="IInvocationHandler"/></param> - /// <returns>Proxy instance</returns> - 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 } ); - } - - /// <summary> - /// - /// </summary> - /// <param name="typeBuilder"></param> - /// <param name="interfaces"></param> - private static void GenerateInterfaceImplementation( TypeBuilder typeBuilder, - Type[] interfaces, FieldBuilder handlerField ) - { - foreach(Type inter in interfaces) - { - GenerateInterfaceImplementation( typeBuilder, inter, handlerField ); - } - } - - /// <summary> - /// Iterates over the interfaces and generate implementation - /// for each method in it. - /// </summary> - /// <param name="typeBuilder"><see cref="TypeBuilder"/> being constructed.</param> - /// <param name="inter">Interface type</param> - 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 ); - } - } - - /// <summary> - /// Generates a public field holding the <see cref="IInvocationHandler"/> - /// </summary> - /// <param name="typeBuilder"><see cref="TypeBuilder"/> being constructed.</param> - /// <returns><see cref="FieldBuilder"/> instance</returns> - private static FieldBuilder GenerateField( TypeBuilder typeBuilder ) - { - return typeBuilder.DefineField( "handler", - typeof(IInvocationHandler), FieldAttributes.Public ); - } - - /// <summary> - /// Generates one public constructor receiving - /// the <see cref="IInvocationHandler"/> instance. - /// </summary> - /// <param name="typeBuilder"><see cref="TypeBuilder"/> being constructed.</param> - /// <param name="handlerField"><see cref="FieldBuilder"/> instance representing the handler field</param> - /// <returns><see cref="ConstructorBuilder"/> instance</returns> - 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; - } - - /// <summary> - /// Generate property implementation - /// </summary> - /// <param name="typeBuilder"><see cref="TypeBuilder"/> being constructed.</param> - /// <param name="property"></param> - /// <param name="propertyBuilder"></param> - private static void GeneratePropertyImplementation( - TypeBuilder typeBuilder, PropertyInfo property, ref PropertyBuilder propertyBuilder ) - { - propertyBuilder = typeBuilder.DefineProperty( - property.Name, property.Attributes, property.PropertyType, null); - } - - /// <summary> - /// Generates implementation for each method. - /// </summary> - /// <param name="typeBuilder"><see cref="TypeBuilder"/> being constructed.</param> - /// <param name="method"></param> - /// <param name="properties"></param> - 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<parameterInfo.Length; i++) - { - parameters[i] = parameterInfo[i].ParameterType; - } - - MethodAttributes atts = MethodAttributes.Public|MethodAttributes.Virtual; - - if ( method.Name.StartsWith("set_") || method.Name.StartsWith("get_") ) - { - atts = MethodAttributes.Public|MethodAttributes.SpecialName|MethodAttributes.Virtual; - } - - MethodBuilder methodBuilder = - typeBuilder.DefineMethod( method.Name, atts, CallingConventions.Standard, - method.ReturnType, parameters ); - - if ( method.Name.StartsWith("set_") || method.Name.StartsWith("get_") ) - { - foreach( PropertyBuilder property in properties ) - { - if (property == null) - { - break; - } - - if (!property.Name.Equals( method.Name.Substring(4) )) - { - continue; - } - - if ( methodBuilder.Name.StartsWith("set_") ) - { - property.SetSetMethod( methodBuilder ); - break; - } - else - { - property.SetGetMethod( methodBuilder ); - break; - } - } - } - - WriteILForMethod( method, methodBuilder, parameters, handlerField ); - } - - /// <summary> - /// Writes the stack for the method implementation. This - /// method generates the IL stack for property get/set method and - /// ordinary methods. - /// </summary> - /// <remarks> - /// The method implementation would be as simple as: - /// <code> - /// public void SomeMethod( int parameter ) - /// { - /// MethodBase method = MethodBase.GetCurrentMethod(); - /// handler.Invoke( this, method, new object[] { parameter } ); - /// } - /// </code> - /// </remarks> - /// <param name="typeBuilder"><see cref="TypeBuilder"/> being constructed.</param> - /// <param name="parameters"></param> - /// <param name="handlerField"></param> - private static void WriteILForMethod( MethodInfo method, 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.Ldtoken, method); - ilGenerator.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle")); - - 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<parameters.Length; c++) - { - ilGenerator.Emit(OpCodes.Ldc_I4, c); - ilGenerator.Emit(OpCodes.Ldarg, c+1); - - if (parameters[c].IsValueType) - { - ilGenerator.Emit(OpCodes.Box, parameters[c].UnderlyingSystemType); - } - - ilGenerator.Emit(OpCodes.Stelem_Ref); - ilGenerator.Emit(OpCodes.Ldloc, arrayPositionInStack); - } - - ilGenerator.Emit(OpCodes.Callvirt, typeof(IInvocationHandler).GetMethod("Invoke") ); - - if (builder.ReturnType != typeof(void)) - { - if (!builder.ReturnType.IsValueType) - { - ilGenerator.Emit(OpCodes.Castclass, builder.ReturnType); - } - else - { - ilGenerator.Emit(OpCodes.Unbox, builder.ReturnType); - ilGenerator.Emit(ConvertTypeToOpCode(builder.ReturnType)); - } - - ilGenerator.Emit(OpCodes.Stloc, 1); - - Label label = ilGenerator.DefineLabel(); - ilGenerator.Emit(OpCodes.Br_S, label); - ilGenerator.MarkLabel(label); - ilGenerator.Emit(OpCodes.Ldloc, 1); - } - else - { - ilGenerator.Emit(OpCodes.Pop); - } - - ilGenerator.Emit(OpCodes.Ret); - } - - /// <summary> - /// Converts a Value type to a correspondent OpCode of - /// </summary> - /// <param name="type"></param> - /// <returns></returns> - 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; - } - } -} + // 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; + + using Apache.Avalon.DynamicProxy.Builder; + + /// <summary> + /// 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. + /// </summary> + /// <remarks> + /// The <see cref="ProxyGenerator"/> should be used to generate a class + /// implementing the specified interfaces. The class implementation will + /// only call the internal <see cref="IInvocationHandler"/> instance. + /// </remarks> + /// <remarks> + /// This proxy implementation currently doesn't not supports ref and out arguments + /// in methods. + /// </remarks> + /// <example> + /// <code> + /// MyInvocationHandler handler = ... + /// IInterfaceExposed proxy = + /// ProxyGenerator.CreateProxy( new Type[] { typeof(IInterfaceExposed) }, handler ); + /// </code> + /// </example> + public abstract class ProxyGenerator + { + private static IProxyBuilder m_builder = new ProxyBuilderImpl(); + + public static IProxyBuilder ProxyBuilder + { + get { return m_builder; } + set { m_builder = value; } + } + + public static object CreateClassProxy(Type baseClass, IInvocationHandler handler) + { + if (baseClass == null) + { + throw new ArgumentNullException("theClass"); + } + if (baseClass.IsInterface) + { + throw new ArgumentException("'baseClass' must be a class, not an interface"); + } + if (handler == null) + { + throw new ArgumentNullException("handler"); + } + + Type newType = ProxyBuilder.CreateClassProxy(baseClass); + return CreateProxyInstance( newType, handler ); + } + + /// <summary> + /// Generates a proxy implementing all the specified interfaces and + /// redirecting method invocations to the specifed handler. + /// </summary> + /// <param name="theInterface">Interface to be implemented</param> + /// <param name="handler">instance of <see cref="IInvocationHandler"/></param> + /// <returns>Proxy instance</returns> + public static object CreateProxy(Type theInterface, IInvocationHandler handler) + { + return CreateProxy(new Type[] {theInterface}, handler); + } + + /// <summary> + /// Generates a proxy implementing all the specified interfaces and + /// redirecting method invocations to the specifed handler. + /// </summary> + /// <param name="interfaces">Array of interfaces to be implemented</param> + /// <param name="handler">instance of <see cref="IInvocationHandler"/></param> + /// <returns>Proxy instance</returns> + 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"); + } + + Type newType = ProxyBuilder.CreateInterfaceProxy(interfaces); + return CreateProxyInstance( newType, handler ); + } + + private static object CreateProxyInstance(Type type, IInvocationHandler handler) + { + return Activator.CreateInstance(type, new object[] {handler}); + } + } +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]