source: xmlrpcnet/trunk/fuentes/src/XmlRpcProxyGen.cs @ 379

Last change on this file since 379 was 379, checked in by hectorgh, 5 years ago

adding project files

File size: 21.8 KB
Line 
1/*
2XML-RPC.NET library
3Copyright (c) 2001-2006, Charles Cook <charlescook@cookcomputing.com>
4
5Permission is hereby granted, free of charge, to any person
6obtaining a copy of this software and associated documentation
7files (the "Software"), to deal in the Software without restriction,
8including without limitation the rights to use, copy, modify, merge,
9publish, distribute, sublicense, and/or sell copies of the Software,
10and to permit persons to whom the Software is furnished to do so,
11subject to the following conditions:
12
13The above copyright notice and this permission notice shall be
14included in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23DEALINGS IN THE SOFTWARE.
24*/
25
26namespace CookComputing.XmlRpc
27{
28  using System;
29  using System.Collections;
30  using System.Reflection;
31  using System.Reflection.Emit;
32
33  public class XmlRpcProxyGen
34  {
35    static Hashtable _types = new Hashtable();
36
37#if (!FX1_0)
38    public static T Create<T>()
39    {
40      return (T)Create(typeof(T));
41    }
42#endif
43
44    public static object Create(Type itf)
45    {
46      // create transient assembly
47      Type proxyType;
48      lock (typeof(XmlRpcProxyGen))
49      {
50        proxyType = (Type)_types[itf];
51        if (proxyType == null)
52        {
53          Guid guid = Guid.NewGuid();
54          string assemblyName = "XmlRpcProxy" + guid.ToString();
55          string moduleName = "XmlRpcProxy" + guid.ToString() + ".dll";
56          string typeName = "XmlRpcProxy" + guid.ToString();
57          AssemblyBuilder assBldr = BuildAssembly(itf, assemblyName,
58            moduleName, typeName, AssemblyBuilderAccess.Run);
59          proxyType = assBldr.GetType(typeName);
60          _types.Add(itf, proxyType);
61        }
62      }
63      object ret = Activator.CreateInstance(proxyType);
64      return ret;
65    }
66
67    public static object CreateAssembly(
68      Type itf,
69      string typeName,
70      string assemblyName
71      )
72    {
73      // create persistable assembly
74      if (assemblyName.IndexOf(".dll") == (assemblyName.Length - 4))
75        assemblyName = assemblyName.Substring(0, assemblyName.Length - 4);
76      string moduleName = assemblyName + ".dll";
77      AssemblyBuilder assBldr = BuildAssembly(itf, assemblyName,
78        moduleName, typeName, AssemblyBuilderAccess.RunAndSave);
79      Type proxyType = assBldr.GetType(typeName);
80      object ret = Activator.CreateInstance(proxyType);
81      assBldr.Save(moduleName);
82      return ret;
83    }
84
85    static AssemblyBuilder BuildAssembly(
86      Type itf,
87      string assemblyName,
88      string moduleName,
89      string typeName,
90      AssemblyBuilderAccess access)
91    {
92      string urlString = GetXmlRpcUrl(itf);
93      ArrayList methods = GetXmlRpcMethods(itf);
94      ArrayList beginMethods = GetXmlRpcBeginMethods(itf);
95      ArrayList endMethods = GetXmlRpcEndMethods(itf);
96      AssemblyName assName = new AssemblyName();
97      assName.Name = assemblyName;
98      if (access == AssemblyBuilderAccess.RunAndSave)
99        assName.Version = itf.Assembly.GetName().Version;
100      AssemblyBuilder assBldr = AppDomain.CurrentDomain.DefineDynamicAssembly(
101        assName, access);
102      ModuleBuilder modBldr = (access == AssemblyBuilderAccess.Run
103        ? assBldr.DefineDynamicModule(assName.Name)
104        : assBldr.DefineDynamicModule(assName.Name, moduleName));
105      TypeBuilder typeBldr = modBldr.DefineType(
106        typeName,
107        TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public,
108        typeof(XmlRpcClientProtocol),
109        new Type[] { itf });
110      BuildConstructor(typeBldr, typeof(XmlRpcClientProtocol), urlString);
111      BuildMethods(typeBldr, methods);
112      BuildBeginMethods(typeBldr, beginMethods);
113      BuildEndMethods(typeBldr, endMethods);
114      typeBldr.CreateType();
115      return assBldr;
116    }
117
118    static void BuildMethods(TypeBuilder tb, ArrayList methods)
119    {
120      foreach (MethodData mthdData in methods)
121      {
122        MethodInfo mi = mthdData.mi;
123        Type[] argTypes = new Type[mi.GetParameters().Length];
124        string[] paramNames = new string[mi.GetParameters().Length];
125        for (int i = 0; i < mi.GetParameters().Length; i++)
126        {
127          argTypes[i] = mi.GetParameters()[i].ParameterType;
128          paramNames[i] = mi.GetParameters()[i].Name;
129        }
130        XmlRpcMethodAttribute mattr = (XmlRpcMethodAttribute)
131          Attribute.GetCustomAttribute(mi, typeof(XmlRpcMethodAttribute));
132        BuildMethod(tb, mi.Name, mthdData.xmlRpcName, paramNames, argTypes,
133          mthdData.paramsMethod, mi.ReturnType, mattr.StructParams);
134      }
135    }
136
137    static void BuildMethod(
138      TypeBuilder tb,
139      string methodName,
140      string rpcMethodName,
141      string[] paramNames,
142      Type[] argTypes,
143      bool paramsMethod,
144      Type returnType,
145      bool structParams)
146    {
147      MethodBuilder mthdBldr = tb.DefineMethod(
148        methodName,
149        MethodAttributes.Public | MethodAttributes.Virtual,
150        returnType, argTypes);
151      // add attribute to method
152      Type[] oneString = new Type[1] { typeof(string) };
153      Type methodAttr = typeof(XmlRpcMethodAttribute);
154      ConstructorInfo ci = methodAttr.GetConstructor(oneString);
155      PropertyInfo[] pis
156        = new PropertyInfo[] { methodAttr.GetProperty("StructParams") };
157      object[] structParam = new object[] { structParams };
158      CustomAttributeBuilder cab =
159        new CustomAttributeBuilder(ci, new object[] { rpcMethodName },
160          pis, structParam);
161      mthdBldr.SetCustomAttribute(cab);
162      for (int i = 0; i < paramNames.Length; i++)
163      {
164        ParameterBuilder paramBldr = mthdBldr.DefineParameter(i + 1, 
165          ParameterAttributes.In, paramNames[i]);
166        // possibly add ParamArrayAttribute to final parameter
167        if (i == paramNames.Length - 1 && paramsMethod)
168        {
169          ConstructorInfo ctorInfo = typeof(ParamArrayAttribute).GetConstructor(
170            new Type[0]);
171          CustomAttributeBuilder attrBldr =
172            new CustomAttributeBuilder(ctorInfo, new object[0]);
173          paramBldr.SetCustomAttribute(attrBldr);
174        }
175      }
176      // generate IL
177      ILGenerator ilgen = mthdBldr.GetILGenerator();
178      // if non-void return, declared locals for processing return value
179      LocalBuilder retVal = null;
180      LocalBuilder tempRetVal = null;
181      if (typeof(void) != returnType)
182      {
183        tempRetVal = ilgen.DeclareLocal(typeof(System.Object));
184        retVal = ilgen.DeclareLocal(returnType);
185      }
186      // declare variable to store method args and emit code to populate ut
187      LocalBuilder argValues = ilgen.DeclareLocal(typeof(System.Object[]));
188      ilgen.Emit(OpCodes.Ldc_I4, argTypes.Length);
189      ilgen.Emit(OpCodes.Newarr, typeof(System.Object));
190      ilgen.Emit(OpCodes.Stloc, argValues);
191      for (int argLoad = 0; argLoad < argTypes.Length; argLoad++)
192      {
193        ilgen.Emit(OpCodes.Ldloc, argValues);
194        ilgen.Emit(OpCodes.Ldc_I4, argLoad);
195        ilgen.Emit(OpCodes.Ldarg, argLoad + 1);
196        if (argTypes[argLoad].IsValueType)
197        {
198          ilgen.Emit(OpCodes.Box, argTypes[argLoad]);
199        }
200        ilgen.Emit(OpCodes.Stelem_Ref);
201      }
202      // call Invoke on base class
203      Type[] invokeTypes = new Type[] { typeof(MethodInfo), typeof(object[]) };
204      MethodInfo invokeMethod
205        = typeof(XmlRpcClientProtocol).GetMethod("Invoke", invokeTypes);
206      ilgen.Emit(OpCodes.Ldarg_0);
207      ilgen.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod"));
208      ilgen.Emit(OpCodes.Castclass, typeof(System.Reflection.MethodInfo));
209      ilgen.Emit(OpCodes.Ldloc, argValues);
210      ilgen.Emit(OpCodes.Call, invokeMethod);
211      //  if non-void return prepare return value, otherwise pop to discard
212      if (typeof(void) != returnType)
213      {
214        // if return value is null, don't cast it to required type
215        Label retIsNull = ilgen.DefineLabel();
216        ilgen.Emit(OpCodes.Stloc, tempRetVal);
217        ilgen.Emit(OpCodes.Ldloc, tempRetVal);
218        ilgen.Emit(OpCodes.Brfalse, retIsNull);
219        ilgen.Emit(OpCodes.Ldloc, tempRetVal);
220        if (true == returnType.IsValueType)
221        {
222          ilgen.Emit(OpCodes.Unbox, returnType);
223          ilgen.Emit(OpCodes.Ldobj, returnType);
224        }
225        else
226        {
227          ilgen.Emit(OpCodes.Castclass, returnType);
228        }
229        ilgen.Emit(OpCodes.Stloc, retVal);
230        ilgen.MarkLabel(retIsNull);
231        ilgen.Emit(OpCodes.Ldloc, retVal);
232      }
233      else
234      {
235        ilgen.Emit(OpCodes.Pop);
236      }
237      ilgen.Emit(OpCodes.Ret);
238    }
239
240    static void BuildBeginMethods(TypeBuilder tb, ArrayList methods)
241    {
242      foreach (MethodData mthdData in methods)
243      {
244        MethodInfo mi = mthdData.mi;
245        // assume method has already been validated for required signature   
246        int paramCount = mi.GetParameters().Length;
247        // argCount counts of params before optional AsyncCallback param
248        int argCount = paramCount;
249        Type[] argTypes = new Type[paramCount];
250        for (int i = 0; i < mi.GetParameters().Length; i++)
251        {
252          argTypes[i] = mi.GetParameters()[i].ParameterType;
253          if (argTypes[i] == typeof(System.AsyncCallback))
254            argCount = i;
255        }
256        MethodBuilder mthdBldr = tb.DefineMethod(
257          mi.Name,
258          MethodAttributes.Public | MethodAttributes.Virtual,
259          mi.ReturnType,
260          argTypes);
261        // add attribute to method
262        Type[] oneString = new Type[1] { typeof(string) };
263        Type methodAttr = typeof(XmlRpcBeginAttribute);
264        ConstructorInfo ci = methodAttr.GetConstructor(oneString);
265        CustomAttributeBuilder cab =
266          new CustomAttributeBuilder(ci, new object[] { mthdData.xmlRpcName });
267        mthdBldr.SetCustomAttribute(cab);
268        // start generating IL
269        ILGenerator ilgen = mthdBldr.GetILGenerator();
270        // declare variable to store method args and emit code to populate it
271        LocalBuilder argValues = ilgen.DeclareLocal(typeof(System.Object[]));
272        ilgen.Emit(OpCodes.Ldc_I4, argCount);
273        ilgen.Emit(OpCodes.Newarr, typeof(System.Object));
274        ilgen.Emit(OpCodes.Stloc, argValues);
275        for (int argLoad = 0; argLoad < argCount; argLoad++)
276        {
277          ilgen.Emit(OpCodes.Ldloc, argValues);
278          ilgen.Emit(OpCodes.Ldc_I4, argLoad);
279          ilgen.Emit(OpCodes.Ldarg, argLoad + 1);
280          ParameterInfo pi = mi.GetParameters()[argLoad];
281          string paramTypeName = pi.ParameterType.AssemblyQualifiedName;
282          paramTypeName = paramTypeName.Replace("&", "");
283          Type paramType = Type.GetType(paramTypeName);
284          if (paramType.IsValueType)
285          {
286            ilgen.Emit(OpCodes.Box, paramType);
287          }
288          ilgen.Emit(OpCodes.Stelem_Ref);
289        }
290        // emit code to store AsyncCallback parameter, defaulting to null
291        // if not in method signature
292        LocalBuilder acbValue = ilgen.DeclareLocal(typeof(System.AsyncCallback));
293        if (argCount < paramCount)
294        {
295          ilgen.Emit(OpCodes.Ldarg, argCount + 1);
296          ilgen.Emit(OpCodes.Stloc, acbValue);
297        }
298        // emit code to store async state parameter, defaulting to null
299        // if not in method signature
300        LocalBuilder objValue = ilgen.DeclareLocal(typeof(System.Object));
301        if (argCount < (paramCount - 1))
302        {
303          ilgen.Emit(OpCodes.Ldarg, argCount + 2);
304          ilgen.Emit(OpCodes.Stloc, objValue);
305        }
306        // emit code to call BeginInvoke on base class
307        Type[] invokeTypes = new Type[] 
308      { 
309        typeof(MethodInfo), 
310        typeof(object[]), 
311        typeof(System.Object),
312        typeof(System.AsyncCallback),
313        typeof(System.Object)
314      };
315        MethodInfo invokeMethod
316          = typeof(XmlRpcClientProtocol).GetMethod("BeginInvoke", invokeTypes);
317        ilgen.Emit(OpCodes.Ldarg_0);
318        ilgen.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod"));
319        ilgen.Emit(OpCodes.Castclass, typeof(System.Reflection.MethodInfo));
320        ilgen.Emit(OpCodes.Ldloc, argValues);
321        ilgen.Emit(OpCodes.Ldarg_0);
322        ilgen.Emit(OpCodes.Ldloc, acbValue);
323        ilgen.Emit(OpCodes.Ldloc, objValue);
324        ilgen.Emit(OpCodes.Call, invokeMethod);
325        // BeginInvoke will leave IAsyncResult on stack - leave it there
326        // for return value from method being built
327        ilgen.Emit(OpCodes.Ret);
328      }
329    }
330
331    static void BuildEndMethods(TypeBuilder tb, ArrayList methods)
332    {
333      LocalBuilder retVal = null;
334      LocalBuilder tempRetVal = null;
335      foreach (MethodData mthdData in methods)
336      {
337        MethodInfo mi = mthdData.mi;
338        Type[] argTypes = new Type[] { typeof(System.IAsyncResult) };
339        MethodBuilder mthdBldr = tb.DefineMethod(mi.Name,
340          MethodAttributes.Public | MethodAttributes.Virtual,
341          mi.ReturnType, argTypes);
342        // start generating IL
343        ILGenerator ilgen = mthdBldr.GetILGenerator();
344        // if non-void return, declared locals for processing return value
345        if (typeof(void) != mi.ReturnType)
346        {
347          tempRetVal = ilgen.DeclareLocal(typeof(System.Object));
348          retVal = ilgen.DeclareLocal(mi.ReturnType);
349        }
350        // call EndInvoke on base class
351        Type[] invokeTypes
352          = new Type[] { typeof(System.IAsyncResult), typeof(System.Type) };
353        MethodInfo invokeMethod
354          = typeof(XmlRpcClientProtocol).GetMethod("EndInvoke", invokeTypes);
355        Type[] GetTypeTypes
356          = new Type[] { typeof(System.String) };
357        MethodInfo GetTypeMethod
358          = typeof(System.Type).GetMethod("GetType", GetTypeTypes);
359        ilgen.Emit(OpCodes.Ldarg_0);  // "this"
360        ilgen.Emit(OpCodes.Ldarg_1);  // IAsyncResult parameter
361        ilgen.Emit(OpCodes.Ldstr, mi.ReturnType.AssemblyQualifiedName);
362        ilgen.Emit(OpCodes.Call, GetTypeMethod);
363        ilgen.Emit(OpCodes.Call, invokeMethod);
364        //  if non-void return prepare return value otherwise pop to discard
365        if (typeof(void) != mi.ReturnType)
366        {
367          // if return value is null, don't cast it to required type
368          Label retIsNull = ilgen.DefineLabel();
369          ilgen.Emit(OpCodes.Stloc, tempRetVal);
370          ilgen.Emit(OpCodes.Ldloc, tempRetVal);
371          ilgen.Emit(OpCodes.Brfalse, retIsNull);
372          ilgen.Emit(OpCodes.Ldloc, tempRetVal);
373          if (true == mi.ReturnType.IsValueType)
374          {
375            ilgen.Emit(OpCodes.Unbox, mi.ReturnType);
376            ilgen.Emit(OpCodes.Ldobj, mi.ReturnType);
377          }
378          else
379          {
380            ilgen.Emit(OpCodes.Castclass, mi.ReturnType);
381          }
382          ilgen.Emit(OpCodes.Stloc, retVal);
383          ilgen.MarkLabel(retIsNull);
384          ilgen.Emit(OpCodes.Ldloc, retVal);
385        }
386        else
387        {
388          // void method so throw away result from EndInvoke
389          ilgen.Emit(OpCodes.Pop);
390        }
391        ilgen.Emit(OpCodes.Ret);
392      }
393    }
394
395    private static void BuildConstructor(
396      TypeBuilder typeBldr,
397      Type baseType,
398      string urlStr)
399    {
400      ConstructorBuilder ctorBldr = typeBldr.DefineConstructor(
401        MethodAttributes.Public | MethodAttributes.SpecialName |
402        MethodAttributes.RTSpecialName | MethodAttributes.HideBySig,
403        CallingConventions.Standard,
404        Type.EmptyTypes);
405      if (urlStr != null && urlStr.Length > 0)
406      {
407        Type urlAttr = typeof(XmlRpcUrlAttribute);
408        Type[] oneString = new Type[1] { typeof(string) };
409        ConstructorInfo ci = urlAttr.GetConstructor(oneString);
410        CustomAttributeBuilder cab =
411          new CustomAttributeBuilder(ci, new object[] { urlStr });
412        typeBldr.SetCustomAttribute(cab);
413      }
414      ILGenerator ilgen = ctorBldr.GetILGenerator();
415      //  Call the base constructor.
416      ilgen.Emit(OpCodes.Ldarg_0);
417      ConstructorInfo ctorInfo = baseType.GetConstructor(System.Type.EmptyTypes);
418      ilgen.Emit(OpCodes.Call, ctorInfo);
419      ilgen.Emit(OpCodes.Ret);
420    }
421
422    private static string GetXmlRpcUrl(Type itf)
423    {
424      Attribute attr = Attribute.GetCustomAttribute(itf,
425        typeof(XmlRpcUrlAttribute));
426      if (attr == null)
427        return null;
428      XmlRpcUrlAttribute xruAttr = attr as XmlRpcUrlAttribute;
429      string url = xruAttr.Uri;
430      return url;
431    }
432
433    /// <summary>
434    /// Type.GetMethods() does not return methods that a derived interface
435    /// inherits from its base interfaces; this method does.
436    /// </summary>
437    private static MethodInfo[] GetMethods(Type type)
438    {
439      MethodInfo[] methods = type.GetMethods();
440      if (!type.IsInterface)
441      {
442        return methods;
443      }
444
445      Type[] interfaces = type.GetInterfaces();
446      if (interfaces.Length == 0)
447      {
448        return methods;
449      }
450
451      ArrayList result = new ArrayList(methods);
452      foreach (Type itf in type.GetInterfaces())
453      {
454        result.AddRange(itf.GetMethods());
455      }
456      return (MethodInfo[])result.ToArray(typeof(MethodInfo));
457    }
458
459    private static ArrayList GetXmlRpcMethods(Type itf)
460    {
461      ArrayList ret = new ArrayList();
462      if (!itf.IsInterface)
463        throw new Exception("type not interface");
464      foreach (MethodInfo mi in GetMethods(itf))
465      {
466        string xmlRpcName = GetXmlRpcMethodName(mi);
467        if (xmlRpcName == null)
468          continue;
469        ParameterInfo[] pis = mi.GetParameters();
470        bool paramsMethod = pis.Length > 0 && Attribute.IsDefined(
471          pis[pis.Length - 1], typeof(ParamArrayAttribute));
472        ret.Add(new MethodData(mi, xmlRpcName, paramsMethod));
473      }
474      return ret;
475    }
476
477    private static string GetXmlRpcMethodName(MethodInfo mi)
478    {
479      Attribute attr = Attribute.GetCustomAttribute(mi,
480        typeof(XmlRpcMethodAttribute));
481      if (attr == null)
482        return null;
483      XmlRpcMethodAttribute xrmAttr = attr as XmlRpcMethodAttribute;
484      string rpcMethod = xrmAttr.Method;
485      if (rpcMethod == "")
486      {
487        rpcMethod = mi.Name;
488      }
489      return rpcMethod;
490    }
491
492    class MethodData
493    {
494      public MethodData(MethodInfo Mi, string XmlRpcName, bool ParamsMethod)
495      {
496        mi = Mi;
497        xmlRpcName = XmlRpcName;
498        paramsMethod = ParamsMethod;
499        returnType = null;
500      }
501      public MethodData(MethodInfo Mi, string XmlRpcName, bool ParamsMethod,
502        Type ReturnType)
503      {
504        mi = Mi;
505        xmlRpcName = XmlRpcName;
506        paramsMethod = ParamsMethod;
507        returnType = ReturnType;
508      }
509      public MethodInfo mi;
510      public string xmlRpcName;
511      public Type returnType;
512      public bool paramsMethod;
513    }
514
515    private static ArrayList GetXmlRpcBeginMethods(Type itf)
516    {
517      ArrayList ret = new ArrayList();
518      if (!itf.IsInterface)
519        throw new Exception("type not interface");
520      foreach (MethodInfo mi in itf.GetMethods())
521      {
522        Attribute attr = Attribute.GetCustomAttribute(mi,
523          typeof(XmlRpcBeginAttribute));
524        if (attr == null)
525          continue;
526        string rpcMethod = ((XmlRpcBeginAttribute)attr).Method;
527        if (rpcMethod == "")
528        {
529          if (!mi.Name.StartsWith("Begin") || mi.Name.Length <= 5)
530            throw new Exception(String.Format(
531              "method {0} has invalid signature for begin method",
532              mi.Name));
533          rpcMethod = mi.Name.Substring(5);
534        }
535        int paramCount = mi.GetParameters().Length;
536        int i;
537        for (i = 0; i < paramCount; i++)
538        {
539          Type paramType = mi.GetParameters()[0].ParameterType;
540          if (paramType == typeof(System.AsyncCallback))
541            break;
542        }
543        if (paramCount > 1)
544        {
545          if (i < paramCount - 2)
546            throw new Exception(String.Format(
547              "method {0} has invalid signature for begin method", mi.Name));
548          if (i == (paramCount - 2))
549          {
550            Type paramType = mi.GetParameters()[i + 1].ParameterType;
551            if (paramType != typeof(System.Object))
552              throw new Exception(String.Format(
553                "method {0} has invalid signature for begin method",
554                mi.Name));
555          }
556        }
557        ret.Add(new MethodData(mi, rpcMethod, false, null));
558      }
559      return ret;
560    }
561
562    private static ArrayList GetXmlRpcEndMethods(Type itf)
563    {
564      ArrayList ret = new ArrayList();
565      if (!itf.IsInterface)
566        throw new Exception("type not interface");
567      foreach (MethodInfo mi in itf.GetMethods())
568      {
569        Attribute attr = Attribute.GetCustomAttribute(mi,
570          typeof(XmlRpcEndAttribute));
571        if (attr == null)
572          continue;
573        ParameterInfo[] pis = mi.GetParameters();
574        if (pis.Length != 1)
575          throw new Exception(String.Format(
576            "method {0} has invalid signature for end method", mi.Name));
577        Type paramType = pis[0].ParameterType;
578        if (paramType != typeof(System.IAsyncResult))
579          throw new Exception(String.Format(
580            "method {0} has invalid signature for end method", mi.Name));
581        ret.Add(new MethodData(mi, "", false));
582      }
583      return ret;
584    }
585  }
586}
Note: See TracBrowser for help on using the repository browser.