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