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