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