Add regression test for #626441.
[mono-project.git] / mcs / tools / corcompare / mono-api-info.cs
blob203875b15ccf4701050a53f44fd36d1bffcf94f3
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 //
10 using System;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Globalization;
14 using System.Runtime.CompilerServices;
15 using System.Runtime.InteropServices;
16 using System.Security.Permissions;
17 using System.Text;
18 using System.Xml;
20 using Mono.Cecil;
21 using Mono.Cecil.Cil;
23 namespace CorCompare
25 public class Driver
27 public static int Main (string [] args)
29 if (args.Length == 0)
30 return 1;
32 AssemblyCollection acoll = new AssemblyCollection ();
34 foreach (string fullName in args) {
35 acoll.Add (fullName);
38 XmlDocument doc = new XmlDocument ();
39 acoll.Document = doc;
40 acoll.DoOutput ();
42 var writer = new WellFormedXmlWriter (new XmlTextWriter (Console.Out) { Formatting = Formatting.Indented });
43 XmlNode decl = doc.CreateXmlDeclaration ("1.0", "utf-8", null);
44 doc.InsertBefore (decl, doc.DocumentElement);
45 doc.WriteTo (writer);
46 return 0;
50 public class Utils {
52 public static string CleanupTypeName (TypeReference type)
54 return CleanupTypeName (type.FullName);
57 static string CleanupTypeName (string t)
59 return t.Replace ('<', '[').Replace ('>', ']').Replace ('/', '+');
63 class AssemblyCollection
65 XmlDocument document;
66 List<AssemblyDefinition> assemblies = new List<AssemblyDefinition> ();
68 public AssemblyCollection ()
72 public bool Add (string name)
74 AssemblyDefinition ass = LoadAssembly (name);
75 if (ass == null)
76 return false;
78 assemblies.Add (ass);
79 return true;
82 public void DoOutput ()
84 if (document == null)
85 throw new InvalidOperationException ("Document not set");
87 XmlNode nassemblies = document.CreateElement ("assemblies", null);
88 document.AppendChild (nassemblies);
89 foreach (AssemblyDefinition a in assemblies) {
90 AssemblyData data = new AssemblyData (document, nassemblies, a);
91 data.DoOutput ();
95 public XmlDocument Document {
96 set { document = value; }
99 AssemblyDefinition LoadAssembly (string assembly)
101 try {
102 return TypeHelper.Resolver.Resolve (assembly);
103 } catch {
104 return null;
109 abstract class BaseData
111 protected XmlDocument document;
112 protected XmlNode parent;
114 protected BaseData (XmlDocument doc, XmlNode parent)
116 this.document = doc;
117 this.parent = parent;
120 public abstract void DoOutput ();
122 protected void AddAttribute (XmlNode node, string name, string value)
124 XmlAttribute attr = document.CreateAttribute (name);
125 attr.Value = value;
126 node.Attributes.Append (attr);
130 class TypeForwardedToData : BaseData
132 AssemblyDefinition ass;
134 public TypeForwardedToData (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
135 : base (document, parent)
137 this.ass = ass;
140 public override void DoOutput ()
142 XmlNode natts = parent.SelectSingleNode("attributes");
143 if (natts == null) {
144 natts = document.CreateElement ("attributes", null);
145 parent.AppendChild (natts);
148 foreach (TypeReference tref in ass.MainModule.ExternTypes) {
149 TypeDefinition def = tref.Resolve ();
150 if (def == null)
151 continue;
153 if (((uint)def.Attributes & 0x200000u) == 0)
154 continue;
156 XmlNode node = document.CreateElement ("attribute");
157 AddAttribute (node, "name", typeof (TypeForwardedToAttribute).FullName);
158 XmlNode properties = node.AppendChild (document.CreateElement ("properties"));
159 XmlNode property = properties.AppendChild (document.CreateElement ("property"));
160 AddAttribute (property, "name", "Destination");
161 AddAttribute (property, "value", Utils.CleanupTypeName (tref));
162 natts.AppendChild (node);
166 public static void OutputForwarders (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
168 TypeForwardedToData tftd = new TypeForwardedToData (document, parent, ass);
169 tftd.DoOutput ();
173 class AssemblyData : BaseData
175 AssemblyDefinition ass;
177 public AssemblyData (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
178 : base (document, parent)
180 this.ass = ass;
183 public override void DoOutput ()
185 if (document == null)
186 throw new InvalidOperationException ("Document not set");
188 XmlNode nassembly = document.CreateElement ("assembly", null);
189 AssemblyNameDefinition aname = ass.Name;
190 AddAttribute (nassembly, "name", aname.Name);
191 AddAttribute (nassembly, "version", aname.Version.ToString ());
192 parent.AppendChild (nassembly);
193 TypeForwardedToData.OutputForwarders (document, nassembly, ass);
194 AttributeData.OutputAttributes (document, nassembly, ass.CustomAttributes);
195 TypeDefinitionCollection typesCollection = ass.MainModule.Types;
196 if (typesCollection == null || typesCollection.Count == 0)
197 return;
198 object [] typesArray = new object [typesCollection.Count];
199 for (int i = 0; i < typesCollection.Count; i++) {
200 typesArray [i] = typesCollection [i];
202 Array.Sort (typesArray, TypeReferenceComparer.Default);
204 XmlNode nss = document.CreateElement ("namespaces", null);
205 nassembly.AppendChild (nss);
207 string current_namespace = "$%&$&";
208 XmlNode ns = null;
209 XmlNode classes = null;
210 foreach (TypeDefinition t in typesArray) {
211 if (string.IsNullOrEmpty (t.Namespace))
212 continue;
214 if ((t.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public)
215 continue;
217 if (t.DeclaringType != null)
218 continue; // enforce !nested
220 if (t.Namespace != current_namespace) {
221 current_namespace = t.Namespace;
222 ns = document.CreateElement ("namespace", null);
223 AddAttribute (ns, "name", current_namespace);
224 nss.AppendChild (ns);
225 classes = document.CreateElement ("classes", null);
226 ns.AppendChild (classes);
229 TypeData bd = new TypeData (document, classes, t);
230 bd.DoOutput ();
235 abstract class MemberData : BaseData
237 MemberReference [] members;
239 public MemberData (XmlDocument document, XmlNode parent, MemberReference [] members)
240 : base (document, parent)
242 this.members = members;
245 public override void DoOutput ()
247 XmlNode mclass = document.CreateElement (ParentTag, null);
248 parent.AppendChild (mclass);
250 foreach (MemberReference member in members) {
251 XmlNode mnode = document.CreateElement (Tag, null);
252 mclass.AppendChild (mnode);
253 AddAttribute (mnode, "name", GetName (member));
254 if (!NoMemberAttributes)
255 AddAttribute (mnode, "attrib", GetMemberAttributes (member));
257 AttributeData.OutputAttributes (document, mnode, GetCustomAttributes (member));
259 AddExtraData (mnode, member);
264 protected abstract CustomAttributeCollection GetCustomAttributes (MemberReference member);
266 protected virtual void AddExtraData (XmlNode p, MemberReference memberDefenition)
270 protected virtual string GetName (MemberReference memberDefenition)
272 return "NoNAME";
275 protected virtual string GetMemberAttributes (MemberReference memberDefenition)
277 return null;
280 public virtual bool NoMemberAttributes {
281 get { return false; }
282 set {}
285 public virtual string ParentTag {
286 get { return "NoPARENTTAG"; }
289 public virtual string Tag {
290 get { return "NoTAG"; }
293 public static void OutputGenericParameters (XmlDocument document, XmlNode nclass, IGenericParameterProvider provider)
295 if (provider.GenericParameters.Count == 0)
296 return;
298 var gparameters = provider.GenericParameters;
300 XmlElement ngeneric = document.CreateElement (string.Format ("generic-parameters"));
301 nclass.AppendChild (ngeneric);
303 foreach (GenericParameter gp in gparameters) {
304 XmlElement nparam = document.CreateElement (string.Format ("generic-parameter"));
305 nparam.SetAttribute ("name", gp.Name);
306 nparam.SetAttribute ("attributes", ((int) gp.Attributes).ToString ());
308 AttributeData.OutputAttributes (document, nparam, gp.CustomAttributes);
310 ngeneric.AppendChild (nparam);
312 var constraints = gp.Constraints;
313 if (constraints.Count == 0)
314 continue;
316 XmlElement nconstraint = document.CreateElement ("generic-parameter-constraints");
318 foreach (TypeReference constraint in constraints) {
319 XmlElement ncons = document.CreateElement ("generic-parameter-constraint");
320 ncons.SetAttribute ("name", Utils.CleanupTypeName (constraint));
321 nconstraint.AppendChild (ncons);
324 nparam.AppendChild (nconstraint);
329 class TypeData : MemberData
331 TypeDefinition type;
333 public TypeData (XmlDocument document, XmlNode parent, TypeDefinition type)
334 : base (document, parent, null)
336 this.type = type;
339 protected override CustomAttributeCollection GetCustomAttributes (MemberReference member) {
340 return ((TypeDefinition) member).CustomAttributes;
343 public override void DoOutput ()
345 if (document == null)
346 throw new InvalidOperationException ("Document not set");
348 XmlNode nclass = document.CreateElement ("class", null);
349 AddAttribute (nclass, "name", type.Name);
350 string classType = GetClassType (type);
351 AddAttribute (nclass, "type", classType);
353 if (type.BaseType != null)
354 AddAttribute (nclass, "base", Utils.CleanupTypeName (type.BaseType));
356 if (type.IsSealed)
357 AddAttribute (nclass, "sealed", "true");
359 if (type.IsAbstract)
360 AddAttribute (nclass, "abstract", "true");
362 if ( (type.Attributes & TypeAttributes.Serializable) != 0 || type.IsEnum)
363 AddAttribute (nclass, "serializable", "true");
365 string charSet = GetCharSet (type);
366 AddAttribute (nclass, "charset", charSet);
368 string layout = GetLayout (type);
369 if (layout != null)
370 AddAttribute (nclass, "layout", layout);
372 parent.AppendChild (nclass);
374 AttributeData.OutputAttributes (document, nclass, GetCustomAttributes(type));
376 XmlNode ifaces = null;
378 foreach (TypeReference iface in TypeHelper.GetInterfaces (type)) {
379 if (!TypeHelper.IsPublic (iface))
380 // we're only interested in public interfaces
381 continue;
383 if (ifaces == null) {
384 ifaces = document.CreateElement ("interfaces", null);
385 nclass.AppendChild (ifaces);
388 XmlNode iface_node = document.CreateElement ("interface", null);
389 AddAttribute (iface_node, "name", Utils.CleanupTypeName (iface));
390 ifaces.AppendChild (iface_node);
393 MemberData.OutputGenericParameters (document, nclass, type);
395 ArrayList members = new ArrayList ();
397 FieldDefinition [] fields = GetFields (type);
398 if (fields.Length > 0) {
399 Array.Sort (fields, MemberReferenceComparer.Default);
400 FieldData fd = new FieldData (document, nclass, fields);
401 members.Add (fd);
404 if (type.IsEnum) {
405 var value_type = GetEnumValueField (type);
406 if (value_type == null)
407 throw new NotSupportedException ();
409 AddAttribute (nclass, "enumtype", Utils.CleanupTypeName (value_type.FieldType));
412 MethodDefinition [] ctors = GetConstructors (type);
413 if (ctors.Length > 0) {
414 Array.Sort (ctors, MemberReferenceComparer.Default);
415 members.Add (new ConstructorData (document, nclass, ctors));
418 PropertyDefinition[] properties = GetProperties (type);
419 if (properties.Length > 0) {
420 Array.Sort (properties, MemberReferenceComparer.Default);
421 members.Add (new PropertyData (document, nclass, properties));
424 EventDefinition [] events = GetEvents (type);
425 if (events.Length > 0) {
426 Array.Sort (events, MemberReferenceComparer.Default);
427 members.Add (new EventData (document, nclass, events));
430 MethodDefinition [] methods = GetMethods (type);
431 if (methods.Length > 0) {
432 Array.Sort (methods, MemberReferenceComparer.Default);
433 members.Add (new MethodData (document, nclass, methods));
436 foreach (MemberData md in members)
437 md.DoOutput ();
439 NestedTypeCollection nested = type.NestedTypes;
440 //remove non public(familiy) and nested in second degree
441 for (int i = nested.Count - 1; i >= 0; i--) {
442 TypeDefinition t = nested [i];
443 if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic ||
444 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily ||
445 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) {
446 // public
447 if (t.DeclaringType == type)
448 continue; // not nested of nested
451 nested.RemoveAt (i);
455 if (nested.Count > 0) {
456 XmlNode classes = document.CreateElement ("classes", null);
457 nclass.AppendChild (classes);
458 foreach (TypeDefinition t in nested) {
459 TypeData td = new TypeData (document, classes, t);
460 td.DoOutput ();
465 static FieldReference GetEnumValueField (TypeDefinition type)
467 foreach (FieldDefinition field in type.Fields)
468 if (field.IsSpecialName && field.Name == "value__")
469 return field;
471 return null;
474 protected override string GetMemberAttributes (MemberReference member)
476 if (member != type)
477 throw new InvalidOperationException ("odd");
479 return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
482 public static bool MustDocumentMethod (MethodDefinition method) {
483 // All other methods
484 MethodAttributes maskedAccess = method.Attributes & MethodAttributes.MemberAccessMask;
485 return maskedAccess == MethodAttributes.Public
486 || maskedAccess == MethodAttributes.Family
487 || maskedAccess == MethodAttributes.FamORAssem;
490 static string GetClassType (TypeDefinition t)
492 if (t.IsEnum)
493 return "enum";
495 if (t.IsValueType)
496 return "struct";
498 if (t.IsInterface)
499 return "interface";
501 if (TypeHelper.IsDelegate(t))
502 return "delegate";
504 return "class";
507 static string GetCharSet (TypeDefinition type)
509 TypeAttributes maskedStringFormat = type.Attributes & TypeAttributes.StringFormatMask;
510 if (maskedStringFormat == TypeAttributes.AnsiClass)
511 return CharSet.Ansi.ToString ();
513 if (maskedStringFormat == TypeAttributes.AutoClass)
514 return CharSet.Auto.ToString ();
516 if (maskedStringFormat == TypeAttributes.UnicodeClass)
517 return CharSet.Unicode.ToString ();
519 return CharSet.None.ToString ();
522 static string GetLayout (TypeDefinition type)
524 TypeAttributes maskedLayout = type.Attributes & TypeAttributes.LayoutMask;
525 if (maskedLayout == TypeAttributes.AutoLayout)
526 return LayoutKind.Auto.ToString ();
528 if (maskedLayout == TypeAttributes.ExplicitLayout)
529 return LayoutKind.Explicit.ToString ();
531 if (maskedLayout == TypeAttributes.SequentialLayout)
532 return LayoutKind.Sequential.ToString ();
534 return null;
537 FieldDefinition [] GetFields (TypeDefinition type) {
538 ArrayList list = new ArrayList ();
540 FieldDefinitionCollection fields = type.Fields;
541 foreach (FieldDefinition field in fields) {
542 if (field.IsSpecialName)
543 continue;
545 // we're only interested in public or protected members
546 FieldAttributes maskedVisibility = (field.Attributes & FieldAttributes.FieldAccessMask);
547 if (maskedVisibility == FieldAttributes.Public
548 || maskedVisibility == FieldAttributes.Family
549 || maskedVisibility == FieldAttributes.FamORAssem) {
550 list.Add (field);
554 return (FieldDefinition []) list.ToArray (typeof (FieldDefinition));
558 internal static PropertyDefinition [] GetProperties (TypeDefinition type) {
559 ArrayList list = new ArrayList ();
561 PropertyDefinitionCollection properties = type.Properties;//type.GetProperties (flags);
562 foreach (PropertyDefinition property in properties) {
563 MethodDefinition getMethod = property.GetMethod;
564 MethodDefinition setMethod = property.SetMethod;
566 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
567 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
569 // if neither the getter or setter should be documented, then
570 // skip the property
571 if (hasGetter || hasSetter) {
572 list.Add (property);
576 return (PropertyDefinition []) list.ToArray (typeof (PropertyDefinition));
579 private MethodDefinition[] GetMethods (TypeDefinition type)
581 ArrayList list = new ArrayList ();
583 MethodDefinitionCollection methods = type.Methods;//type.GetMethods (flags);
584 foreach (MethodDefinition method in methods) {
585 if (method.IsSpecialName && !method.Name.StartsWith ("op_"))
586 continue;
588 // we're only interested in public or protected members
589 if (!MustDocumentMethod(method))
590 continue;
592 if (IsFinalizer (method))
593 continue;
595 list.Add (method);
598 return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
601 static bool IsFinalizer (MethodDefinition method)
603 if (method.Name != "Finalize")
604 return false;
606 if (!method.IsVirtual)
607 return false;
609 if (method.Parameters.Count != 0)
610 return false;
612 return true;
615 private MethodDefinition [] GetConstructors (TypeDefinition type)
617 ArrayList list = new ArrayList ();
619 ConstructorCollection ctors = type.Constructors;//type.GetConstructors (flags);
620 foreach (MethodDefinition constructor in ctors) {
621 // we're only interested in public or protected members
622 if (!MustDocumentMethod(constructor))
623 continue;
625 list.Add (constructor);
628 return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
631 private EventDefinition[] GetEvents (TypeDefinition type)
633 ArrayList list = new ArrayList ();
635 EventDefinitionCollection events = type.Events;//type.GetEvents (flags);
636 foreach (EventDefinition eventDef in events) {
637 MethodDefinition addMethod = eventDef.AddMethod;//eventInfo.GetAddMethod (true);
639 if (addMethod == null || !MustDocumentMethod (addMethod))
640 continue;
642 list.Add (eventDef);
645 return (EventDefinition []) list.ToArray (typeof (EventDefinition));
649 class FieldData : MemberData
651 public FieldData (XmlDocument document, XmlNode parent, FieldDefinition [] members)
652 : base (document, parent, members)
656 protected override CustomAttributeCollection GetCustomAttributes (MemberReference member) {
657 return ((FieldDefinition) member).CustomAttributes;
660 protected override string GetName (MemberReference memberDefenition)
662 FieldDefinition field = (FieldDefinition) memberDefenition;
663 return field.Name;
666 protected override string GetMemberAttributes (MemberReference memberDefenition)
668 FieldDefinition field = (FieldDefinition) memberDefenition;
669 return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
672 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
674 base.AddExtraData (p, memberDefenition);
675 FieldDefinition field = (FieldDefinition) memberDefenition;
676 AddAttribute (p, "fieldtype", Utils.CleanupTypeName (field.FieldType));
678 if (field.IsLiteral) {
679 object value = field.Constant;//object value = field.GetValue (null);
680 string stringValue = null;
681 //if (value is Enum) {
682 // // FIXME: when Mono bug #60090 has been
683 // // fixed, we should just be able to use
684 // // Convert.ToString
685 // stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
687 //else {
688 stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
691 if (stringValue != null)
692 AddAttribute (p, "value", stringValue);
696 public override string ParentTag {
697 get { return "fields"; }
700 public override string Tag {
701 get { return "field"; }
705 class PropertyData : MemberData
707 public PropertyData (XmlDocument document, XmlNode parent, PropertyDefinition [] members)
708 : base (document, parent, members)
712 protected override CustomAttributeCollection GetCustomAttributes (MemberReference member) {
713 return ((PropertyDefinition) member).CustomAttributes;
716 protected override string GetName (MemberReference memberDefenition)
718 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
719 return prop.Name;
722 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
724 base.AddExtraData (p, memberDefenition);
725 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
726 AddAttribute (p, "ptype", Utils.CleanupTypeName (prop.PropertyType));
727 MethodDefinition _get = prop.GetMethod;
728 MethodDefinition _set = prop.SetMethod;
729 bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
730 bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
731 MethodDefinition [] methods;
733 if (haveGet && haveSet) {
734 methods = new MethodDefinition [] { _get, _set };
735 } else if (haveGet) {
736 methods = new MethodDefinition [] { _get };
737 } else if (haveSet) {
738 methods = new MethodDefinition [] { _set };
739 } else {
740 //odd
741 return;
744 string parms = Parameters.GetSignature (methods [0].Parameters);
745 if (!string.IsNullOrEmpty (parms))
746 AddAttribute (p, "params", parms);
748 MethodData data = new MethodData (document, p, methods);
749 //data.NoMemberAttributes = true;
750 data.DoOutput ();
753 protected override string GetMemberAttributes (MemberReference memberDefenition)
755 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
756 return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
759 public override string ParentTag {
760 get { return "properties"; }
763 public override string Tag {
764 get { return "property"; }
768 class EventData : MemberData
770 public EventData (XmlDocument document, XmlNode parent, EventDefinition [] members)
771 : base (document, parent, members)
775 protected override CustomAttributeCollection GetCustomAttributes (MemberReference member) {
776 return ((EventDefinition) member).CustomAttributes;
779 protected override string GetName (MemberReference memberDefenition)
781 EventDefinition evt = (EventDefinition) memberDefenition;
782 return evt.Name;
785 protected override string GetMemberAttributes (MemberReference memberDefenition)
787 EventDefinition evt = (EventDefinition) memberDefenition;
788 return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
791 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
793 base.AddExtraData (p, memberDefenition);
794 EventDefinition evt = (EventDefinition) memberDefenition;
795 AddAttribute (p, "eventtype", Utils.CleanupTypeName (evt.EventType));
798 public override string ParentTag {
799 get { return "events"; }
802 public override string Tag {
803 get { return "event"; }
807 class MethodData : MemberData
809 bool noAtts;
811 public MethodData (XmlDocument document, XmlNode parent, MethodDefinition [] members)
812 : base (document, parent, members)
816 protected override CustomAttributeCollection GetCustomAttributes (MemberReference member) {
817 return ((MethodDefinition) member).CustomAttributes;
820 protected override string GetName (MemberReference memberDefenition)
822 MethodDefinition method = (MethodDefinition) memberDefenition;
823 string name = method.Name;
824 string parms = Parameters.GetSignature (method.Parameters);
826 return string.Format ("{0}({1})", name, parms);
829 protected override string GetMemberAttributes (MemberReference memberDefenition)
831 MethodDefinition method = (MethodDefinition) memberDefenition;
832 return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture);
835 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
837 base.AddExtraData (p, memberDefenition);
839 if (!(memberDefenition is MethodDefinition))
840 return;
842 MethodDefinition mbase = (MethodDefinition) memberDefenition;
844 ParameterData parms = new ParameterData (document, p, mbase.Parameters);
845 parms.DoOutput ();
847 if (mbase.IsAbstract)
848 AddAttribute (p, "abstract", "true");
849 if (mbase.IsVirtual)
850 AddAttribute (p, "virtual", "true");
851 if (mbase.IsStatic)
852 AddAttribute (p, "static", "true");
854 string rettype = Utils.CleanupTypeName (mbase.ReturnType.ReturnType);
855 if (rettype != "System.Void" || !mbase.IsConstructor)
856 AddAttribute (p, "returntype", (rettype));
858 AttributeData.OutputAttributes (document, p, mbase.ReturnType.CustomAttributes);
860 MemberData.OutputGenericParameters (document, p, mbase);
863 public override bool NoMemberAttributes {
864 get { return noAtts; }
865 set { noAtts = value; }
868 public override string ParentTag {
869 get { return "methods"; }
872 public override string Tag {
873 get { return "method"; }
877 class ConstructorData : MethodData
879 public ConstructorData (XmlDocument document, XmlNode parent, MethodDefinition [] members)
880 : base (document, parent, members)
884 public override string ParentTag {
885 get { return "constructors"; }
888 public override string Tag {
889 get { return "constructor"; }
893 class ParameterData : BaseData
895 private ParameterDefinitionCollection parameters;
897 public ParameterData (XmlDocument document, XmlNode parent, ParameterDefinitionCollection parameters)
898 : base (document, parent)
900 this.parameters = parameters;
903 public override void DoOutput ()
905 XmlNode parametersNode = document.CreateElement ("parameters");
906 parent.AppendChild (parametersNode);
908 foreach (ParameterDefinition parameter in parameters) {
909 XmlNode paramNode = document.CreateElement ("parameter");
910 parametersNode.AppendChild (paramNode);
911 AddAttribute (paramNode, "name", parameter.Name);
912 AddAttribute (paramNode, "position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture));
913 AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString());
915 string direction = "in";
917 if (parameter.ParameterType is ReferenceType)
918 direction = parameter.IsOut ? "out" : "ref";
920 TypeReference t = parameter.ParameterType;
921 AddAttribute (paramNode, "type", Utils.CleanupTypeName (t));
923 if (parameter.IsOptional) {
924 AddAttribute (paramNode, "optional", "true");
925 if (parameter.HasConstant)
926 AddAttribute (paramNode, "defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ());
929 if (direction != "in")
930 AddAttribute (paramNode, "direction", direction);
932 AttributeData.OutputAttributes (document, paramNode, parameter.CustomAttributes);
937 class AttributeData : BaseData
939 CustomAttributeCollection atts;
941 AttributeData (XmlDocument doc, XmlNode parent, CustomAttributeCollection attributes)
942 : base (doc, parent)
944 atts = attributes;
947 public override void DoOutput ()
949 if (document == null)
950 throw new InvalidOperationException ("Document not set");
952 if (atts == null || atts.Count == 0)
953 return;
955 XmlNode natts = parent.SelectSingleNode("attributes");
956 if (natts == null) {
957 natts = document.CreateElement ("attributes", null);
958 parent.AppendChild (natts);
961 for (int i = 0; i < atts.Count; ++i) {
962 CustomAttribute att = atts [i];
963 try {
964 att.Resolve ();
965 } catch {}
967 if (!att.Resolved)
968 continue;
970 string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType);
971 if (SkipAttribute (att))
972 continue;
974 XmlNode node = document.CreateElement ("attribute");
975 AddAttribute (node, "name", attName);
977 XmlNode properties = null;
979 Dictionary<string, object> attribute_mapping = CreateAttributeMapping (att);
981 foreach (string name in attribute_mapping.Keys) {
982 if (name == "TypeId")
983 continue;
985 if (properties == null) {
986 properties = node.AppendChild (document.CreateElement ("properties"));
989 object o = attribute_mapping [name];
991 XmlNode n = properties.AppendChild (document.CreateElement ("property"));
992 AddAttribute (n, "name", name);
994 if (o == null) {
995 AddAttribute (n, "value", "null");
996 continue;
998 string value = o.ToString ();
999 if (attName.EndsWith ("GuidAttribute"))
1000 value = value.ToUpper ();
1001 AddAttribute (n, "value", value);
1004 natts.AppendChild (node);
1008 static Dictionary<string, object> CreateAttributeMapping (CustomAttribute attribute)
1010 var mapping = new Dictionary<string, object> ();
1012 PopulateMapping (mapping, attribute);
1014 var constructor = attribute.Constructor.Resolve ();
1015 if (constructor == null || constructor.Parameters.Count == 0)
1016 return mapping;
1018 PopulateMapping (mapping, constructor, attribute);
1020 return mapping;
1023 static void PopulateMapping (Dictionary<string, object> mapping, CustomAttribute attribute)
1025 foreach (DictionaryEntry entry in attribute.Properties) {
1026 var name = (string) entry.Key;
1028 mapping.Add (name, GetArgumentValue (attribute.GetPropertyType (name), entry.Value));
1032 static Dictionary<FieldReference, int> CreateArgumentFieldMapping (MethodDefinition constructor)
1034 Dictionary<FieldReference, int> field_mapping = new Dictionary<FieldReference, int> ();
1036 int? argument = null;
1038 foreach (Instruction instruction in constructor.Body.Instructions) {
1039 switch (instruction.OpCode.Code) {
1040 case Code.Ldarg_1:
1041 argument = 1;
1042 break;
1043 case Code.Ldarg_2:
1044 argument = 2;
1045 break;
1046 case Code.Ldarg_3:
1047 argument = 3;
1048 break;
1049 case Code.Ldarg:
1050 case Code.Ldarg_S:
1051 argument = ((ParameterDefinition) instruction.Operand).Sequence;
1052 break;
1054 case Code.Stfld:
1055 FieldReference field = (FieldReference) instruction.Operand;
1056 if (field.DeclaringType.FullName != constructor.DeclaringType.FullName)
1057 continue;
1059 if (!argument.HasValue)
1060 break;
1062 if (!field_mapping.ContainsKey (field))
1063 field_mapping.Add (field, (int) argument - 1);
1065 argument = null;
1066 break;
1070 return field_mapping;
1073 static Dictionary<PropertyDefinition, FieldReference> CreatePropertyFieldMapping (TypeDefinition type)
1075 Dictionary<PropertyDefinition, FieldReference> property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
1077 foreach (PropertyDefinition property in type.Properties) {
1078 if (property.GetMethod == null)
1079 continue;
1080 if (!property.GetMethod.HasBody)
1081 continue;
1083 foreach (Instruction instruction in property.GetMethod.Body.Instructions) {
1084 if (instruction.OpCode.Code != Code.Ldfld)
1085 continue;
1087 FieldReference field = (FieldReference) instruction.Operand;
1088 if (field.DeclaringType.FullName != type.FullName)
1089 continue;
1091 property_mapping.Add (property, field);
1092 break;
1096 return property_mapping;
1099 static void PopulateMapping (Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
1101 if (!constructor.HasBody)
1102 return;
1104 var field_mapping = CreateArgumentFieldMapping (constructor);
1105 var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
1107 foreach (var pair in property_mapping) {
1108 int argument;
1109 if (!field_mapping.TryGetValue (pair.Value, out argument))
1110 continue;
1112 mapping.Add (pair.Key.Name, GetArgumentValue (constructor.Parameters [argument].ParameterType, attribute.ConstructorParameters [argument]));
1116 static object GetArgumentValue (TypeReference reference, object value)
1118 var type = reference.Resolve ();
1119 if (type == null)
1120 return value;
1122 if (type.IsEnum) {
1123 if (IsFlaggedEnum (type))
1124 return GetFlaggedEnumValue (type, value);
1126 return GetEnumValue (type, value);
1129 return value;
1132 static bool IsFlaggedEnum (TypeDefinition type)
1134 if (!type.IsEnum)
1135 return false;
1137 if (type.CustomAttributes.Count == 0)
1138 return false;
1140 foreach (CustomAttribute attribute in type.CustomAttributes)
1141 if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute")
1142 return true;
1144 return false;
1147 static object GetFlaggedEnumValue (TypeDefinition type, object value)
1149 long flags = Convert.ToInt64 (value);
1150 var signature = new StringBuilder ();
1152 for (int i = type.Fields.Count - 1; i >= 0; i--) {
1153 FieldDefinition field = type.Fields [i];
1155 if (!field.HasConstant)
1156 continue;
1158 long flag = Convert.ToInt64 (field.Constant);
1160 if (flag == 0)
1161 continue;
1163 if ((flags & flag) == flag) {
1164 if (signature.Length != 0)
1165 signature.Append (", ");
1167 signature.Append (field.Name);
1168 flags -= flag;
1172 return signature.ToString ();
1175 static object GetEnumValue (TypeDefinition type, object value)
1177 foreach (FieldDefinition field in type.Fields) {
1178 if (!field.HasConstant)
1179 continue;
1181 if (Comparer.Default.Compare (field.Constant, value) == 0)
1182 return field.Name;
1185 return value;
1188 static bool SkipAttribute (CustomAttribute attribute)
1190 var type_name = Utils.CleanupTypeName (attribute.Constructor.DeclaringType);
1192 return !TypeHelper.IsPublic (attribute)
1193 || type_name.EndsWith ("TODOAttribute");
1196 public static void OutputAttributes (XmlDocument doc, XmlNode parent, CustomAttributeCollection attributes)
1198 AttributeData ad = new AttributeData (doc, parent, attributes);
1199 ad.DoOutput ();
1203 static class Parameters {
1205 public static string GetSignature (ParameterDefinitionCollection infos)
1207 if (infos == null || infos.Count == 0)
1208 return "";
1210 var signature = new StringBuilder ();
1211 for (int i = 0; i < infos.Count; i++) {
1213 if (i > 0)
1214 signature.Append (", ");
1216 ParameterDefinition info = infos [i];
1218 string modifier;
1219 if ((info.Attributes & ParameterAttributes.In) != 0)
1220 modifier = "in";
1221 else if ((info.Attributes & ParameterAttributes.Retval) != 0)
1222 modifier = "ref";
1223 else if ((info.Attributes & ParameterAttributes.Out) != 0)
1224 modifier = "out";
1225 else
1226 modifier = string.Empty;
1228 if (modifier.Length > 0)
1229 signature.AppendFormat ("{0} ", modifier);
1231 signature.Append (Utils.CleanupTypeName (info.ParameterType));
1234 return signature.ToString ();
1239 class TypeReferenceComparer : IComparer
1241 public static TypeReferenceComparer Default = new TypeReferenceComparer ();
1243 public int Compare (object a, object b)
1245 TypeReference ta = (TypeReference) a;
1246 TypeReference tb = (TypeReference) b;
1247 int result = String.Compare (ta.Namespace, tb.Namespace);
1248 if (result != 0)
1249 return result;
1251 return String.Compare (ta.Name, tb.Name);
1255 class MemberReferenceComparer : IComparer
1257 public static MemberReferenceComparer Default = new MemberReferenceComparer ();
1259 public int Compare (object a, object b)
1261 MemberReference ma = (MemberReference) a;
1262 MemberReference mb = (MemberReference) b;
1263 return String.Compare (ma.Name, mb.Name);
1267 class MethodDefinitionComparer : IComparer
1269 public static MethodDefinitionComparer Default = new MethodDefinitionComparer ();
1271 public int Compare (object a, object b)
1273 MethodDefinition ma = (MethodDefinition) a;
1274 MethodDefinition mb = (MethodDefinition) b;
1275 int res = String.Compare (ma.Name, mb.Name);
1276 if (res != 0)
1277 return res;
1279 ParameterDefinitionCollection pia = ma.Parameters ;
1280 ParameterDefinitionCollection pib = mb.Parameters;
1281 res = pia.Count - pib.Count;
1282 if (res != 0)
1283 return res;
1285 string siga = Parameters.GetSignature (pia);
1286 string sigb = Parameters.GetSignature (pib);
1287 return String.Compare (siga, sigb);