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