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