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