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