LuaInterface.CodeGeneration.GenerateMethod C# (CSharp) Method

GenerateMethod() private method

private GenerateMethod ( TypeBuilder myType, MethodInfo method, MethodAttributes attributes, int methodIndex, FieldInfo luaTableField, FieldInfo returnTypesField, bool generateBase, Type &returnTypes ) : void
myType System.Reflection.Emit.TypeBuilder
method System.Reflection.MethodInfo
attributes MethodAttributes
methodIndex int
luaTableField System.Reflection.FieldInfo
returnTypesField System.Reflection.FieldInfo
generateBase bool
returnTypes System.Type
return void
        private void GenerateMethod(TypeBuilder myType,MethodInfo method,MethodAttributes attributes,
            int methodIndex,FieldInfo luaTableField,FieldInfo returnTypesField,bool generateBase,out Type[] returnTypes)
        {
            ParameterInfo[] paramInfo=method.GetParameters();
            Type[] paramTypes=new Type[paramInfo.Length];
            List<Type> returnTypesList=new List<Type>();

            // Counts out and ref parameters, for later use,
            // and creates the list of return types
            int nOutParams=0; int nOutAndRefParams=0;
            Type returnType=method.ReturnType;
            returnTypesList.Add(returnType);
            for(int i=0;i<paramTypes.Length;i++)
            {
                paramTypes[i]=paramInfo[i].ParameterType;
                if((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
                    nOutParams++;
                if(paramTypes[i].IsByRef)
                {
                    returnTypesList.Add(paramTypes[i].GetElementType());
                    nOutAndRefParams++;
                }
            }
            int[] refArgs=new int[nOutAndRefParams];
            returnTypes=returnTypesList.ToArray();

            // Generates a version of the method that calls the base implementation
            // directly, for use by the base field of the table
            if(generateBase)
            {
                String baseName = "__luaInterface_base_"+method.Name;
                MethodBuilder baseMethod=myType.DefineMethod(baseName,
                    MethodAttributes.Public|MethodAttributes.NewSlot|MethodAttributes.HideBySig,
                    returnType,paramTypes);
                ILGenerator generatorBase=baseMethod.GetILGenerator();
                generatorBase.Emit(OpCodes.Ldarg_0);
                for(int i=0;i<paramTypes.Length;i++)
                    generatorBase.Emit(OpCodes.Ldarg,i+1);
                generatorBase.Emit(OpCodes.Call,method);
                //if (returnType == typeof(void))
                 //   generatorBase.Emit(OpCodes.Pop);
                generatorBase.Emit(OpCodes.Ret);
            }

            // Defines the method
            MethodBuilder methodImpl=myType.DefineMethod(method.Name,attributes,
                returnType,paramTypes);
            // If it's an implementation of an interface tells what method it
            // is overriding
            if(myType.BaseType.Equals(typeof(object)))
                myType.DefineMethodOverride(methodImpl,method);

            ILGenerator generator=methodImpl.GetILGenerator( );

            generator.DeclareLocal(typeof(object[])); // original arguments
            generator.DeclareLocal(typeof(object[])); // with out-only arguments removed
            generator.DeclareLocal(typeof(int[])); // indexes of out and ref arguments
            if(!(returnType == typeof(void))) // return value
                generator.DeclareLocal(returnType);
            else
                generator.DeclareLocal(typeof(object));
            // Initializes local variables
            generator.Emit(OpCodes.Ldc_I4,paramTypes.Length);
            generator.Emit(OpCodes.Newarr,typeof(object));
            generator.Emit(OpCodes.Stloc_0);
            generator.Emit(OpCodes.Ldc_I4,paramTypes.Length-nOutParams+1);
            generator.Emit(OpCodes.Newarr,typeof(object));
            generator.Emit(OpCodes.Stloc_1);
            generator.Emit(OpCodes.Ldc_I4,nOutAndRefParams);
            generator.Emit(OpCodes.Newarr,typeof(int));
            generator.Emit(OpCodes.Stloc_2);
            generator.Emit(OpCodes.Ldloc_1);
            generator.Emit(OpCodes.Ldc_I4_0);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldfld,luaTableField);
            generator.Emit(OpCodes.Stelem_Ref);
            // Stores the arguments into the local variables, as needed
            for(int iArgs=0,iInArgs=1,iOutArgs=0;iArgs<paramTypes.Length;iArgs++)
            {
                generator.Emit(OpCodes.Ldloc_0);
                generator.Emit(OpCodes.Ldc_I4,iArgs);
                generator.Emit(OpCodes.Ldarg,iArgs+1);
                if(paramTypes[iArgs].IsByRef)
                {
                    if(paramTypes[iArgs].GetElementType().IsValueType)
                    {
                        generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
                        generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
                    }
                    else generator.Emit(OpCodes.Ldind_Ref);
                }
                else
                {
                    if(paramTypes[iArgs].IsValueType)
                        generator.Emit(OpCodes.Box,paramTypes[iArgs]);
                }
                generator.Emit(OpCodes.Stelem_Ref);
                if(paramTypes[iArgs].IsByRef)
                {
                    generator.Emit(OpCodes.Ldloc_2);
                    generator.Emit(OpCodes.Ldc_I4,iOutArgs);
                    generator.Emit(OpCodes.Ldc_I4,iArgs);
                    generator.Emit(OpCodes.Stelem_I4);
                    refArgs[iOutArgs]=iArgs;
                    iOutArgs++;
                }
                if(paramInfo[iArgs].IsIn || (!paramInfo[iArgs].IsOut))
                {
                    generator.Emit(OpCodes.Ldloc_1);
                    generator.Emit(OpCodes.Ldc_I4,iInArgs);
                    generator.Emit(OpCodes.Ldarg,iArgs+1);
                    if(paramTypes[iArgs].IsByRef)
                    {
                        if(paramTypes[iArgs].GetElementType().IsValueType)
                        {
                            generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
                            generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
                        }
                        else generator.Emit(OpCodes.Ldind_Ref);
                    }
                    else
                    {
                        if(paramTypes[iArgs].IsValueType)
                            generator.Emit(OpCodes.Box,paramTypes[iArgs]);
                    }
                    generator.Emit(OpCodes.Stelem_Ref);
                    iInArgs++;
                }
            }
            // Gets the function the method will delegate to by calling
            // the getTableFunction method of class LuaClassHelper
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldfld,luaTableField);
            generator.Emit(OpCodes.Ldstr,method.Name);
            generator.Emit(OpCodes.Call,classHelper.GetMethod("getTableFunction"));
            Label lab1=generator.DefineLabel();
            generator.Emit(OpCodes.Dup);
            generator.Emit(OpCodes.Brtrue_S,lab1);
            // Function does not exist, call base method
            generator.Emit(OpCodes.Pop);
            if(!method.IsAbstract)
            {
                generator.Emit(OpCodes.Ldarg_0);
                for(int i=0;i<paramTypes.Length;i++)
                    generator.Emit(OpCodes.Ldarg,i+1);
                generator.Emit(OpCodes.Call,method);
                if(returnType == typeof(void))
                    generator.Emit(OpCodes.Pop);
                generator.Emit(OpCodes.Ret);
                generator.Emit(OpCodes.Ldnull);
            } else
                generator.Emit(OpCodes.Ldnull);
            Label lab2=generator.DefineLabel();
            generator.Emit(OpCodes.Br_S,lab2);
            generator.MarkLabel(lab1);
            // Function exists, call using method callFunction of LuaClassHelper
            generator.Emit(OpCodes.Ldloc_0);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldfld,returnTypesField);
            generator.Emit(OpCodes.Ldc_I4,methodIndex);
            generator.Emit(OpCodes.Ldelem_Ref);
            generator.Emit(OpCodes.Ldloc_1);
            generator.Emit(OpCodes.Ldloc_2);
            generator.Emit(OpCodes.Call,classHelper.GetMethod("callFunction"));
            generator.MarkLabel(lab2);
            // Stores the function return value
            if(returnType == typeof(void))
            {
                generator.Emit(OpCodes.Pop);
                generator.Emit(OpCodes.Ldnull);
            }
            else if(returnType.IsValueType)
            {
                generator.Emit(OpCodes.Unbox,returnType);
                generator.Emit(OpCodes.Ldobj,returnType);
            }
            else generator.Emit(OpCodes.Castclass,returnType);
            generator.Emit(OpCodes.Stloc_3);
            // Sets return values of out and ref parameters
            for(int i=0;i<refArgs.Length;i++)
            {
                generator.Emit(OpCodes.Ldarg,refArgs[i]+1);
                generator.Emit(OpCodes.Ldloc_0);
                generator.Emit(OpCodes.Ldc_I4,refArgs[i]);
                generator.Emit(OpCodes.Ldelem_Ref);
                if(paramTypes[refArgs[i]].GetElementType().IsValueType)
                {
                    generator.Emit(OpCodes.Unbox,paramTypes[refArgs[i]].GetElementType());
                    generator.Emit(OpCodes.Ldobj,paramTypes[refArgs[i]].GetElementType());
                    generator.Emit(OpCodes.Stobj,paramTypes[refArgs[i]].GetElementType());
                }
                else
                {
                    generator.Emit(OpCodes.Castclass,paramTypes[refArgs[i]].GetElementType());
                    generator.Emit(OpCodes.Stind_Ref);
                }
            }

            // Returns
            if(!(returnType == typeof(void)))
                generator.Emit(OpCodes.Ldloc_3);
            generator.Emit(OpCodes.Ret);
        }