context: /* 
  XML-RPC.NET library
  Copyright (c) 2001-2006, Charles Cook <charlescook@cookcomputing.com>
  
  Permission is hereby granted, free of charge, to any person 
  obtaining a copy of this software and associated documentation 
  files (the "Software"), to deal in the Software without restriction, 
  including without limitation the rights to use, copy, modify, merge, 
  publish, distribute, sublicense, and/or sell copies of the Software, 
  and to permit persons to whom the Software is furnished to do so, 
  subject to the following conditions:
  
  The above copyright notice and this permission notice shall be 
  included in all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
  DEALINGS IN THE SOFTWARE.
  */
  
  namespace CookComputing.XmlRpc
  {
    using System;
    using System.Collections;
    using System.Reflection;
    using System.Reflection.Emit;
  
    public class XmlRpcProxyGen
    {
      static Hashtable _types = new Hashtable();
  
  #if (!FX1_0)
      public static T Create<T>()
      {
        return (T)Create(typeof(T));
      }
  #endif
  
      public static object Create(Type itf)
      {
        // create transient assembly
        Type proxyType;
        lock (typeof(XmlRpcProxyGen))
        {
          proxyType = (Type)_types[itf];
          if (proxyType == null)
          {
            Guid guid = Guid.NewGuid();
            string assemblyName = "XmlRpcProxy" + guid.ToString();
            string moduleName = "XmlRpcProxy" + guid.ToString() + ".dll";
            string typeName = "XmlRpcProxy" + guid.ToString();
            AssemblyBuilder assBldr = BuildAssembly(itf, assemblyName,
              moduleName, typeName, AssemblyBuilderAccess.Run);
            proxyType = assBldr.GetType(typeName);
            _types.Add(itf, proxyType);
          }
        }
        object ret = Activator.CreateInstance(proxyType);
        return ret;
      }
  
      public static object CreateAssembly(
        Type itf,
        string typeName,
        string assemblyName
        )
      {
        // create persistable assembly
        if (assemblyName.IndexOf(".dll") == (assemblyName.Length - 4))
          assemblyName = assemblyName.Substring(0, assemblyName.Length - 4);
        string moduleName = assemblyName + ".dll";
        AssemblyBuilder assBldr = BuildAssembly(itf, assemblyName,
          moduleName, typeName, AssemblyBuilderAccess.RunAndSave);
        Type proxyType = assBldr.GetType(typeName);
        object ret = Activator.CreateInstance(proxyType);
        assBldr.Save(moduleName);
        return ret;
      }
  
      static AssemblyBuilder BuildAssembly(
        Type itf,
        string assemblyName,
        string moduleName,
        string typeName,
        AssemblyBuilderAccess access)
      {
        string urlString = GetXmlRpcUrl(itf);
        ArrayList methods = GetXmlRpcMethods(itf);
        ArrayList beginMethods = GetXmlRpcBeginMethods(itf);
        ArrayList endMethods = GetXmlRpcEndMethods(itf);
        AssemblyName assName = new AssemblyName();
        assName.Name = assemblyName;
        if (access == AssemblyBuilderAccess.RunAndSave)
          assName.Version = itf.Assembly.GetName().Version;
        AssemblyBuilder assBldr = AppDomain.CurrentDomain.DefineDynamicAssembly(
          assName, access);
        ModuleBuilder modBldr = (access == AssemblyBuilderAccess.Run
          ? assBldr.DefineDynamicModule(assName.Name)
          : assBldr.DefineDynamicModule(assName.Name, moduleName));
        TypeBuilder typeBldr = modBldr.DefineType(
          typeName,
          TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public,
          typeof(XmlRpcClientProtocol),
          new Type[] { itf });
        BuildConstructor(typeBldr, typeof(XmlRpcClientProtocol), urlString);
        BuildMethods(typeBldr, methods);
        BuildBeginMethods(typeBldr, beginMethods);
        BuildEndMethods(typeBldr, endMethods);
        typeBldr.CreateType();
        return assBldr;
      }
  
      static void BuildMethods(TypeBuilder tb, ArrayList methods)
      {
        foreach (MethodData mthdData in methods)
        {
          MethodInfo mi = mthdData.mi;
          Type[] argTypes = new Type[mi.GetParameters().Length];
          string[] paramNames = new string[mi.GetParameters().Length];
          for (int i = 0; i < mi.GetParameters().Length; i++)
          {
            argTypes[i] = mi.GetParameters()[i].ParameterType;
            paramNames[i] = mi.GetParameters()[i].Name;
          }
          XmlRpcMethodAttribute mattr = (XmlRpcMethodAttribute)
            Attribute.GetCustomAttribute(mi, typeof(XmlRpcMethodAttribute));
          BuildMethod(tb, mi.Name, mthdData.xmlRpcName, paramNames, argTypes,
            mthdData.paramsMethod, mi.ReturnType, mattr.StructParams);
        }
      }
  
      static void BuildMethod(
        TypeBuilder tb,
        string methodName,
        string rpcMethodName,
        string[] paramNames,
        Type[] argTypes,
        bool paramsMethod,
        Type returnType,
        bool structParams)
      {
        MethodBuilder mthdBldr = tb.DefineMethod(
          methodName,
          MethodAttributes.Public | MethodAttributes.Virtual,
          returnType, argTypes);
        // add attribute to method
        Type[] oneString = new Type[1] { typeof(string) };
        Type methodAttr = typeof(XmlRpcMethodAttribute);
        ConstructorInfo ci = methodAttr.GetConstructor(oneString);
        PropertyInfo[] pis 
          = new PropertyInfo[] { methodAttr.GetProperty("StructParams") };
        object[] structParam = new object[] { structParams };
        CustomAttributeBuilder cab =
          new CustomAttributeBuilder(ci, new object[] { rpcMethodName },
            pis, structParam);
        mthdBldr.SetCustomAttribute(cab);
        for (int i = 0; i < paramNames.Length; i++)
        {
          ParameterBuilder paramBldr = mthdBldr.DefineParameter(i + 1, 
            ParameterAttributes.In, paramNames[i]);
          // possibly add ParamArrayAttribute to final parameter
          if (i == paramNames.Length - 1 && paramsMethod)
          {
            ConstructorInfo ctorInfo = typeof(ParamArrayAttribute).GetConstructor(
              new Type[0]);
            CustomAttributeBuilder attrBldr =
              new CustomAttributeBuilder(ctorInfo, new object[0]);
            paramBldr.SetCustomAttribute(attrBldr);
          }
        }
        // generate IL
        ILGenerator ilgen = mthdBldr.GetILGenerator();
        // if non-void return, declared locals for processing return value
        LocalBuilder retVal = null;
        LocalBuilder tempRetVal = null;
        if (typeof(void) != returnType)
        {
          tempRetVal = ilgen.DeclareLocal(typeof(System.Object));
          retVal = ilgen.DeclareLocal(returnType);
        }
        // declare variable to store method args and emit code to populate ut
        LocalBuilder argValues = ilgen.DeclareLocal(typeof(System.Object[]));
        ilgen.Emit(OpCodes.Ldc_I4, argTypes.Length);
        ilgen.Emit(OpCodes.Newarr, typeof(System.Object));
        ilgen.Emit(OpCodes.Stloc, argValues);
        for (int argLoad = 0; argLoad < argTypes.Length; argLoad++)
        {
          ilgen.Emit(OpCodes.Ldloc, argValues);
          ilgen.Emit(OpCodes.Ldc_I4, argLoad);
          ilgen.Emit(OpCodes.Ldarg, argLoad + 1);
          if (argTypes[argLoad].IsValueType)
          {
            ilgen.Emit(OpCodes.Box, argTypes[argLoad]);
          }
          ilgen.Emit(OpCodes.Stelem_Ref);
        }
        // call Invoke on base class
        Type[] invokeTypes = new Type[] { typeof(MethodInfo), typeof(object[]) };
        MethodInfo invokeMethod
          = typeof(XmlRpcClientProtocol).GetMethod("Invoke", invokeTypes);
        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod"));
        ilgen.Emit(OpCodes.Castclass, typeof(System.Reflection.MethodInfo));
        ilgen.Emit(OpCodes.Ldloc, argValues);
        ilgen.Emit(OpCodes.Call, invokeMethod);
        //  if non-void return prepare return value, otherwise pop to discard 
        if (typeof(void) != returnType)
        {
          // if return value is null, don't cast it to required type
          Label retIsNull = ilgen.DefineLabel();
          ilgen.Emit(OpCodes.Stloc, tempRetVal);
          ilgen.Emit(OpCodes.Ldloc, tempRetVal);
          ilgen.Emit(OpCodes.Brfalse, retIsNull);
          ilgen.Emit(OpCodes.Ldloc, tempRetVal);
          if (true == returnType.IsValueType)
          {
            ilgen.Emit(OpCodes.Unbox, returnType);
            ilgen.Emit(OpCodes.Ldobj, returnType);
          }
          else
          {
            ilgen.Emit(OpCodes.Castclass, returnType);
          }
          ilgen.Emit(OpCodes.Stloc, retVal);
          ilgen.MarkLabel(retIsNull);
          ilgen.Emit(OpCodes.Ldloc, retVal);
        }
        else
        {
          ilgen.Emit(OpCodes.Pop);
        }
        ilgen.Emit(OpCodes.Ret);
      }
  
      static void BuildBeginMethods(TypeBuilder tb, ArrayList methods)
      {
        foreach (MethodData mthdData in methods)
        {
          MethodInfo mi = mthdData.mi;
          // assume method has already been validated for required signature   
          int paramCount = mi.GetParameters().Length;
          // argCount counts of params before optional AsyncCallback param
          int argCount = paramCount;
          Type[] argTypes = new Type[paramCount];
          for (int i = 0; i < mi.GetParameters().Length; i++)
          {
            argTypes[i] = mi.GetParameters()[i].ParameterType;
            if (argTypes[i] == typeof(System.AsyncCallback))
              argCount = i;
          }
          MethodBuilder mthdBldr = tb.DefineMethod(
            mi.Name,
            MethodAttributes.Public | MethodAttributes.Virtual,
            mi.ReturnType,
            argTypes);
          // add attribute to method
          Type[] oneString = new Type[1] { typeof(string) };
          Type methodAttr = typeof(XmlRpcBeginAttribute);
          ConstructorInfo ci = methodAttr.GetConstructor(oneString);
          CustomAttributeBuilder cab =
            new CustomAttributeBuilder(ci, new object[] { mthdData.xmlRpcName });
          mthdBldr.SetCustomAttribute(cab);
          // start generating IL
          ILGenerator ilgen = mthdBldr.GetILGenerator();
          // declare variable to store method args and emit code to populate it
          LocalBuilder argValues = ilgen.DeclareLocal(typeof(System.Object[]));
          ilgen.Emit(OpCodes.Ldc_I4, argCount);
          ilgen.Emit(OpCodes.Newarr, typeof(System.Object));
          ilgen.Emit(OpCodes.Stloc, argValues);
          for (int argLoad = 0; argLoad < argCount; argLoad++)
          {
            ilgen.Emit(OpCodes.Ldloc, argValues);
            ilgen.Emit(OpCodes.Ldc_I4, argLoad);
            ilgen.Emit(OpCodes.Ldarg, argLoad + 1);
            ParameterInfo pi = mi.GetParameters()[argLoad];
            string paramTypeName = pi.ParameterType.AssemblyQualifiedName;
            paramTypeName = paramTypeName.Replace("&", "");
            Type paramType = Type.GetType(paramTypeName);
            if (paramType.IsValueType)
            {
              ilgen.Emit(OpCodes.Box, paramType);
            }
            ilgen.Emit(OpCodes.Stelem_Ref);
          }
          // emit code to store AsyncCallback parameter, defaulting to null 
          // if not in method signature
          LocalBuilder acbValue = ilgen.DeclareLocal(typeof(System.AsyncCallback));
          if (argCount < paramCount)
          {
            ilgen.Emit(OpCodes.Ldarg, argCount + 1);
            ilgen.Emit(OpCodes.Stloc, acbValue);
          }
          // emit code to store async state parameter, defaulting to null 
          // if not in method signature
          LocalBuilder objValue = ilgen.DeclareLocal(typeof(System.Object));
          if (argCount < (paramCount - 1))
          {
            ilgen.Emit(OpCodes.Ldarg, argCount + 2);
            ilgen.Emit(OpCodes.Stloc, objValue);
          }
          // emit code to call BeginInvoke on base class
          Type[] invokeTypes = new Type[] 
        { 
          typeof(MethodInfo), 
          typeof(object[]), 
          typeof(System.Object),
          typeof(System.AsyncCallback),
          typeof(System.Object)
        };
          MethodInfo invokeMethod
            = typeof(XmlRpcClientProtocol).GetMethod("BeginInvoke", invokeTypes);
          ilgen.Emit(OpCodes.Ldarg_0);
          ilgen.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod"));
          ilgen.Emit(OpCodes.Castclass, typeof(System.Reflection.MethodInfo));
          ilgen.Emit(OpCodes.Ldloc, argValues);
          ilgen.Emit(OpCodes.Ldarg_0);
          ilgen.Emit(OpCodes.Ldloc, acbValue);
          ilgen.Emit(OpCodes.Ldloc, objValue);
          ilgen.Emit(OpCodes.Call, invokeMethod);
          // BeginInvoke will leave IAsyncResult on stack - leave it there
          // for return value from method being built
          ilgen.Emit(OpCodes.Ret);
        }
      }
  
      static void BuildEndMethods(TypeBuilder tb, ArrayList methods)
      {
        LocalBuilder retVal = null;
        LocalBuilder tempRetVal = null;
        foreach (MethodData mthdData in methods)
        {
          MethodInfo mi = mthdData.mi;
          Type[] argTypes = new Type[] { typeof(System.IAsyncResult) };
          MethodBuilder mthdBldr = tb.DefineMethod(mi.Name,
            MethodAttributes.Public | MethodAttributes.Virtual,
            mi.ReturnType, argTypes);
          // start generating IL
          ILGenerator ilgen = mthdBldr.GetILGenerator();
          // if non-void return, declared locals for processing return value
          if (typeof(void) != mi.ReturnType)
          {
            tempRetVal = ilgen.DeclareLocal(typeof(System.Object));
            retVal = ilgen.DeclareLocal(mi.ReturnType);
          }
          // call EndInvoke on base class
          Type[] invokeTypes
            = new Type[] { typeof(System.IAsyncResult), typeof(System.Type) };
          MethodInfo invokeMethod
            = typeof(XmlRpcClientProtocol).GetMethod("EndInvoke", invokeTypes);
          Type[] GetTypeTypes
            = new Type[] { typeof(System.String) };
          MethodInfo GetTypeMethod
            = typeof(System.Type).GetMethod("GetType", GetTypeTypes);
          ilgen.Emit(OpCodes.Ldarg_0);  // "this"
          ilgen.Emit(OpCodes.Ldarg_1);  // IAsyncResult parameter
          ilgen.Emit(OpCodes.Ldstr, mi.ReturnType.AssemblyQualifiedName);
          ilgen.Emit(OpCodes.Call, GetTypeMethod);
          ilgen.Emit(OpCodes.Call, invokeMethod);
          //  if non-void return prepare return value otherwise pop to discard 
          if (typeof(void) != mi.ReturnType)
          {
            // if return value is null, don't cast it to required type
            Label retIsNull = ilgen.DefineLabel();
            ilgen.Emit(OpCodes.Stloc, tempRetVal);
            ilgen.Emit(OpCodes.Ldloc, tempRetVal);
            ilgen.Emit(OpCodes.Brfalse, retIsNull);
            ilgen.Emit(OpCodes.Ldloc, tempRetVal);
            if (true == mi.ReturnType.IsValueType)
            {
              ilgen.Emit(OpCodes.Unbox, mi.ReturnType);
              ilgen.Emit(OpCodes.Ldobj, mi.ReturnType);
            }
            else
            {
              ilgen.Emit(OpCodes.Castclass, mi.ReturnType);
            }
            ilgen.Emit(OpCodes.Stloc, retVal);
            ilgen.MarkLabel(retIsNull);
            ilgen.Emit(OpCodes.Ldloc, retVal);
          }
          else
          {
            // void method so throw away result from EndInvoke
            ilgen.Emit(OpCodes.Pop);
          }
          ilgen.Emit(OpCodes.Ret);
        }
      }
  
      private static void BuildConstructor(
        TypeBuilder typeBldr,
        Type baseType,
        string urlStr)
      {
        ConstructorBuilder ctorBldr = typeBldr.DefineConstructor(
          MethodAttributes.Public | MethodAttributes.SpecialName |
          MethodAttributes.RTSpecialName | MethodAttributes.HideBySig,
          CallingConventions.Standard,
          Type.EmptyTypes);
        if (urlStr != null && urlStr.Length > 0)
        {
          Type urlAttr = typeof(XmlRpcUrlAttribute);
          Type[] oneString = new Type[1] { typeof(string) };
          ConstructorInfo ci = urlAttr.GetConstructor(oneString);
          CustomAttributeBuilder cab =
            new CustomAttributeBuilder(ci, new object[] { urlStr });
          typeBldr.SetCustomAttribute(cab);
        }
        ILGenerator ilgen = ctorBldr.GetILGenerator();
        //  Call the base constructor.
        ilgen.Emit(OpCodes.Ldarg_0);
        ConstructorInfo ctorInfo = baseType.GetConstructor(System.Type.EmptyTypes);
        ilgen.Emit(OpCodes.Call, ctorInfo);
        ilgen.Emit(OpCodes.Ret);
      }
  
      private static string GetXmlRpcUrl(Type itf)
      {
        Attribute attr = Attribute.GetCustomAttribute(itf,
          typeof(XmlRpcUrlAttribute));
        if (attr == null)
          return null;
        XmlRpcUrlAttribute xruAttr = attr as XmlRpcUrlAttribute;
        string url = xruAttr.Uri;
        return url;
      }
  
      /// <summary>
      /// Type.GetMethods() does not return methods that a derived interface
      /// inherits from its base interfaces; this method does.
      /// </summary>
      private static MethodInfo[] GetMethods(Type type)
      {
        MethodInfo[] methods = type.GetMethods();
        if (!type.IsInterface)
        {
          return methods;
        }
  
        Type[] interfaces = type.GetInterfaces();
        if (interfaces.Length == 0)
        {
          return methods;
        }
  
        ArrayList result = new ArrayList(methods);
  
gt:       foreach (Type itf in type.GetInterfaces())