File: system\runtime\interopservices\tceadaptergen\eventsinkhelperwriter.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
namespace System.Runtime.InteropServices.TCEAdapterGen {
    using System.Runtime.InteropServices;
    using System;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Collections;
    using System.Diagnostics.Contracts;
    internal class EventSinkHelperWriter
    {
        public static readonly String GeneratedTypeNamePostfix = "_SinkHelper";
        
        public EventSinkHelperWriter( ModuleBuilder OutputModule, Type InputType, Type EventItfType )
        {
            m_InputType = InputType;
            m_OutputModule = OutputModule;
            m_EventItfType = EventItfType;
        }
        
        public Type Perform()
        {
            // Create the output Type.
            Type[] aInterfaces = new Type[1];
            aInterfaces[0] = m_InputType;
            String strFullName = null;
            String strNameSpace = NameSpaceExtractor.ExtractNameSpace( m_EventItfType.FullName );
            
            if (strNameSpace != "")
                strFullName = strNameSpace  + ".";
            
            strFullName += m_InputType.Name + GeneratedTypeNamePostfix;
            TypeBuilder OutputTypeBuilder = TCEAdapterGenerator.DefineUniqueType( 
                                                             strFullName,
                                                             TypeAttributes.Sealed | TypeAttributes.Public, 
                                                             null,
                                                             aInterfaces,
                                                             m_OutputModule
                                                            );
            // Hide the _SinkProvider interface
            TCEAdapterGenerator.SetHiddenAttribute(OutputTypeBuilder);
 
            // Set the class interface to none.
            TCEAdapterGenerator.SetClassInterfaceTypeToNone(OutputTypeBuilder);
 
            // Retrieve the property methods on the input interface and give them a dummy implementation.
            MethodInfo[] pMethods = TCEAdapterGenerator.GetPropertyMethods(m_InputType);
            foreach (MethodInfo method in pMethods)
            {
                DefineBlankMethod(OutputTypeBuilder, method);
            }
 
            // Retrieve the non-property methods on the input interface.
            MethodInfo[] aMethods = TCEAdapterGenerator.GetNonPropertyMethods(m_InputType);
    
            // Allocate an array to contain the delegate fields.
            FieldBuilder[] afbDelegates = new FieldBuilder[aMethods.Length];   
            // Process all the methods on the input interface.
            for ( int cMethods = 0; cMethods < aMethods.Length; cMethods++ )
            {
                if ( m_InputType == aMethods[cMethods].DeclaringType )
                {
                    // Retrieve the delegate type from the add_XXX method.
                    MethodInfo AddMeth = m_EventItfType.GetMethod( "add_" + aMethods[cMethods].Name );
                    ParameterInfo[] aParams = AddMeth.GetParameters();
                    Contract.Assert(aParams.Length == 1, "All event interface methods must take a single delegate derived type and have a void return type");    
                    Type DelegateCls = aParams[0].ParameterType;
 
                    // Define the delegate instance field.
                    afbDelegates[cMethods] = OutputTypeBuilder.DefineField( 
                                                  "m_" + aMethods[cMethods].Name + "Delegate", 
                                                  DelegateCls,
                                                  FieldAttributes.Public 
                                                 );
                    
                    // Define the event method itself.
                    DefineEventMethod( OutputTypeBuilder, aMethods[cMethods], DelegateCls, afbDelegates[cMethods] );                
                }
            }
 
            // Create the cookie field.
            FieldBuilder fbCookie = OutputTypeBuilder.DefineField( 
                                                  "m_dwCookie", 
                                                  typeof(Int32), 
                                                  FieldAttributes.Public 
                                                 );
    
            // Define the constructor.      
            DefineConstructor( OutputTypeBuilder, fbCookie, afbDelegates );
    
            return OutputTypeBuilder.CreateType();
        }
 
        private void DefineBlankMethod(TypeBuilder OutputTypeBuilder, MethodInfo Method)
        {
            ParameterInfo[] PIs = Method.GetParameters();
            Type[] parameters = new Type[PIs.Length];
            for (int i=0; i < PIs.Length; i++)
            {
                parameters[i] = PIs[i].ParameterType;
            }
           
            MethodBuilder Meth = OutputTypeBuilder.DefineMethod(Method.Name,
                                                                Method.Attributes & ~MethodAttributes.Abstract,
                                                                Method.CallingConvention,
                                                                Method.ReturnType,
                                                                parameters);
 
            ILGenerator il = Meth.GetILGenerator();
 
            AddReturn(Method.ReturnType, il, Meth);
            
            il.Emit(OpCodes.Ret);
        }
 
