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