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