        private void DefineEventMethod( TypeBuilder OutputTypeBuilder, MethodInfo Method, Type DelegateCls, FieldBuilder fbDelegate )
        {
            // Retrieve the method info for the invoke method on the delegate.
            MethodInfo DelegateInvokeMethod = DelegateCls.GetMethod( "Invoke" );
            Contract.Assert(DelegateInvokeMethod != null, "Unable to find method Delegate.Invoke()");    
    
            // Retrieve the return type.
            Type ReturnType = Method.ReturnType;
        
            // Define the actual event method.
            ParameterInfo[] paramInfos = Method.GetParameters();
            Type[]          parameterTypes;
            if (paramInfos != null)
            {
                parameterTypes = new Type[paramInfos.Length];
                for (int i = 0; i < paramInfos.Length; i++)
                {
                    parameterTypes[i] = paramInfos[i].ParameterType;
                }
            }
            else
                parameterTypes = null;
 
            MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Virtual;
            MethodBuilder Meth = OutputTypeBuilder.DefineMethod( Method.Name, 
                                                                 attr, 
                                                                 CallingConventions.Standard,
                                                                 ReturnType,
                                                                 parameterTypes);
 
            // We explicitly do not specify parameter name and attributes since this Type
            // is not meant to be exposed to the user. It is only used internally to do the
            // connection point to TCE mapping.
            
            ILGenerator il = Meth.GetILGenerator();
                        
            // Create the exit branch.
            Label ExitLabel = il.DefineLabel();
        
            // Generate the code that verifies that the delegate is not null.
            il.Emit( OpCodes.Ldarg, (short)0 );
            il.Emit( OpCodes.Ldfld, fbDelegate );    
            il.Emit( OpCodes.Brfalse, ExitLabel );
                            
            // The delegate is not NULL so we need to invoke it.
            il.Emit( OpCodes.Ldarg, (short)0 );
            il.Emit( OpCodes.Ldfld, fbDelegate );
        
            // Generate the code to load the arguments before we call invoke.
            ParameterInfo[] aParams = Method.GetParameters();
            for ( int cParams = 0; cParams < aParams.Length; cParams++ )
            {
                il.Emit( OpCodes.Ldarg, (short)(cParams + 1) );
            }
        
            // Generate a tail call to invoke. This will cause the callvirt to return 
            // directly to the caller of the current method instead of actually coming
            // back to the current method and returning. This will cause the value returned
            // from the call to the COM server to be returned to the caller of this method.
                
            il.Emit( OpCodes.Callvirt, DelegateInvokeMethod );
            il.Emit( OpCodes.Ret );
        
            // This is the label that will be jumped to if no delegate is present.
            il.MarkLabel( ExitLabel );
            
            AddReturn(ReturnType, il, Meth);
            
            il.Emit( OpCodes.Ret );
        
        }
 
        private void AddReturn(Type ReturnType, ILGenerator il, MethodBuilder Meth)
        {
            // Place a dummy return value on the stack before we return.
            if ( ReturnType == typeof(void) )
            {
                // There is nothing to place on the stack.
            }
            else if ( ReturnType.IsPrimitive )
            {
                switch (System.Type.GetTypeCode(ReturnType)) 
                {
                    case TypeCode.Boolean:
                    case TypeCode.Char:
                    case TypeCode.Byte:
                    case TypeCode.SByte:
                    case TypeCode.Int16:
                    case TypeCode.UInt16:
                    case TypeCode.Int32:
                    case TypeCode.UInt32:
                        il.Emit( OpCodes.Ldc_I4_0 );
                        break;
                        
                    case TypeCode.Int64:
                    case TypeCode.UInt64:
                        il.Emit( OpCodes.Ldc_I4_0 );
                        il.Emit( OpCodes.Conv_I8 );
                        break;
                        
                    case TypeCode.Single:
                        il.Emit( OpCodes.Ldc_R4, 0 );
                        break;
                    
                    case TypeCode.Double:
                        il.Emit( OpCodes.Ldc_R4, 0 );
                        il.Emit( OpCodes.Conv_R8 );
                        break;
                    
                    default:
                        // "TypeCode" does not include IntPtr, so special case it.
                        if ( ReturnType == typeof(IntPtr) )
                            il.Emit( OpCodes.Ldc_I4_0 );
                        else
                            Contract.Assert(false, "Unexpected type for Primitive type.");    
                        break;
                }
            }
            else if ( ReturnType.IsValueType )
            {
                // Allocate stack space for the return value type.  Zero-init.
                Meth.InitLocals = true;
                LocalBuilder ltRetVal = il.DeclareLocal( ReturnType );        
                
                // Load the value class on the stack.
                il.Emit( OpCodes.Ldloc_S, ltRetVal );
                
            }
            else
            {
                // The return type is a normal type.
                il.Emit( OpCodes.Ldnull );
            }
        }
        
        private void DefineConstructor( TypeBuilder OutputTypeBuilder, FieldBuilder fbCookie, FieldBuilder[] afbDelegates )
        {
            // Retrieve the constructor info for the base classe's constructor.
            ConstructorInfo DefaultBaseClsCons = typeof(Object).GetConstructor(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, null, new Type[0], null );
            Contract.Assert(DefaultBaseClsCons != null, "Unable to find the constructor for class " + m_InputType.Name);    
        
            // Define the default constructor.
            MethodBuilder Cons = OutputTypeBuilder.DefineMethod( ".ctor", 
                                                                 MethodAttributes.Assembly | MethodAttributes.SpecialName, 
                                                                 CallingConventions.Standard,
                                                                 null,
                                                                 null);
 
            ILGenerator il = Cons.GetILGenerator();
    
            // Generate the code to call the constructor of the base class.
            il.Emit( OpCodes.Ldarg, (short)0 );
            il.Emit( OpCodes.Call, DefaultBaseClsCons );
    
            // Generate the code to set the cookie field to 0.
            il.Emit( OpCodes.Ldarg, (short)0 );
            il.Emit( OpCodes.Ldc_I4, 0 );
            il.Emit( OpCodes.Stfld, fbCookie );
    
            // Generate the code to set all the delegates to NULL.
            for ( int cDelegates = 0; cDelegates < afbDelegates.Length; cDelegates++ )
            {
                if (afbDelegates[cDelegates] != null)
                {
                    il.Emit( OpCodes.Ldarg,(short)0 );
                    il.Emit( OpCodes.Ldnull );
                    il.Emit( OpCodes.Stfld, afbDelegates[cDelegates] );
                }
            }
    
            // Emit the return opcode.
            il.Emit( OpCodes.Ret );
    
        }
 
        private Type m_InputType;  
        private Type m_EventItfType;
        private ModuleBuilder m_OutputModule;
    }
}