ParamArrayAttribute Codegen
Wednesday 9 November 2005
I mentioned a while ago that I'm adding support for XML-RPC methods with a variable number of parameters in XML-RPC.NET. I recently enhanced the XmlRpcProxyGen class to add the ParamArrayAttribute where required. I wrote the following sample program to explore how to do this (just a few lines of new code) and to get some practice using Reflection.Emit (the implementation of the Foo method).
using System;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
public interface IFoo
{
int Foo(params int[] args);
}
class Program
{
static void Main(string[] args)
{
AppDomain ad = AppDomain.CurrentDomain;
Type itf = typeof(IFoo);
AssemblyName asmName = new AssemblyName();
asmName.Name = "FooAssembly";
AssemblyBuilder asmBldr = ad.DefineDynamicAssembly(
asmName, AssemblyBuilderAccess.Run);
ModuleBuilder modBldr = asmBldr.DefineDynamicModule(
asmName.Name);
TypeBuilder typeBldr = modBldr.DefineType(
"FooClass", TypeAttributes.Class | TypeAttributes.Public,
null, new Type[] { itf });
MethodBuilder mthdBldr = typeBldr.DefineMethod("Foo",
MethodAttributes.Public | MethodAttributes.Virtual,
typeof(int), new Type[] { typeof(int[]) } );
// add ParamArrayAttribute to Foo parameter
ParameterBuilder paramBldr = mthdBldr.DefineParameter(1,
ParameterAttributes.In, "args");
ConstructorInfo ctorInfo
= typeof(ParamArrayAttribute).GetConstructor(new Type[0]);
CustomAttributeBuilder attrBldr =
new CustomAttributeBuilder(ctorInfo, new object[0]);
paramBldr.SetCustomAttribute(attrBldr);
// generate the IL
ILGenerator ilgen = mthdBldr.GetILGenerator();
Label Next = ilgen.DefineLabel();
Label Done = ilgen.DefineLabel();
LocalBuilder total = ilgen.DeclareLocal (typeof (int));
LocalBuilder idx = ilgen.DeclareLocal (typeof (int));
// get count of arguments in array
ilgen.Emit(OpCodes.Ldarg, 1);
ilgen.Emit(OpCodes.Ldlen);
ilgen.Emit(OpCodes.Stloc, idx);
// return here for subsequent arguments
ilgen.MarkLabel (Next);
// decrement index - if result less than zero we're done
ilgen.Emit(OpCodes.Ldloc, idx);
ilgen.Emit(OpCodes.Ldc_I4_1);
ilgen.Emit(OpCodes.Sub);
ilgen.Emit(OpCodes.Dup);
ilgen.Emit(OpCodes.Stloc, idx);
ilgen.Emit(OpCodes.Ldc_I4_0);
ilgen.Emit(OpCodes.Blt, Done);
// use index to get argument and add to total
ilgen.Emit(OpCodes.Ldarg, 1);
ilgen.Emit(OpCodes.Ldloc, idx);
ilgen.Emit(OpCodes.Ldelem_I4);
ilgen.Emit(OpCodes.Ldloc, total);
ilgen.Emit(OpCodes.Add);
ilgen.Emit(OpCodes.Stloc, total);
// go back for next argument
ilgen.Emit(OpCodes.Br_S, Next);
// all arguments added - put total on stack and return
ilgen.MarkLabel (Done);
ilgen.Emit(OpCodes.Ldloc, total);
ilgen.Emit(OpCodes.Ret);
// build the type
Type fooType = typeBldr.CreateType();
// check IFoo.Foo has ParamArrayAttribute
MethodInfo mi = fooType.GetMethod("Foo");
ParameterInfo pi = mi.GetParameters()[0];
Debug.Assert(Attribute.IsDefined(pi,
typeof(ParamArrayAttribute)));
// create instance of type and test it
IFoo ifoo = (IFoo)Activator.CreateInstance(fooType);
Debug.Assert(ifoo.Foo() == 0);
Debug.Assert(ifoo.Foo(1) == 1);
Debug.Assert(ifoo.Foo(1,2) == 3);
Debug.Assert(ifoo.Foo(1,2,3) == 6);
}
}
Posted by
at 08:49 PM.
Permalink.
Copyright © Charles Cook.
Valid XHTML 1.0