[mono-api-info] Take return type into account when comparing methods.
[mono.git] / mcs / tools / corcompare / mono-api-info.cs
1 //
2 // mono-api-info.cs - Dumps public assembly information to an xml file.
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // Copyright (C) 2003-2008 Novell, Inc (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Globalization;
14 using System.Linq;
15 using System.Runtime.CompilerServices;
16 using System.Runtime.InteropServices;
17 using System.Security.Permissions;
18 using System.Text;
19 using System.Xml;
20
21 using Mono.Cecil;
22 using Mono.Cecil.Cil;
23 using System.IO;
24
25 namespace CorCompare
26 {
27         public class Driver
28         {
29                 public static int Main (string [] args)
30                 {
31                         bool showHelp = false;
32                         AbiMode = false;
33                         FollowForwarders = false;
34
35                         var acoll = new AssemblyCollection ();
36
37                         var options = new Mono.Options.OptionSet {
38                                 { "h|help", "Show this help", v => showHelp = true },
39                                 { "abi", _ => AbiMode = true},
40                                 { "f|follow-forwarders", _ => FollowForwarders = true },
41                                 { "d|search-directory=", v => TypeHelper.Resolver.AddSearchDirectory (v) },
42                         };
43
44                         var asms = options.Parse (args);
45
46                         if (showHelp || asms.Count == 0) {
47                                 Console.WriteLine (@"Usage: mono-api-info [options] <assemblies>");
48                                 Console.WriteLine ();
49                                 Console.WriteLine ("Available options:");
50                                 options.WriteOptionDescriptions (Console.Out);
51                                 Console.WriteLine ();
52                                 return showHelp? 0 :1;
53                         }
54
55                         string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
56                         string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
57                         TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"assembly\GAC\MSDATASRC\7.0.3300.0__b03f5f7f11d50a3a"));
58
59                         foreach (string arg in asms) {
60                                 acoll.Add (arg);
61
62                                 if (arg.Contains ("v3.0")) {
63                                         TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
64                                 } else if (arg.Contains ("v3.5")) {
65                                         TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
66                                         TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v3.0\Windows Communication Foundation"));
67                                 } else if (arg.Contains ("v4.0")) {
68                                         if (arg.Contains ("Silverlight")) {
69                                                 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (pf, @"Microsoft Silverlight\4.0.51204.0"));
70                                         } else {
71                                                 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319"));
72                                                 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319\WPF"));
73                                         }
74                                 } else {
75                                         TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg));
76                                 }
77                         }
78
79                         XmlDocument doc = new XmlDocument ();
80                         acoll.Document = doc;
81                         acoll.DoOutput ();
82
83                         var writer = new WellFormedXmlWriter (new XmlTextWriter (Console.Out) { Formatting = Formatting.Indented });
84                         XmlNode decl = doc.CreateXmlDeclaration ("1.0", "utf-8", null);
85                         doc.InsertBefore (decl, doc.DocumentElement);
86                         doc.WriteTo (writer);
87                         return 0;
88                 }
89
90                 internal static bool AbiMode { get; private set; }
91                 internal static bool FollowForwarders { get; private set; }
92         }
93
94         public class Utils {
95
96                 public static string CleanupTypeName (TypeReference type)
97                 {
98                         return CleanupTypeName (type.FullName);
99                 }
100
101                 public static string CleanupTypeName (string t)
102                 {
103                         return t.Replace ('<', '[').Replace ('>', ']').Replace ('/', '+');
104                 }
105         }
106
107         class AssemblyCollection
108         {
109                 XmlDocument document;
110                 List<AssemblyDefinition> assemblies = new List<AssemblyDefinition> ();
111
112                 public AssemblyCollection ()
113                 {
114                 }
115
116                 public bool Add (string name)
117                 {
118                         AssemblyDefinition ass = LoadAssembly (name);
119                         if (ass == null) {
120                                 Console.Error.WriteLine ("Cannot load assembly file " + name);
121                                 return false;
122                         }
123
124                         assemblies.Add (ass);
125                         return true;
126                 }
127
128                 public void DoOutput ()
129                 {
130                         if (document == null)
131                                 throw new InvalidOperationException ("Document not set");
132
133                         XmlNode nassemblies = document.CreateElement ("assemblies", null);
134                         document.AppendChild (nassemblies);
135                         foreach (AssemblyDefinition a in assemblies) {
136                                 AssemblyData data = new AssemblyData (document, nassemblies, a);
137                                 data.DoOutput ();
138                         }
139                 }
140
141                 public XmlDocument Document {
142                         set { document = value; }
143                 }
144
145                 AssemblyDefinition LoadAssembly (string assembly)
146                 {
147                         try {
148                                 if (File.Exists (assembly))
149                                         return TypeHelper.Resolver.ResolveFile (assembly);
150
151                                 return TypeHelper.Resolver.Resolve (assembly);
152                         } catch (Exception e) {
153                                 Console.WriteLine (e);
154                                 return null;
155                         }
156                 }
157         }
158
159         abstract class BaseData
160         {
161                 protected XmlDocument document;
162                 protected XmlNode parent;
163
164                 protected BaseData (XmlDocument doc, XmlNode parent)
165                 {
166                         this.document = doc;
167                         this.parent = parent;
168                 }
169
170                 public abstract void DoOutput ();
171
172                 protected void AddAttribute (XmlNode node, string name, string value)
173                 {
174                         XmlAttribute attr = document.CreateAttribute (name);
175                         attr.Value = value;
176                         node.Attributes.Append (attr);
177                 }
178         }
179
180         class TypeForwardedToData : BaseData
181         {
182                 AssemblyDefinition ass;
183
184                 public TypeForwardedToData (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
185                         : base (document, parent)
186                 {
187                         this.ass = ass;
188                 }
189
190                 public override void DoOutput ()
191                 {
192                         XmlNode natts = parent.SelectSingleNode("attributes");
193                         if (natts == null) {
194                                 natts = document.CreateElement ("attributes", null);
195                                 parent.AppendChild (natts);
196                         }
197
198                         foreach (ExportedType type in ass.MainModule.ExportedTypes) {
199
200                                 if (((uint)type.Attributes & 0x200000u) == 0)
201                                         continue;
202
203                                 XmlNode node = document.CreateElement ("attribute");
204                                 AddAttribute (node, "name", typeof (TypeForwardedToAttribute).FullName);
205                                 XmlNode properties = node.AppendChild (document.CreateElement ("properties"));
206                                 XmlNode property = properties.AppendChild (document.CreateElement ("property"));
207                                 AddAttribute (property, "name", "Destination");
208                                 AddAttribute (property, "value", Utils.CleanupTypeName (type.FullName));
209                                 natts.AppendChild (node);
210                         }
211                 }
212
213                 public static void OutputForwarders (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
214                 {
215                         TypeForwardedToData tftd = new TypeForwardedToData (document, parent, ass);
216                         tftd.DoOutput ();
217                 }
218         }
219
220         class AssemblyData : BaseData
221         {
222                 AssemblyDefinition ass;
223
224                 public AssemblyData (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
225                         : base (document, parent)
226                 {
227                         this.ass = ass;
228                 }
229
230                 public override void DoOutput ()
231                 {
232                         if (document == null)
233                                 throw new InvalidOperationException ("Document not set");
234
235                         XmlNode nassembly = document.CreateElement ("assembly", null);
236                         AssemblyNameDefinition aname = ass.Name;
237                         AddAttribute (nassembly, "name", aname.Name);
238                         AddAttribute (nassembly, "version", aname.Version.ToString ());
239                         parent.AppendChild (nassembly);
240
241                         if (!Driver.FollowForwarders) {
242                                 TypeForwardedToData.OutputForwarders (document, nassembly, ass);
243                         }
244
245                         AttributeData.OutputAttributes (document, nassembly, ass);
246
247                         var types = new List<TypeDefinition> ();
248                         if (ass.MainModule.Types != null) {
249                                 types.AddRange (ass.MainModule.Types);
250                         }
251
252                         if (Driver.FollowForwarders && ass.MainModule.ExportedTypes != null) {
253                                 foreach (var t in ass.MainModule.ExportedTypes) {
254                                         var forwarded = t.Resolve ();
255                                         if (forwarded == null) {
256                                                 throw new Exception ("Could not resolve forwarded type " + t.FullName + " in " + ass.Name);
257                                         }
258                                         types.Add (forwarded);
259                                 }
260                         }
261
262                         if (types.Count == 0) {
263                                 return;
264                         }
265
266                         types.Sort (TypeReferenceComparer.Default);
267
268                         XmlNode nss = document.CreateElement ("namespaces", null);
269                         nassembly.AppendChild (nss);
270
271                         string current_namespace = "$%&$&";
272                         XmlNode ns = null;
273                         XmlNode classes = null;
274                         foreach (TypeDefinition t in types) {
275                                 if (string.IsNullOrEmpty (t.Namespace))
276                                         continue;
277
278                                 if (!Driver.AbiMode && ((t.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public))
279                                         continue;
280
281                                 if (t.DeclaringType != null)
282                                         continue; // enforce !nested
283
284                                 if (t.Namespace != current_namespace) {
285                                         current_namespace = t.Namespace;
286                                         ns = document.CreateElement ("namespace", null);
287                                         AddAttribute (ns, "name", current_namespace);
288                                         nss.AppendChild (ns);
289                                         classes = document.CreateElement ("classes", null);
290                                         ns.AppendChild (classes);
291                                 }
292
293                                 TypeData bd = new TypeData (document, classes, t);
294                                 bd.DoOutput ();
295                         }
296                 }
297         }
298
299         abstract class MemberData : BaseData
300         {
301                 MemberReference [] members;
302
303                 public MemberData (XmlDocument document, XmlNode parent, MemberReference [] members)
304                         : base (document, parent)
305                 {
306                         this.members = members;
307                 }
308
309                 public override void DoOutput ()
310                 {
311                         XmlNode mclass = document.CreateElement (ParentTag, null);
312                         parent.AppendChild (mclass);
313
314                         foreach (MemberReference member in members) {
315                                 XmlNode mnode = document.CreateElement (Tag, null);
316                                 mclass.AppendChild (mnode);
317                                 AddAttribute (mnode, "name", GetName (member));
318                                 if (!NoMemberAttributes)
319                                         AddAttribute (mnode, "attrib", GetMemberAttributes (member));
320
321                                 AttributeData.OutputAttributes (document, mnode, (ICustomAttributeProvider) member);
322
323                                 AddExtraData (mnode, member);
324                         }
325                 }
326
327                 protected virtual void AddExtraData (XmlNode p, MemberReference memberDefenition)
328                 {
329                 }
330
331                 protected virtual string GetName (MemberReference memberDefenition)
332                 {
333                         return "NoNAME";
334                 }
335
336                 protected virtual string GetMemberAttributes (MemberReference memberDefenition)
337                 {
338                         return null;
339                 }
340
341                 public virtual bool NoMemberAttributes {
342                         get { return false; }
343                         set {}
344                 }
345
346                 public virtual string ParentTag {
347                         get { return "NoPARENTTAG"; }
348                 }
349
350                 public virtual string Tag {
351                         get { return "NoTAG"; }
352                 }
353
354                 public static void OutputGenericParameters (XmlDocument document, XmlNode nclass, IGenericParameterProvider provider)
355                 {
356                         if (provider.GenericParameters.Count == 0)
357                                 return;
358
359                         var gparameters = provider.GenericParameters;
360
361                         XmlElement ngeneric = document.CreateElement ("generic-parameters");
362                         nclass.AppendChild (ngeneric);
363
364                         foreach (GenericParameter gp in gparameters) {
365                                 XmlElement nparam = document.CreateElement ("generic-parameter");
366                                 nparam.SetAttribute ("name", gp.Name);
367                                 nparam.SetAttribute ("attributes", ((int) gp.Attributes).ToString ());
368
369                                 AttributeData.OutputAttributes (document, nparam, gp);
370
371                                 ngeneric.AppendChild (nparam);
372
373                                 var constraints = gp.Constraints;
374                                 if (constraints.Count == 0)
375                                         continue;
376
377                                 XmlElement nconstraint = document.CreateElement ("generic-parameter-constraints");
378
379                                 foreach (TypeReference constraint in constraints) {
380                                         XmlElement ncons = document.CreateElement ("generic-parameter-constraint");
381                                         ncons.SetAttribute ("name", Utils.CleanupTypeName (constraint));
382                                         nconstraint.AppendChild (ncons);
383                                 }
384
385                                 nparam.AppendChild (nconstraint);
386                         }
387                 }
388         }
389
390         class TypeData : MemberData
391         {
392                 TypeDefinition type;
393
394                 public TypeData (XmlDocument document, XmlNode parent, TypeDefinition type)
395                         : base (document, parent, null)
396                 {
397                         this.type = type;
398                 }
399                 public override void DoOutput ()
400                 {
401                         if (document == null)
402                                 throw new InvalidOperationException ("Document not set");
403
404                         XmlNode nclass = document.CreateElement ("class", null);
405                         AddAttribute (nclass, "name", type.Name);
406                         string classType = GetClassType (type);
407                         AddAttribute (nclass, "type", classType);
408
409                         if (type.BaseType != null)
410                                 AddAttribute (nclass, "base", Utils.CleanupTypeName (type.BaseType));
411
412                         if (type.IsSealed)
413                                 AddAttribute (nclass, "sealed", "true");
414
415                         if (type.IsAbstract)
416                                 AddAttribute (nclass, "abstract", "true");
417
418                         if ( (type.Attributes & TypeAttributes.Serializable) != 0 || type.IsEnum)
419                                 AddAttribute (nclass, "serializable", "true");
420
421                         string charSet = GetCharSet (type);
422                         AddAttribute (nclass, "charset", charSet);
423
424                         string layout = GetLayout (type);
425                         if (layout != null)
426                                 AddAttribute (nclass, "layout", layout);
427
428                         if (type.PackingSize >= 0) {
429                                 AddAttribute (nclass, "pack", type.PackingSize.ToString ());
430                         }
431
432                         if (type.ClassSize >= 0) {
433                                 AddAttribute (nclass, "size", type.ClassSize.ToString ());
434                         }
435
436                         parent.AppendChild (nclass);
437
438                         AttributeData.OutputAttributes (document, nclass, type);
439
440                         XmlNode ifaces = null;
441
442                         foreach (TypeReference iface in TypeHelper.GetInterfaces (type).OrderBy (s => s.FullName)) {
443                                 if (!TypeHelper.IsPublic (iface))
444                                         // we're only interested in public interfaces
445                                         continue;
446
447                                 if (ifaces == null) {
448                                         ifaces = document.CreateElement ("interfaces", null);
449                                         nclass.AppendChild (ifaces);
450                                 }
451
452                                 XmlNode iface_node = document.CreateElement ("interface", null);
453                                 AddAttribute (iface_node, "name", Utils.CleanupTypeName (iface));
454                                 ifaces.AppendChild (iface_node);
455                         }
456
457                         MemberData.OutputGenericParameters (document, nclass, type);
458
459                         ArrayList members = new ArrayList ();
460
461                         FieldDefinition [] fields = GetFields (type);
462                         if (fields.Length > 0) {
463                                 Array.Sort (fields, MemberReferenceComparer.Default);
464                                 FieldData fd = new FieldData (document, nclass, fields);
465                                 members.Add (fd);
466                         }
467
468                         if (type.IsEnum) {
469                                 var value_type = GetEnumValueField (type);
470                                 if (value_type == null)
471                                         throw new NotSupportedException ();
472
473                                 AddAttribute (nclass, "enumtype", Utils.CleanupTypeName (value_type.FieldType));
474                         }
475
476                         if (!Driver.AbiMode) {
477
478                                 MethodDefinition [] ctors = GetConstructors (type);
479                                 if (ctors.Length > 0) {
480                                         Array.Sort (ctors, MethodDefinitionComparer.Default);
481                                         members.Add (new ConstructorData (document, nclass, ctors));
482                                 }
483
484                                 PropertyDefinition[] properties = GetProperties (type);
485                                 if (properties.Length > 0) {
486                                         Array.Sort (properties, PropertyDefinitionComparer.Default);
487                                         members.Add (new PropertyData (document, nclass, properties));
488                                 }
489
490                                 EventDefinition [] events = GetEvents (type);
491                                 if (events.Length > 0) {
492                                         Array.Sort (events, MemberReferenceComparer.Default);
493                                         members.Add (new EventData (document, nclass, events));
494                                 }
495
496                                 MethodDefinition [] methods = GetMethods (type);
497                                 if (methods.Length > 0) {
498                                         Array.Sort (methods, MethodDefinitionComparer.Default);
499                                         members.Add (new MethodData (document, nclass, methods));
500                                 }
501                         }
502
503                         foreach (MemberData md in members)
504                                 md.DoOutput ();
505
506                         var nested = type.NestedTypes;
507                         //remove non public(familiy) and nested in second degree
508                         for (int i = nested.Count - 1; i >= 0; i--) {
509                                 TypeDefinition t = nested [i];
510                                 if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic ||
511                                         (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily ||
512                                         (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) {
513                                         // public
514                                         if (t.DeclaringType == type)
515                                                 continue; // not nested of nested
516                                 }
517
518                                 nested.RemoveAt (i);
519                         }
520
521                         if (nested.Count > 0) {
522                                 var nestedArray = nested.ToArray ();
523                                 Array.Sort (nestedArray, TypeReferenceComparer.Default);
524
525                                 XmlNode classes = document.CreateElement ("classes", null);
526                                 nclass.AppendChild (classes);
527                                 foreach (TypeDefinition t in nestedArray) {
528                                         TypeData td = new TypeData (document, classes, t);
529                                         td.DoOutput ();
530                                 }
531                         }
532                 }
533
534                 static FieldReference GetEnumValueField (TypeDefinition type)
535                 {
536                         foreach (FieldDefinition field in type.Fields)
537                                 if (field.IsSpecialName && field.Name == "value__")
538                                         return field;
539
540                         return null;
541                 }
542
543                 protected override string GetMemberAttributes (MemberReference member)
544                 {
545                         if (member != type)
546                                 throw new InvalidOperationException ("odd");
547
548                         return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
549                 }
550
551                 public static bool MustDocumentMethod (MethodDefinition method) {
552                         // All other methods
553                         MethodAttributes maskedAccess = method.Attributes & MethodAttributes.MemberAccessMask;
554                         return maskedAccess == MethodAttributes.Public
555                                 || maskedAccess == MethodAttributes.Family
556                                 || maskedAccess == MethodAttributes.FamORAssem;
557                 }
558
559                 static string GetClassType (TypeDefinition t)
560                 {
561                         if (t.IsEnum)
562                                 return "enum";
563
564                         if (t.IsValueType)
565                                 return "struct";
566
567                         if (t.IsInterface)
568                                 return "interface";
569
570                         if (TypeHelper.IsDelegate(t))
571                                 return "delegate";
572
573                         if (t.IsPointer)
574                                 return "pointer";
575
576                         return "class";
577                 }
578
579                 static string GetCharSet (TypeDefinition type)
580                 {
581                         TypeAttributes maskedStringFormat = type.Attributes & TypeAttributes.StringFormatMask;
582                         if (maskedStringFormat == TypeAttributes.AnsiClass)
583                                 return CharSet.Ansi.ToString ();
584
585                         if (maskedStringFormat == TypeAttributes.AutoClass)
586                                 return CharSet.Auto.ToString ();
587
588                         if (maskedStringFormat == TypeAttributes.UnicodeClass)
589                                 return CharSet.Unicode.ToString ();
590
591                         return CharSet.None.ToString ();
592                 }
593
594                 static string GetLayout (TypeDefinition type)
595                 {
596                         TypeAttributes maskedLayout = type.Attributes & TypeAttributes.LayoutMask;
597                         if (maskedLayout == TypeAttributes.AutoLayout)
598                                 return LayoutKind.Auto.ToString ();
599
600                         if (maskedLayout == TypeAttributes.ExplicitLayout)
601                                 return LayoutKind.Explicit.ToString ();
602
603                         if (maskedLayout == TypeAttributes.SequentialLayout)
604                                 return LayoutKind.Sequential.ToString ();
605
606                         return null;
607                 }
608
609                 FieldDefinition [] GetFields (TypeDefinition type) {
610                         ArrayList list = new ArrayList ();
611
612                         var fields = type.Fields;
613                         foreach (FieldDefinition field in fields) {
614                                 if (field.IsSpecialName)
615                                         continue;
616
617                                 if (Driver.AbiMode && field.IsStatic)
618                                         continue;
619
620                                 // we're only interested in public or protected members
621                                 FieldAttributes maskedVisibility = (field.Attributes & FieldAttributes.FieldAccessMask);
622                                 if (Driver.AbiMode && !field.IsNotSerialized) {
623                                         list.Add (field);
624                                 } else {
625                                         if (maskedVisibility == FieldAttributes.Public
626                                                 || maskedVisibility == FieldAttributes.Family
627                                                 || maskedVisibility == FieldAttributes.FamORAssem) {
628                                                 list.Add (field);
629                                         }
630                                 }
631                         }
632
633                         return (FieldDefinition []) list.ToArray (typeof (FieldDefinition));
634                 }
635
636
637                 internal static PropertyDefinition [] GetProperties (TypeDefinition type) {
638                         ArrayList list = new ArrayList ();
639
640                         var properties = type.Properties;//type.GetProperties (flags);
641                         foreach (PropertyDefinition property in properties) {
642                                 MethodDefinition getMethod = property.GetMethod;
643                                 MethodDefinition setMethod = property.SetMethod;
644
645                                 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
646                                 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
647
648                                 // if neither the getter or setter should be documented, then
649                                 // skip the property
650                                 if (hasGetter || hasSetter) {
651                                         list.Add (property);
652                                 }
653                         }
654
655                         return (PropertyDefinition []) list.ToArray (typeof (PropertyDefinition));
656                 }
657
658                 private MethodDefinition[] GetMethods (TypeDefinition type)
659                 {
660                         ArrayList list = new ArrayList ();
661
662                         var methods = type.Methods;//type.GetMethods (flags);
663                         foreach (MethodDefinition method in methods) {
664                                 if (method.IsSpecialName && !method.Name.StartsWith ("op_"))
665                                         continue;
666
667                                 // we're only interested in public or protected members
668                                 if (!MustDocumentMethod(method))
669                                         continue;
670
671                                 if (IsFinalizer (method)) {
672                                         string name = method.DeclaringType.Name;
673                                         int arity = name.IndexOf ('`');
674                                         if (arity > 0)
675                                                 name = name.Substring (0, arity);
676
677                                         method.Name = "~" + name;
678                                 }
679
680                                 list.Add (method);
681                         }
682
683                         return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
684                 }
685
686                 static bool IsFinalizer (MethodDefinition method)
687                 {
688                         if (method.Name != "Finalize")
689                                 return false;
690
691                         if (!method.IsVirtual)
692                                 return false;
693
694                         if (method.Parameters.Count != 0)
695                                 return false;
696
697                         return true;
698                 }
699
700                 private MethodDefinition [] GetConstructors (TypeDefinition type)
701                 {
702                         ArrayList list = new ArrayList ();
703
704                         var ctors = type.Methods.Where (m => m.IsConstructor);//type.GetConstructors (flags);
705                         foreach (MethodDefinition constructor in ctors) {
706                                 // we're only interested in public or protected members
707                                 if (!MustDocumentMethod(constructor))
708                                         continue;
709
710                                 list.Add (constructor);
711                         }
712
713                         return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
714                 }
715
716                 private EventDefinition[] GetEvents (TypeDefinition type)
717                 {
718                         ArrayList list = new ArrayList ();
719
720                         var events = type.Events;//type.GetEvents (flags);
721                         foreach (EventDefinition eventDef in events) {
722                                 MethodDefinition addMethod = eventDef.AddMethod;//eventInfo.GetAddMethod (true);
723
724                                 if (addMethod == null || !MustDocumentMethod (addMethod))
725                                         continue;
726
727                                 list.Add (eventDef);
728                         }
729
730                         return (EventDefinition []) list.ToArray (typeof (EventDefinition));
731                 }
732         }
733
734         class FieldData : MemberData
735         {
736                 public FieldData (XmlDocument document, XmlNode parent, FieldDefinition [] members)
737                         : base (document, parent, members)
738                 {
739                 }
740
741                 protected override string GetName (MemberReference memberDefenition)
742                 {
743                         FieldDefinition field = (FieldDefinition) memberDefenition;
744                         return field.Name;
745                 }
746
747                 protected override string GetMemberAttributes (MemberReference memberDefenition)
748                 {
749                         FieldDefinition field = (FieldDefinition) memberDefenition;
750                         return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
751                 }
752
753                 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
754                 {
755                         base.AddExtraData (p, memberDefenition);
756                         FieldDefinition field = (FieldDefinition) memberDefenition;
757                         AddAttribute (p, "fieldtype", Utils.CleanupTypeName (field.FieldType));
758
759                         if (field.IsLiteral) {
760                                 object value = field.Constant;//object value = field.GetValue (null);
761                                 string stringValue = null;
762                                 //if (value is Enum) {
763                                 //    // FIXME: when Mono bug #60090 has been
764                                 //    // fixed, we should just be able to use
765                                 //    // Convert.ToString
766                                 //    stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
767                                 //}
768                                 //else {
769                                         stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
770                                 //}
771
772                                 if (stringValue != null)
773                                         AddAttribute (p, "value", stringValue);
774                         }
775                 }
776
777                 public override string ParentTag {
778                         get { return "fields"; }
779                 }
780
781                 public override string Tag {
782                         get { return "field"; }
783                 }
784         }
785
786         class PropertyData : MemberData
787         {
788                 public PropertyData (XmlDocument document, XmlNode parent, PropertyDefinition [] members)
789                         : base (document, parent, members)
790                 {
791                 }
792
793                 protected override string GetName (MemberReference memberDefenition)
794                 {
795                         PropertyDefinition prop = (PropertyDefinition) memberDefenition;
796                         return prop.Name;
797                 }
798
799                 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
800                 {
801                         base.AddExtraData (p, memberDefenition);
802                         PropertyDefinition prop = (PropertyDefinition) memberDefenition;
803                         AddAttribute (p, "ptype", Utils.CleanupTypeName (prop.PropertyType));
804                         MethodDefinition _get = prop.GetMethod;
805                         MethodDefinition _set = prop.SetMethod;
806                         bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
807                         bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
808                         MethodDefinition [] methods;
809
810                         if (haveGet && haveSet) {
811                                 methods = new MethodDefinition [] { _get, _set };
812                         } else if (haveGet) {
813                                 methods = new MethodDefinition [] { _get };
814                         } else if (haveSet) {
815                                 methods = new MethodDefinition [] { _set };
816                         } else {
817                                 //odd
818                                 return;
819                         }
820
821                         if (haveGet || _set.Parameters.Count > 1) {
822                                 string parms = Parameters.GetSignature (methods [0].Parameters);
823                                 if (!string.IsNullOrEmpty (parms))
824                                         AddAttribute (p, "params", parms);
825                         }
826
827                         MethodData data = new MethodData (document, p, methods);
828                         //data.NoMemberAttributes = true;
829                         data.DoOutput ();
830                 }
831
832                 protected override string GetMemberAttributes (MemberReference memberDefenition)
833                 {
834                         PropertyDefinition prop = (PropertyDefinition) memberDefenition;
835                         return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
836                 }
837
838                 public override string ParentTag {
839                         get { return "properties"; }
840                 }
841
842                 public override string Tag {
843                         get { return "property"; }
844                 }
845         }
846
847         class EventData : MemberData
848         {
849                 public EventData (XmlDocument document, XmlNode parent, EventDefinition [] members)
850                         : base (document, parent, members)
851                 {
852                 }
853
854                 protected override string GetName (MemberReference memberDefenition)
855                 {
856                         EventDefinition evt = (EventDefinition) memberDefenition;
857                         return evt.Name;
858                 }
859
860                 protected override string GetMemberAttributes (MemberReference memberDefenition)
861                 {
862                         EventDefinition evt = (EventDefinition) memberDefenition;
863                         return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
864                 }
865
866                 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
867                 {
868                         base.AddExtraData (p, memberDefenition);
869                         EventDefinition evt = (EventDefinition) memberDefenition;
870                         AddAttribute (p, "eventtype", Utils.CleanupTypeName (evt.EventType));
871                 }
872
873                 public override string ParentTag {
874                         get { return "events"; }
875                 }
876
877                 public override string Tag {
878                         get { return "event"; }
879                 }
880         }
881
882         class MethodData : MemberData
883         {
884                 bool noAtts;
885
886                 public MethodData (XmlDocument document, XmlNode parent, MethodDefinition [] members)
887                         : base (document, parent, members)
888                 {
889                 }
890
891                 protected override string GetName (MemberReference memberDefenition)
892                 {
893                         MethodDefinition method = (MethodDefinition) memberDefenition;
894                         string name = method.Name;
895                         string parms = Parameters.GetSignature (method.Parameters);
896
897                         return string.Format ("{0}({1})", name, parms);
898                 }
899
900                 protected override string GetMemberAttributes (MemberReference memberDefenition)
901                 {
902                         MethodDefinition method = (MethodDefinition) memberDefenition;
903                         return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture);
904                 }
905
906                 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
907                 {
908                         base.AddExtraData (p, memberDefenition);
909
910                         if (!(memberDefenition is MethodDefinition))
911                                 return;
912
913                         MethodDefinition mbase = (MethodDefinition) memberDefenition;
914
915                         ParameterData parms = new ParameterData (document, p, mbase.Parameters);
916                         parms.DoOutput ();
917
918                         if (mbase.IsAbstract)
919                                 AddAttribute (p, "abstract", "true");
920                         if (mbase.IsVirtual)
921                                 AddAttribute (p, "virtual", "true");
922                         if (mbase.IsFinal && mbase.IsVirtual && mbase.IsReuseSlot)
923                                 AddAttribute (p, "sealed", "true");
924                         if (mbase.IsStatic)
925                                 AddAttribute (p, "static", "true");
926
927                         string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType);
928                         if (rettype != "System.Void" || !mbase.IsConstructor)
929                                 AddAttribute (p, "returntype", (rettype));
930
931                         AttributeData.OutputAttributes (document, p, mbase.MethodReturnType);
932
933                         MemberData.OutputGenericParameters (document, p, mbase);
934                 }
935
936                 public override bool NoMemberAttributes {
937                         get { return noAtts; }
938                         set { noAtts = value; }
939                 }
940
941                 public override string ParentTag {
942                         get { return "methods"; }
943                 }
944
945                 public override string Tag {
946                         get { return "method"; }
947                 }
948         }
949
950         class ConstructorData : MethodData
951         {
952                 public ConstructorData (XmlDocument document, XmlNode parent, MethodDefinition [] members)
953                         : base (document, parent, members)
954                 {
955                 }
956
957                 public override string ParentTag {
958                         get { return "constructors"; }
959                 }
960
961                 public override string Tag {
962                         get { return "constructor"; }
963                 }
964         }
965
966         class ParameterData : BaseData
967         {
968                 private IList<ParameterDefinition> parameters;
969
970                 public ParameterData (XmlDocument document, XmlNode parent, IList<ParameterDefinition> parameters)
971                         : base (document, parent)
972                 {
973                         this.parameters = parameters;
974                 }
975
976                 public override void DoOutput ()
977                 {
978                         XmlNode parametersNode = document.CreateElement ("parameters");
979                         parent.AppendChild (parametersNode);
980
981                         foreach (ParameterDefinition parameter in parameters) {
982                                 XmlNode paramNode = document.CreateElement ("parameter");
983                                 parametersNode.AppendChild (paramNode);
984                                 AddAttribute (paramNode, "name", parameter.Name);
985                                 AddAttribute (paramNode, "position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture));
986                                 AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString());
987
988                                 string direction = "in";
989
990                                 if (parameter.ParameterType is ByReferenceType)
991                                         direction = parameter.IsOut ? "out" : "ref";
992
993                                 TypeReference t = parameter.ParameterType;
994                                 AddAttribute (paramNode, "type", Utils.CleanupTypeName (t));
995
996                                 if (parameter.IsOptional) {
997                                         AddAttribute (paramNode, "optional", "true");
998                                         if (parameter.HasConstant)
999                                                 AddAttribute (paramNode, "defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ());
1000                                 }
1001
1002                                 if (direction != "in")
1003                                         AddAttribute (paramNode, "direction", direction);
1004
1005                                 AttributeData.OutputAttributes (document, paramNode, parameter);
1006                         }
1007                 }
1008         }
1009
1010         class AttributeData : BaseData
1011         {
1012                 IList<CustomAttribute> atts;
1013
1014                 AttributeData (XmlDocument doc, XmlNode parent, IList<CustomAttribute> attributes)
1015                         : base (doc, parent)
1016                 {
1017                         atts = attributes;
1018                 }
1019
1020                 public override void DoOutput ()
1021                 {
1022                         if (document == null)
1023                                 throw new InvalidOperationException ("Document not set");
1024
1025                         if (atts == null || atts.Count == 0)
1026                                 return;
1027
1028                         XmlNode natts = parent.SelectSingleNode("attributes");
1029                         if (natts == null) {
1030                                 natts = document.CreateElement ("attributes", null);
1031                                 parent.AppendChild (natts);
1032                         }
1033
1034                         foreach (var att in atts.OrderBy ((a) => a.Constructor.DeclaringType.FullName)) {
1035                                 string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType);
1036                                 if (SkipAttribute (att))
1037                                         continue;
1038
1039                                 XmlNode node = document.CreateElement ("attribute");
1040                                 AddAttribute (node, "name", attName);
1041
1042                                 XmlNode properties = null;
1043
1044                                 Dictionary<string, object> attribute_mapping = CreateAttributeMapping (att);
1045
1046                                 foreach (string name in attribute_mapping.Keys) {
1047                                         if (name == "TypeId")
1048                                                 continue;
1049
1050                                         if (properties == null) {
1051                                                 properties = node.AppendChild (document.CreateElement ("properties"));
1052                                         }
1053
1054                                         object o = attribute_mapping [name];
1055
1056                                         XmlNode n = properties.AppendChild (document.CreateElement ("property"));
1057                                         AddAttribute (n, "name", name);
1058
1059                                         if (o == null) {
1060                                                 AddAttribute (n, "value", "null");
1061                                                 continue;
1062                                         }
1063                                         
1064                                         string value = o.ToString ();
1065                                         if (attName.EndsWith ("GuidAttribute"))
1066                                                 value = value.ToUpper ();
1067                                         AddAttribute (n, "value", value);
1068                                 }
1069
1070                                 natts.AppendChild (node);
1071                         }
1072                 }
1073
1074                 static Dictionary<string, object> CreateAttributeMapping (CustomAttribute attribute)
1075                 {
1076                         var mapping = new Dictionary<string, object> ();
1077
1078                         PopulateMapping (mapping, attribute);
1079
1080                         var constructor = attribute.Constructor.Resolve ();
1081                         if (constructor == null || !constructor.HasParameters)
1082                                 return mapping;
1083
1084                         PopulateMapping (mapping, constructor, attribute);
1085
1086                         return mapping;
1087                 }
1088
1089                 static void PopulateMapping (Dictionary<string, object> mapping, CustomAttribute attribute)
1090                 {
1091                         if (!attribute.HasProperties)
1092                                 return;
1093                         
1094                         foreach (var named_argument in attribute.Properties) {
1095                                 var name = named_argument.Name;
1096                                 var arg = named_argument.Argument;
1097
1098                                 if (arg.Value is CustomAttributeArgument)
1099                                         arg = (CustomAttributeArgument) arg.Value;
1100
1101                                 mapping.Add (name, GetArgumentValue (arg.Type, arg.Value));
1102                         }
1103                 }
1104
1105                 static Dictionary<FieldReference, int> CreateArgumentFieldMapping (MethodDefinition constructor)
1106                 {
1107                         Dictionary<FieldReference, int> field_mapping = new Dictionary<FieldReference, int> ();
1108
1109                         int? argument = null;
1110
1111                         foreach (Instruction instruction in constructor.Body.Instructions) {
1112                                 switch (instruction.OpCode.Code) {
1113                                 case Code.Ldarg_1:
1114                                         argument = 1;
1115                                         break;
1116                                 case Code.Ldarg_2:
1117                                         argument = 2;
1118                                         break;
1119                                 case Code.Ldarg_3:
1120                                         argument = 3;
1121                                         break;
1122                                 case Code.Ldarg:
1123                                 case Code.Ldarg_S:
1124                                         argument = ((ParameterDefinition) instruction.Operand).Index + 1;
1125                                         break;
1126
1127                                 case Code.Stfld:
1128                                         FieldReference field = (FieldReference) instruction.Operand;
1129                                         if (field.DeclaringType.FullName != constructor.DeclaringType.FullName)
1130                                                 continue;
1131
1132                                         if (!argument.HasValue)
1133                                                 break;
1134
1135                                         if (!field_mapping.ContainsKey (field))
1136                                                 field_mapping.Add (field, (int) argument - 1);
1137
1138                                         argument = null;
1139                                         break;
1140                                 }
1141                         }
1142
1143                         return field_mapping;
1144                 }
1145
1146                 static Dictionary<PropertyDefinition, FieldReference> CreatePropertyFieldMapping (TypeDefinition type)
1147                 {
1148                         Dictionary<PropertyDefinition, FieldReference> property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
1149
1150                         foreach (PropertyDefinition property in type.Properties) {
1151                                 if (property.GetMethod == null)
1152                                         continue;
1153                                 if (!property.GetMethod.HasBody)
1154                                         continue;
1155
1156                                 foreach (Instruction instruction in property.GetMethod.Body.Instructions) {
1157                                         if (instruction.OpCode.Code != Code.Ldfld)
1158                                                 continue;
1159
1160                                         FieldReference field = (FieldReference) instruction.Operand;
1161                                         if (field.DeclaringType.FullName != type.FullName)
1162                                                 continue;
1163
1164                                         property_mapping.Add (property, field);
1165                                         break;
1166                                 }
1167                         }
1168
1169                         return property_mapping;
1170                 }
1171
1172                 static void PopulateMapping (Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
1173                 {
1174                         if (!constructor.HasBody)
1175                                 return;
1176
1177                         // Custom handling for attributes with arguments which cannot be easily extracted
1178                         var ca = attribute.ConstructorArguments;
1179                         switch (constructor.DeclaringType.FullName) {
1180                         case "System.Runtime.CompilerServices.DecimalConstantAttribute":
1181                                 var dca = constructor.Parameters[2].ParameterType == constructor.Module.TypeSystem.Int32 ?
1182                                         new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (int) ca[2].Value, (int) ca[3].Value, (int) ca[4].Value) :
1183                                         new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (uint) ca[2].Value, (uint) ca[3].Value, (uint) ca[4].Value);
1184
1185                                 mapping.Add ("Value", dca.Value);
1186                                 return;
1187                         case "System.ComponentModel.BindableAttribute":
1188                                 if (ca.Count != 1)
1189                                         break;
1190
1191                                 if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) {
1192                                         mapping.Add ("Bindable", ca[0].Value);
1193                                 } else {
1194                                         throw new NotImplementedException ();
1195                                 }
1196
1197                                 return;
1198                         }
1199
1200                         var field_mapping = CreateArgumentFieldMapping (constructor);
1201                         var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
1202
1203                         foreach (var pair in property_mapping) {
1204                                 int argument;
1205                                 if (!field_mapping.TryGetValue (pair.Value, out argument))
1206                                         continue;
1207
1208                                 var ca_arg = ca [argument];
1209                                 if (ca_arg.Value is CustomAttributeArgument)
1210                                         ca_arg = (CustomAttributeArgument) ca_arg.Value;
1211
1212                                 mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value));
1213                         }
1214                 }
1215
1216                 static object GetArgumentValue (TypeReference reference, object value)
1217                 {
1218                         var type = reference.Resolve ();
1219                         if (type == null)
1220                                 return value;
1221
1222                         if (type.IsEnum) {
1223                                 if (IsFlaggedEnum (type))
1224                                         return GetFlaggedEnumValue (type, value);
1225
1226                                 return GetEnumValue (type, value);
1227                         }
1228
1229                         return value;
1230                 }
1231
1232                 static bool IsFlaggedEnum (TypeDefinition type)
1233                 {
1234                         if (!type.IsEnum)
1235                                 return false;
1236
1237                         if (!type.HasCustomAttributes)
1238                                 return false;
1239
1240                         foreach (CustomAttribute attribute in type.CustomAttributes)
1241                                 if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute")
1242                                         return true;
1243
1244                         return false;
1245                 }
1246
1247                 static object GetFlaggedEnumValue (TypeDefinition type, object value)
1248                 {
1249                         if (value is ulong)
1250                                 return GetFlaggedEnumValue (type, (ulong)value);
1251
1252                         long flags = Convert.ToInt64 (value);
1253                         var signature = new StringBuilder ();
1254
1255                         for (int i = type.Fields.Count - 1; i >= 0; i--) {
1256                                 FieldDefinition field = type.Fields [i];
1257
1258                                 if (!field.HasConstant)
1259                                         continue;
1260
1261                                 long flag = Convert.ToInt64 (field.Constant);
1262
1263                                 if (flag == 0)
1264                                         continue;
1265
1266                                 if ((flags & flag) == flag) {
1267                                         if (signature.Length != 0)
1268                                                 signature.Append (", ");
1269
1270                                         signature.Append (field.Name);
1271                                         flags -= flag;
1272                                 }
1273                         }
1274
1275                         return signature.ToString ();
1276                 }
1277
1278                 static object GetFlaggedEnumValue (TypeDefinition type, ulong flags)
1279                 {
1280                         var signature = new StringBuilder ();
1281
1282                         for (int i = type.Fields.Count - 1; i >= 0; i--) {
1283                                 FieldDefinition field = type.Fields [i];
1284
1285                                 if (!field.HasConstant)
1286                                         continue;
1287
1288                                 ulong flag = Convert.ToUInt64 (field.Constant);
1289
1290                                 if (flag == 0)
1291                                         continue;
1292
1293                                 if ((flags & flag) == flag) {
1294                                         if (signature.Length != 0)
1295                                                 signature.Append (", ");
1296
1297                                         signature.Append (field.Name);
1298                                         flags -= flag;
1299                                 }
1300                         }
1301
1302                         return signature.ToString ();
1303                 }
1304
1305                 static object GetEnumValue (TypeDefinition type, object value)
1306                 {
1307                         foreach (FieldDefinition field in type.Fields) {
1308                                 if (!field.HasConstant)
1309                                         continue;
1310
1311                                 if (Comparer.Default.Compare (field.Constant, value) == 0)
1312                                         return field.Name;
1313                         }
1314
1315                         return value;
1316                 }
1317
1318                 static bool SkipAttribute (CustomAttribute attribute)
1319                 {
1320                         var type_name = Utils.CleanupTypeName (attribute.Constructor.DeclaringType);
1321
1322                         return !TypeHelper.IsPublic (attribute)
1323                                 || type_name.EndsWith ("TODOAttribute");
1324                 }
1325
1326                 public static void OutputAttributes (XmlDocument doc, XmlNode parent, ICustomAttributeProvider provider)
1327                 {
1328                         if (!provider.HasCustomAttributes)
1329                                 return;
1330                         
1331                         AttributeData ad = new AttributeData (doc, parent, provider.CustomAttributes);
1332                         ad.DoOutput ();
1333                 }
1334         }
1335
1336         static class Parameters {
1337
1338                 public static string GetSignature (IList<ParameterDefinition> infos)
1339                 {
1340                         if (infos == null || infos.Count == 0)
1341                                 return string.Empty;
1342
1343                         var signature = new StringBuilder ();
1344                         for (int i = 0; i < infos.Count; i++) {
1345
1346                                 if (i > 0)
1347                                         signature.Append (", ");
1348
1349                                 ParameterDefinition info = infos [i];
1350
1351                                 string modifier;
1352                                 if ((info.Attributes & ParameterAttributes.In) != 0)
1353                                         modifier = "in";
1354                                 else if ((info.Attributes & ParameterAttributes.Out) != 0)
1355                                         modifier = "out";
1356                                 else
1357                                         modifier = string.Empty;
1358
1359                                 if (modifier.Length > 0) {
1360                                         signature.Append (modifier);
1361                                         signature.Append (" ");
1362                                 }
1363
1364                                 signature.Append (Utils.CleanupTypeName (info.ParameterType));
1365                         }
1366
1367                         return signature.ToString ();
1368                 }
1369
1370         }
1371
1372         class TypeReferenceComparer : IComparer<TypeReference>
1373         {
1374                 public static TypeReferenceComparer Default = new TypeReferenceComparer ();
1375
1376                 public int Compare (TypeReference a, TypeReference b)
1377                 {
1378                         int result = String.Compare (a.Namespace, b.Namespace, StringComparison.Ordinal);
1379                         if (result != 0)
1380                                 return result;
1381
1382                         return String.Compare (a.Name, b.Name, StringComparison.Ordinal);
1383                 }
1384         }
1385
1386         class MemberReferenceComparer : IComparer
1387         {
1388                 public static MemberReferenceComparer Default = new MemberReferenceComparer ();
1389
1390                 public int Compare (object a, object b)
1391                 {
1392                         MemberReference ma = (MemberReference) a;
1393                         MemberReference mb = (MemberReference) b;
1394                         return String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1395                 }
1396         }
1397
1398         class PropertyDefinitionComparer : IComparer<PropertyDefinition>
1399         {
1400                 public static PropertyDefinitionComparer Default = new PropertyDefinitionComparer ();
1401
1402                 public int Compare (PropertyDefinition ma, PropertyDefinition mb)
1403                 {
1404                         int res = String.Compare (ma.Name, mb.Name);
1405                         if (res != 0)
1406                                 return res;
1407
1408                         if (!ma.HasParameters && !mb.HasParameters)
1409                                 return 0;
1410
1411                         if (!ma.HasParameters)
1412                                 return -1;
1413
1414                         if (!mb.HasParameters)
1415                                 return 1;
1416
1417                         return MethodDefinitionComparer.Compare (ma.Parameters, mb.Parameters);
1418                 }
1419         }
1420
1421         class MethodDefinitionComparer : IComparer
1422         {
1423                 public static MethodDefinitionComparer Default = new MethodDefinitionComparer ();
1424
1425                 public int Compare (object a, object b)
1426                 {
1427                         MethodDefinition ma = (MethodDefinition) a;
1428                         MethodDefinition mb = (MethodDefinition) b;
1429                         int res = String.Compare (ma.Name, mb.Name);
1430                         if (res != 0)
1431                                 return res;
1432
1433                         if (!ma.HasParameters && !mb.HasParameters)
1434                                 return 0;
1435
1436                         if (!ma.HasParameters)
1437                                 return -1;
1438
1439                         if (!mb.HasParameters)
1440                                 return 1;
1441
1442                         res = Compare (ma.Parameters, mb.Parameters);
1443                         if (res != 0)
1444                                 return res;
1445
1446                         // operators can differ by only return type
1447                         return string.CompareOrdinal (ma.ReturnType.FullName, mb.ReturnType.FullName);
1448                 }
1449
1450                 public static int Compare (IList<ParameterDefinition> pia, IList<ParameterDefinition> pib)
1451                 {
1452                         var res = pia.Count - pib.Count;
1453                         if (res != 0)
1454                                 return res;
1455
1456                         string siga = Parameters.GetSignature (pia);
1457                         string sigb = Parameters.GetSignature (pib);
1458                         return String.Compare (siga, sigb);
1459                 }
1460         }
1461 }
1462