From: Choy R. <ch...@us...> - 2005-01-09 06:48:44
|
Update of /cvsroot/dotnetmock/dotnetmock/DotNetMock/Dynamic/Generate In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv25106/DotNetMock/Dynamic/Generate Modified Files: ClassGenerator.cs Added Files: MockClassBuilder.cs Log Message: Factored out mock class builder to improve clarity. --- NEW FILE: MockClassBuilder.cs --- #region License // Copyright (c) 2004 Griffin Caprio & Choy Rim. All rights reserved. #endregion #region Imports using System; using System.Collections; using System.Reflection; using System.Reflection.Emit; using DotNetMock.Dynamic; #endregion namespace DotNetMock.Dynamic.Generate { /// <summary> /// Builds mock classes dynamically. /// </summary> /// <author>Griffin Caprio</author> /// <author>Choy Rim</author> public class MockClassBuilder { private const string HANDLER_FIELD_NAME = "_mockedCallHandler"; private static readonly Type HANDLER_TYPE = typeof(IMockedCallHandler); private static readonly MethodInfo CALL_HANDLING_METHOD = HANDLER_TYPE.GetMethod( "Call", new Type[] { typeof(MethodInfo), typeof(object[]) } ); TypeBuilder _typeBuilder; FieldBuilder _handlerFieldBuilder; Type _mockClass = null; /// <summary> /// Create instance of mock class builder. /// </summary> /// <param name="moduleBuilder"><see cref="ModuleBuilder"/> that /// we'll define new type in</param> /// <param name="mockClassName">name of mock class that we are /// making</param> /// <param name="superClass">base class that we are extending /// to implement our mock class</param> /// <param name="interfaces">interfaces that our mock class /// will (declare that we) implement</param> public MockClassBuilder( ModuleBuilder moduleBuilder, string mockClassName, Type superClass, Type[] interfaces ) { _typeBuilder = moduleBuilder.DefineType( mockClassName, TypeAttributes.Public, superClass, interfaces ); _handlerFieldBuilder = _typeBuilder.DefineField( HANDLER_FIELD_NAME, HANDLER_TYPE, FieldAttributes.Public ); } /// <summary> /// The mock class we've built. /// </summary> public Type MockClass { get { if ( ! IsCompiled ) { throw new InvalidOperationException( "Must compile before accessing the MockClass" ); } return _mockClass; } } /// <summary> /// The field that holds our <see cref="IMockedCallHandler"/> /// which will receive all calls to this type. /// </summary> public FieldInfo HandlerField { get { if ( ! IsCompiled ) { throw new InvalidOperationException( "Must compile before accessing the HandlerField" ); } return _mockClass.GetField(HANDLER_FIELD_NAME); } } /// <summary> /// Has this class been compiled into a usable type? /// </summary> public bool IsCompiled { get { return _mockClass!=null; } } /// <summary> /// Compile the implemented methods so far into a real /// type. /// </summary> public void Compile() { if ( IsCompiled ) { throw new InvalidOperationException( "Cannot compile class more than once." ); } _mockClass = _typeBuilder.CreateType(); } /// <summary> /// Define a mock implementation method on our mock class given /// a <see cref="MethodInfo"/>. /// </summary> /// <param name="mi"><see cref="MethodInfo"/> describing /// the method we must mock</param> public void ImplementMockedMethod(MethodInfo mi) { ImplementMockedMethod(mi.Name, mi.ReturnType, getParameterTypes(mi)); } /// <summary> /// Define a mock implementation of a method. /// </summary> /// <param name="methodName">name of method to mock</param> /// <param name="returnType">return <see cref="Type"/> of method /// to mock</param> /// <param name="parameterTypes">array of <see cref="Type"/>s in /// the method signature</param> public void ImplementMockedMethod(string methodName, Type returnType, Type[] parameterTypes) { if ( IsCompiled ) { throw new InvalidOperationException( "Cannot add methods after class has been compiled." ); } MethodBuilder methodBuilder = _typeBuilder.DefineMethod( methodName, MethodAttributes.Public | MethodAttributes.Virtual, returnType, parameterTypes ); ILGenerator il = methodBuilder.GetILGenerator(); il.DeclareLocal(typeof(object[])); il.Emit(OpCodes.Ldarg_0); // stack: this il.Emit(OpCodes.Ldfld, _handlerFieldBuilder); // stack: this handler // emit call to get current method il.EmitCall( OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod", new Type[0]), null ); // stack: this handler methodinfo // assert parameterTypes.Length<128 il.Emit(OpCodes.Ldc_I4_S, (sbyte) parameterTypes.Length); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Dup); // store args array in local var il.Emit(OpCodes.Stloc_0); // stack: this handler methodinfo args[] // fill args array with arguments of call if (parameterTypes.Length > 0) { for(int i = 0; i < parameterTypes.Length; i++) { Type parameterType = parameterTypes[i]; Type elementType = parameterType; // push array ref onto stack il.Emit(OpCodes.Ldloc_0); // push array index onto stack il.Emit(OpCodes.Ldc_I4_S, (sbyte) i); // load corresponding argument il.Emit(OpCodes.Ldarg_S, (sbyte) (i + 1)); // dereference if necessary if ( parameterType.IsByRef ) { elementType = parameterType.GetElementType(); ILUtils.EmitTypedLdind(il, elementType); } if ( elementType.IsValueType ) { il.Emit(OpCodes.Box, elementType); } il.Emit(OpCodes.Stelem_Ref); } } il.EmitCall(OpCodes.Callvirt, CALL_HANDLING_METHOD, null); if (returnType == typeof(void)) { il.Emit(OpCodes.Pop); } else { if (returnType.IsValueType) { il.Emit(OpCodes.Unbox, returnType); // load boxed value from heap ILUtils.EmitTypedLdind(il, returnType); } } // load out/ref parameter values if (parameterTypes.Length > 0) { for(int i = 0; i < parameterTypes.Length; i++) { Type parameterType = parameterTypes[i]; if ( parameterType.IsByRef ) { Type elementType = parameterType.GetElementType(); // address of byref arg il.Emit(OpCodes.Ldarg_S, (sbyte) (i+1)); // fetch value from args array il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4_S, (sbyte) i); il.Emit(OpCodes.Ldelem_Ref); // unbox if necessary if ( elementType.IsValueType ) { il.Emit(OpCodes.Unbox, elementType); ILUtils.EmitTypedLdind(il, elementType); } // store indirectly into ref arg ILUtils.EmitTypedStind(il, elementType); } } } il.Emit(OpCodes.Ret); } /// <summary> /// Returns the array of parameters types given a /// <see cref="MethodInfo"/> /// </summary> /// <param name="mi">method we want the parameter types of</param> /// <returns>array of parameter types in method signature</returns> private static Type[] getParameterTypes(MethodInfo mi) { ArrayList types = new ArrayList(); foreach (ParameterInfo pi in mi.GetParameters()) { types.Add(pi.ParameterType); } return (Type[]) types.ToArray(typeof(Type)); } } } Index: ClassGenerator.cs =================================================================== RCS file: /cvsroot/dotnetmock/dotnetmock/DotNetMock/Dynamic/Generate/ClassGenerator.cs,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** ClassGenerator.cs 8 Jan 2005 20:24:22 -0000 1.9 --- ClassGenerator.cs 9 Jan 2005 06:48:35 -0000 1.10 *************** *** 41,67 **** } /// <summary> ! /// Generates proxy mock object for the input type using the parameters from the <see cref="IMockedCallHandler"/> /// </summary> /// <param name="type">type to generate proxy for.</param> ! /// <param name="mock"><see cref="IMockedCallHandler"/> containg parameters for proxy object.</param> /// <returns>proxy mock object for input type.</returns> ! public object Generate(Type type, IMockedCallHandler mock) { ! TypeBuilder typeBuilder = getTypeBuilder("Mock" + type.Name, type); ! FieldBuilder mockFieldBuilder = typeBuilder.DefineField("underlyingMock", typeof(IMockedCallHandler), FieldAttributes.Public); IList methods = getMethods(type); ! foreach ( MethodInfo m in methods ) { ! implementMethod(typeBuilder, m, mockFieldBuilder); } ! Type proxyType = typeBuilder.CreateType(); ! object result = Activator.CreateInstance(proxyType); ! FieldInfo underlyingMock = proxyType.GetField("underlyingMock"); ! underlyingMock.SetValue(result, mock); if ( _assemblyFilename!=null ) { _assemblyBuilder.Save(_assemblyFilename); } ! return result; } private IList getMethods(Type type) --- 41,82 ---- } /// <summary> ! /// Generates a mock object for the specified type. /// </summary> /// <param name="type">type to generate proxy for.</param> ! /// <param name="handler"><see cref="IMockedCallHandler"/> which ! /// will handle all calls to the generated mock object.</param> /// <returns>proxy mock object for input type.</returns> ! public object Generate(Type type, IMockedCallHandler handler) { ! string mockClassName = "Mock" + type.Name; ! Type superClass; ! Type[] interfaces; ! determineSuperClassAndInterfaces( ! type, ! out superClass, out interfaces ! ); ! MockClassBuilder classBuilder = new MockClassBuilder( ! _moduleBuilder, ! mockClassName, ! superClass, ! interfaces ! ); IList methods = getMethods(type); ! foreach (MethodInfo mi in methods) { ! classBuilder.ImplementMockedMethod(mi); } ! // create type ! classBuilder.Compile(); ! object newMockObject = ! Activator.CreateInstance(classBuilder.MockClass); ! // set handler field ! classBuilder.HandlerField.SetValue(newMockObject, handler); ! // save if necessary if ( _assemblyFilename!=null ) { _assemblyBuilder.Save(_assemblyFilename); } ! return newMockObject; } private IList getMethods(Type type) *************** *** 89,208 **** } } ! private TypeBuilder getTypeBuilder(string name, Type originalType) { ! Type superClass; ! Type[] interfaces; ! if (originalType.IsInterface) { superClass = null; ! interfaces = new Type[] {originalType}; } else { ! superClass = originalType; interfaces = new Type[0]; } - return _moduleBuilder.DefineType(name, TypeAttributes.Public, superClass, interfaces); - } - - private void implementMethod(TypeBuilder typeBuilder, MethodInfo m, FieldBuilder mockFieldBuilder) - { - Type returnType = m.ReturnType; - ArrayList types = new ArrayList(); - foreach (ParameterInfo param in m.GetParameters()) - { - types.Add(param.ParameterType); - } - Type[] paramTypes = (Type[])types.ToArray(typeof(Type)); - MethodBuilder methodBuilder = typeBuilder.DefineMethod(m.Name, MethodAttributes.Public | MethodAttributes.Virtual, returnType, paramTypes); - - ILGenerator il = methodBuilder.GetILGenerator(); - - il.DeclareLocal(typeof(object[])); - - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, mockFieldBuilder); - // emit call to get current method - il.EmitCall( - OpCodes.Call, - typeof(MethodBase).GetMethod("GetCurrentMethod", new Type[0]), - null - ); - // assert paramTypes.Length<128 - il.Emit(OpCodes.Ldc_I4_S, (sbyte) paramTypes.Length); - il.Emit(OpCodes.Newarr, typeof(object)); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Stloc_0); - - if (paramTypes.Length > 0) - { - for(int i = 0; i < paramTypes.Length; i++) - { - Type paramType = paramTypes[i]; - Type elementType = paramType; - // push array ref onto stack - il.Emit(OpCodes.Ldloc_0); - // push array index onto stack - il.Emit(OpCodes.Ldc_I4_S, (sbyte) i); - // load corresponding argument - il.Emit(OpCodes.Ldarg_S, (sbyte) (i + 1)); - // dereference if necessary - if ( paramType.IsByRef ) - { - elementType = paramType.GetElementType(); - ILUtils.EmitTypedLdind(il, elementType); - } - if ( elementType.IsValueType ) - { - il.Emit(OpCodes.Box, elementType); - } - il.Emit(OpCodes.Stelem_Ref); - } - } - - MethodInfo call = typeof(IMockedCallHandler).GetMethod("Call", new Type[] {typeof(MethodInfo), typeof(object[]) } ); - il.EmitCall(OpCodes.Callvirt, call, null); - - if (returnType == typeof(void)) - { - il.Emit(OpCodes.Pop); - } - else - { - if (returnType.IsValueType) - { - il.Emit(OpCodes.Unbox, returnType); - // load boxed value from heap - ILUtils.EmitTypedLdind(il, returnType); - } - } - // load out/ref parameter values - if (paramTypes.Length > 0) - { - for(int i = 0; i < paramTypes.Length; i++) - { - Type paramType = paramTypes[i]; - if ( paramType.IsByRef ) - { - Type elementType = paramType.GetElementType(); - // address of byref arg - il.Emit(OpCodes.Ldarg_S, (sbyte) (i+1)); - // fetch value from args array - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldc_I4_S, (sbyte) i); - il.Emit(OpCodes.Ldelem_Ref); - // unbox if necessary - if ( elementType.IsValueType ) - { - il.Emit(OpCodes.Unbox, elementType); - ILUtils.EmitTypedLdind(il, elementType); - } - // store indirectly into ref arg - ILUtils.EmitTypedStind(il, elementType); - } - } - } - - il.Emit(OpCodes.Ret); } private static AssemblyBuilder newAssemblyBuilder(AssemblyBuilderAccess access) --- 104,119 ---- } } ! private static void determineSuperClassAndInterfaces(Type targetType, out Type superClass, out Type[] interfaces) { ! if (targetType.IsInterface) { superClass = null; ! interfaces = new Type[] { targetType }; } else { ! superClass = targetType; interfaces = new Type[0]; } } private static AssemblyBuilder newAssemblyBuilder(AssemblyBuilderAccess access) |