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