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