[mono-api-info] CreateAttributeMapping: don't create dictionaries unless there is...
[mono-project.git] / mcs / tools / corcompare / mono-api-info.cs
blobf9189a2ce80bf193d39b3e676fcf64028db18330
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.Linq;
15 using System.Runtime.CompilerServices;
16 using System.Runtime.InteropServices;
17 using System.Security.Permissions;
18 using System.Text;
19 using System.Xml;
21 using Mono.Cecil;
22 using Mono.Cecil.Cil;
23 using System.IO;
25 namespace CorCompare
27 public class Driver
29 public static int Main (string [] args)
31 bool showHelp = false;
32 AbiMode = false;
33 FollowForwarders = false;
34 string output = null;
36 var acoll = new AssemblyCollection ();
38 var options = new Mono.Options.OptionSet {
39 "usage: mono-api-info [OPTIONS+] ASSEMBLY+",
40 "",
41 "Expose IL structure of CLR assemblies as XML.",
42 "",
43 "Available Options:",
44 { "abi",
45 "Generate ABI, not API; contains only classes with instance fields which are not [NonSerialized].",
46 v => AbiMode = v != null },
47 { "f|follow-forwarders",
48 "Follow type forwarders.",
49 v => FollowForwarders = v != null },
50 { "d|L|lib|search-directory=",
51 "Check for assembly references in {DIRECTORY}.",
52 v => TypeHelper.Resolver.AddSearchDirectory (v) },
53 { "r=",
54 "Read and register the file {ASSEMBLY}, and add the directory containing ASSEMBLY to the search path.",
55 v => TypeHelper.Resolver.ResolveFile (v) },
56 { "o=",
57 "The output file. If not specified the output will be written to stdout.",
58 v => output = v },
59 { "h|?|help",
60 "Show this message and exit.",
61 v => showHelp = v != null },
64 var asms = options.Parse (args);
66 if (showHelp || asms.Count == 0) {
67 options.WriteOptionDescriptions (Console.Out);
68 Console.WriteLine ();
69 return showHelp? 0 :1;
72 string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
73 string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
74 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"assembly\GAC\MSDATASRC\7.0.3300.0__b03f5f7f11d50a3a"));
76 foreach (string arg in asms) {
77 acoll.Add (arg);
79 if (arg.Contains ("v3.0")) {
80 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
81 } else if (arg.Contains ("v3.5")) {
82 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
83 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v3.0\Windows Communication Foundation"));
84 } else if (arg.Contains ("v4.0")) {
85 if (arg.Contains ("Silverlight")) {
86 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (pf, @"Microsoft Silverlight\4.0.51204.0"));
87 } else {
88 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319"));
89 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319\WPF"));
91 } else {
92 TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg));
96 StreamWriter outputStream = null;
97 if (!string.IsNullOrEmpty (output))
98 outputStream = new StreamWriter (output);
99 try {
100 TextWriter outStream = outputStream ?? Console.Out;
101 var settings = new XmlWriterSettings ();
102 settings.Indent = true;
103 var textWriter = XmlWriter.Create (outStream, settings);
104 var writer = new WellFormedXmlWriter (textWriter);
105 writer.WriteStartDocument ();
106 acoll.Writer = writer;
107 acoll.DoOutput ();
108 writer.WriteEndDocument ();
109 writer.Flush ();
110 } finally {
111 if (outputStream != null)
112 outputStream.Dispose ();
114 return 0;
117 internal static bool AbiMode { get; private set; }
118 internal static bool FollowForwarders { get; private set; }
121 public class Utils {
122 static char[] CharsToCleanup = new char[] { '<', '>', '/' };
124 public static string CleanupTypeName (TypeReference type)
126 return CleanupTypeName (type.FullName);
129 public static string CleanupTypeName (string t)
131 if (t.IndexOfAny (CharsToCleanup) == -1)
132 return t;
133 var sb = new StringBuilder (t.Length);
134 for (int i = 0; i < t.Length; i++) {
135 var ch = t [i];
136 switch (ch) {
137 case '<':
138 sb.Append ('[');
139 break;
140 case '>':
141 sb.Append (']');
142 break;
143 case '/':
144 sb.Append ('+');
145 break;
146 default:
147 sb.Append (ch);
148 break;
151 return sb.ToString ();
155 class AssemblyCollection
157 XmlWriter writer;
158 List<AssemblyDefinition> assemblies = new List<AssemblyDefinition> ();
160 public AssemblyCollection ()
164 public bool Add (string name)
166 AssemblyDefinition ass = LoadAssembly (name);
167 if (ass == null) {
168 Console.Error.WriteLine ("Cannot load assembly file " + name);
169 return false;
172 assemblies.Add (ass);
173 return true;
176 public void DoOutput ()
178 if (writer == null)
179 throw new InvalidOperationException ("Document not set");
181 writer.WriteStartElement ("assemblies");
182 foreach (AssemblyDefinition a in assemblies) {
183 AssemblyData data = new AssemblyData (writer, a);
184 data.DoOutput ();
186 writer.WriteEndElement ();
189 public XmlWriter Writer {
190 set { writer = value; }
193 AssemblyDefinition LoadAssembly (string assembly)
195 try {
196 if (File.Exists (assembly))
197 return TypeHelper.Resolver.ResolveFile (assembly);
199 return TypeHelper.Resolver.Resolve (assembly);
200 } catch (Exception e) {
201 Console.WriteLine (e);
202 return null;
207 abstract class BaseData
209 protected XmlWriter writer;
211 protected BaseData (XmlWriter writer)
213 this.writer = writer;
216 public abstract void DoOutput ();
218 protected void AddAttribute (string name, string value)
220 writer.WriteAttributeString (name, value);
224 class TypeForwardedToData : BaseData
226 AssemblyDefinition ass;
228 public TypeForwardedToData (XmlWriter writer, AssemblyDefinition ass)
229 : base (writer)
231 this.ass = ass;
234 public override void DoOutput ()
236 foreach (ExportedType type in ass.MainModule.ExportedTypes) {
238 if (((uint)type.Attributes & 0x200000u) == 0)
239 continue;
241 writer.WriteStartElement ("attribute");
242 AddAttribute ("name", typeof (TypeForwardedToAttribute).FullName);
243 writer.WriteStartElement ("properties");
244 writer.WriteStartElement ("property");
245 AddAttribute ("name", "Destination");
246 AddAttribute ("value", Utils.CleanupTypeName (type.FullName));
247 writer.WriteEndElement (); // properties
248 writer.WriteEndElement (); // properties
249 writer.WriteEndElement (); // attribute
253 public static void OutputForwarders (XmlWriter writer, AssemblyDefinition ass)
255 TypeForwardedToData tftd = new TypeForwardedToData (writer, ass);
256 tftd.DoOutput ();
260 class AssemblyData : BaseData
262 AssemblyDefinition ass;
264 public AssemblyData (XmlWriter writer, AssemblyDefinition ass)
265 : base (writer)
267 this.ass = ass;
270 public override void DoOutput ()
272 if (writer == null)
273 throw new InvalidOperationException ("Document not set");
275 writer.WriteStartElement ("assembly");
276 AssemblyNameDefinition aname = ass.Name;
277 AddAttribute ("name", aname.Name);
278 AddAttribute ("version", aname.Version.ToString ());
280 AttributeData.OutputAttributes (writer, ass);
282 var types = new List<TypeDefinition> ();
283 if (ass.MainModule.Types != null) {
284 types.AddRange (ass.MainModule.Types);
287 if (Driver.FollowForwarders && ass.MainModule.ExportedTypes != null) {
288 foreach (var t in ass.MainModule.ExportedTypes) {
289 var forwarded = t.Resolve ();
290 if (forwarded == null) {
291 throw new Exception ("Could not resolve forwarded type " + t.FullName + " in " + ass.Name);
293 types.Add (forwarded);
297 if (types.Count == 0) {
298 writer.WriteEndElement (); // assembly
299 return;
302 types.Sort (TypeReferenceComparer.Default);
304 writer.WriteStartElement ("namespaces");
306 string current_namespace = "$%&$&";
307 bool in_namespace = false;
308 foreach (TypeDefinition t in types) {
309 if (string.IsNullOrEmpty (t.Namespace))
310 continue;
312 if (!Driver.AbiMode && ((t.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public))
313 continue;
315 if (t.DeclaringType != null)
316 continue; // enforce !nested
318 if (t.Namespace != current_namespace) {
319 current_namespace = t.Namespace;
320 if (in_namespace) {
321 writer.WriteEndElement (); // classes
322 writer.WriteEndElement (); // namespace
323 } else {
324 in_namespace = true;
326 writer.WriteStartElement ("namespace");
327 AddAttribute ("name", current_namespace);
328 writer.WriteStartElement ("classes");
331 TypeData bd = new TypeData (writer, t);
332 bd.DoOutput ();
336 if (in_namespace) {
337 writer.WriteEndElement (); // classes
338 writer.WriteEndElement (); // namespace
341 writer.WriteEndElement (); // namespaces
343 writer.WriteEndElement (); // assembly
347 abstract class MemberData : BaseData
349 MemberReference [] members;
351 public MemberData (XmlWriter writer, MemberReference [] members)
352 : base (writer)
354 this.members = members;
357 protected virtual ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member)
359 return null;
362 public override void DoOutput ()
364 writer.WriteStartElement (ParentTag);
366 foreach (MemberReference member in members) {
367 writer.WriteStartElement (Tag);
368 AddAttribute ("name", GetName (member));
369 if (!NoMemberAttributes)
370 AddAttribute ("attrib", GetMemberAttributes (member));
371 AddExtraAttributes (member);
373 AttributeData.OutputAttributes (writer, (ICustomAttributeProvider) member, GetAdditionalCustomAttributeProvider (member));
375 AddExtraData (member);
376 writer.WriteEndElement (); // Tag
379 writer.WriteEndElement (); // ParentTag
382 protected virtual void AddExtraData (MemberReference memberDefenition)
386 protected virtual void AddExtraAttributes (MemberReference memberDefinition)
390 protected virtual string GetName (MemberReference memberDefenition)
392 return "NoNAME";
395 protected virtual string GetMemberAttributes (MemberReference memberDefenition)
397 return null;
400 public virtual bool NoMemberAttributes {
401 get { return false; }
402 set {}
405 public virtual string ParentTag {
406 get { return "NoPARENTTAG"; }
409 public virtual string Tag {
410 get { return "NoTAG"; }
413 public static void OutputGenericParameters (XmlWriter writer, IGenericParameterProvider provider)
415 if (provider.GenericParameters.Count == 0)
416 return;
418 var gparameters = provider.GenericParameters;
420 writer.WriteStartElement ("generic-parameters");
422 foreach (GenericParameter gp in gparameters) {
423 writer.WriteStartElement ("generic-parameter");
424 writer.WriteAttributeString ("name", gp.Name);
425 writer.WriteAttributeString ("attributes", ((int) gp.Attributes).ToString ());
427 AttributeData.OutputAttributes (writer, gp);
429 var constraints = gp.Constraints;
430 if (constraints.Count == 0) {
431 writer.WriteEndElement (); // generic-parameter
432 continue;
435 writer.WriteStartElement ("generic-parameter-constraints");
437 foreach (TypeReference constraint in constraints) {
438 writer.WriteStartElement ("generic-parameter-constraint");
439 writer.WriteAttributeString ("name", Utils.CleanupTypeName (constraint));
440 writer.WriteEndElement (); // generic-parameter-constraint
443 writer.WriteEndElement (); // generic-parameter-constraints
445 writer.WriteEndElement (); // generic-parameter
448 writer.WriteEndElement (); // generic-parameters
452 class TypeData : MemberData
454 TypeDefinition type;
456 public TypeData (XmlWriter writer, TypeDefinition type)
457 : base (writer, null)
459 this.type = type;
461 public override void DoOutput ()
463 if (writer == null)
464 throw new InvalidOperationException ("Document not set");
466 writer.WriteStartElement ("class");
467 AddAttribute ("name", type.Name);
468 string classType = GetClassType (type);
469 AddAttribute ("type", classType);
471 if (type.BaseType != null)
472 AddAttribute ("base", Utils.CleanupTypeName (type.BaseType));
474 if (type.IsSealed)
475 AddAttribute ("sealed", "true");
477 if (type.IsAbstract)
478 AddAttribute ("abstract", "true");
480 if ( (type.Attributes & TypeAttributes.Serializable) != 0 || type.IsEnum)
481 AddAttribute ("serializable", "true");
483 string charSet = GetCharSet (type);
484 AddAttribute ("charset", charSet);
486 string layout = GetLayout (type);
487 if (layout != null)
488 AddAttribute ("layout", layout);
490 if (type.PackingSize >= 0) {
491 AddAttribute ("pack", type.PackingSize.ToString ());
494 if (type.ClassSize >= 0) {
495 AddAttribute ("size", type.ClassSize.ToString ());
498 if (type.IsEnum) {
499 var value_type = GetEnumValueField (type);
500 if (value_type == null)
501 throw new NotSupportedException ();
503 AddAttribute ("enumtype", Utils.CleanupTypeName (value_type.FieldType));
506 AttributeData.OutputAttributes (writer, type);
508 var ifaces = TypeHelper.GetInterfaces (type).
509 Where ((iface) => TypeHelper.IsPublic (iface)). // we're only interested in public interfaces
510 OrderBy (s => s.FullName, StringComparer.Ordinal);
512 if (ifaces.Any ()) {
513 writer.WriteStartElement ("interfaces");
514 foreach (TypeReference iface in ifaces) {
515 writer.WriteStartElement ("interface");
516 AddAttribute ("name", Utils.CleanupTypeName (iface));
517 writer.WriteEndElement (); // interface
519 writer.WriteEndElement (); // interfaces
522 MemberData.OutputGenericParameters (writer, type);
524 ArrayList members = new ArrayList ();
526 FieldDefinition [] fields = GetFields (type);
527 if (fields.Length > 0) {
528 Array.Sort (fields, MemberReferenceComparer.Default);
529 FieldData fd = new FieldData (writer, fields);
530 members.Add (fd);
533 if (!Driver.AbiMode) {
535 MethodDefinition [] ctors = GetConstructors (type);
536 if (ctors.Length > 0) {
537 Array.Sort (ctors, MethodDefinitionComparer.Default);
538 members.Add (new ConstructorData (writer, ctors));
541 PropertyDefinition[] properties = GetProperties (type);
542 if (properties.Length > 0) {
543 Array.Sort (properties, PropertyDefinitionComparer.Default);
544 members.Add (new PropertyData (writer, properties));
547 EventDefinition [] events = GetEvents (type);
548 if (events.Length > 0) {
549 Array.Sort (events, MemberReferenceComparer.Default);
550 members.Add (new EventData (writer, events));
553 MethodDefinition [] methods = GetMethods (type);
554 if (methods.Length > 0) {
555 Array.Sort (methods, MethodDefinitionComparer.Default);
556 members.Add (new MethodData (writer, methods));
560 foreach (MemberData md in members)
561 md.DoOutput ();
563 var nested = type.NestedTypes;
564 //remove non public(familiy) and nested in second degree
565 for (int i = nested.Count - 1; i >= 0; i--) {
566 TypeDefinition t = nested [i];
567 if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic ||
568 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily ||
569 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) {
570 // public
571 if (t.DeclaringType == type)
572 continue; // not nested of nested
575 nested.RemoveAt (i);
578 if (nested.Count > 0) {
579 var nestedArray = nested.ToArray ();
580 Array.Sort (nestedArray, TypeReferenceComparer.Default);
582 writer.WriteStartElement ("classes");
583 foreach (TypeDefinition t in nestedArray) {
584 TypeData td = new TypeData (writer, t);
585 td.DoOutput ();
587 writer.WriteEndElement (); // classes
590 writer.WriteEndElement (); // class
593 static FieldReference GetEnumValueField (TypeDefinition type)
595 foreach (FieldDefinition field in type.Fields)
596 if (field.IsSpecialName && field.Name == "value__")
597 return field;
599 return null;
602 protected override string GetMemberAttributes (MemberReference member)
604 if (member != type)
605 throw new InvalidOperationException ("odd");
607 return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
610 public static bool MustDocumentMethod (MethodDefinition method) {
611 // All other methods
612 MethodAttributes maskedAccess = method.Attributes & MethodAttributes.MemberAccessMask;
613 return maskedAccess == MethodAttributes.Public
614 || maskedAccess == MethodAttributes.Family
615 || maskedAccess == MethodAttributes.FamORAssem;
618 static string GetClassType (TypeDefinition t)
620 if (t.IsEnum)
621 return "enum";
623 if (t.IsValueType)
624 return "struct";
626 if (t.IsInterface)
627 return "interface";
629 if (TypeHelper.IsDelegate(t))
630 return "delegate";
632 if (t.IsPointer)
633 return "pointer";
635 return "class";
638 static string GetCharSet (TypeDefinition type)
640 TypeAttributes maskedStringFormat = type.Attributes & TypeAttributes.StringFormatMask;
641 if (maskedStringFormat == TypeAttributes.AnsiClass)
642 return CharSet.Ansi.ToString ();
644 if (maskedStringFormat == TypeAttributes.AutoClass)
645 return CharSet.Auto.ToString ();
647 if (maskedStringFormat == TypeAttributes.UnicodeClass)
648 return CharSet.Unicode.ToString ();
650 return CharSet.None.ToString ();
653 static string GetLayout (TypeDefinition type)
655 TypeAttributes maskedLayout = type.Attributes & TypeAttributes.LayoutMask;
656 if (maskedLayout == TypeAttributes.AutoLayout)
657 return LayoutKind.Auto.ToString ();
659 if (maskedLayout == TypeAttributes.ExplicitLayout)
660 return LayoutKind.Explicit.ToString ();
662 if (maskedLayout == TypeAttributes.SequentialLayout)
663 return LayoutKind.Sequential.ToString ();
665 return null;
668 FieldDefinition [] GetFields (TypeDefinition type) {
669 ArrayList list = new ArrayList ();
671 var fields = type.Fields;
672 foreach (FieldDefinition field in fields) {
673 if (field.IsSpecialName)
674 continue;
676 if (Driver.AbiMode && field.IsStatic)
677 continue;
679 // we're only interested in public or protected members
680 FieldAttributes maskedVisibility = (field.Attributes & FieldAttributes.FieldAccessMask);
681 if (Driver.AbiMode && !field.IsNotSerialized) {
682 list.Add (field);
683 } else {
684 if (maskedVisibility == FieldAttributes.Public
685 || maskedVisibility == FieldAttributes.Family
686 || maskedVisibility == FieldAttributes.FamORAssem) {
687 list.Add (field);
692 return (FieldDefinition []) list.ToArray (typeof (FieldDefinition));
696 internal static PropertyDefinition [] GetProperties (TypeDefinition type) {
697 ArrayList list = new ArrayList ();
699 var properties = type.Properties;//type.GetProperties (flags);
700 foreach (PropertyDefinition property in properties) {
701 MethodDefinition getMethod = property.GetMethod;
702 MethodDefinition setMethod = property.SetMethod;
704 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
705 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
707 // if neither the getter or setter should be documented, then
708 // skip the property
709 if (hasGetter || hasSetter) {
710 list.Add (property);
714 return (PropertyDefinition []) list.ToArray (typeof (PropertyDefinition));
717 private MethodDefinition[] GetMethods (TypeDefinition type)
719 ArrayList list = new ArrayList ();
721 var methods = type.Methods;//type.GetMethods (flags);
722 foreach (MethodDefinition method in methods) {
723 if (method.IsSpecialName && !method.Name.StartsWith ("op_", StringComparison.Ordinal))
724 continue;
726 // we're only interested in public or protected members
727 if (!MustDocumentMethod(method))
728 continue;
730 if (IsFinalizer (method)) {
731 string name = method.DeclaringType.Name;
732 int arity = name.IndexOf ('`');
733 if (arity > 0)
734 name = name.Substring (0, arity);
736 method.Name = "~" + name;
739 list.Add (method);
742 return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
745 static bool IsFinalizer (MethodDefinition method)
747 if (method.Name != "Finalize")
748 return false;
750 if (!method.IsVirtual)
751 return false;
753 if (method.Parameters.Count != 0)
754 return false;
756 return true;
759 private MethodDefinition [] GetConstructors (TypeDefinition type)
761 ArrayList list = new ArrayList ();
763 var ctors = type.Methods.Where (m => m.IsConstructor);//type.GetConstructors (flags);
764 foreach (MethodDefinition constructor in ctors) {
765 // we're only interested in public or protected members
766 if (!MustDocumentMethod(constructor))
767 continue;
769 list.Add (constructor);
772 return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
775 private EventDefinition[] GetEvents (TypeDefinition type)
777 ArrayList list = new ArrayList ();
779 var events = type.Events;//type.GetEvents (flags);
780 foreach (EventDefinition eventDef in events) {
781 MethodDefinition addMethod = eventDef.AddMethod;//eventInfo.GetAddMethod (true);
783 if (addMethod == null || !MustDocumentMethod (addMethod))
784 continue;
786 list.Add (eventDef);
789 return (EventDefinition []) list.ToArray (typeof (EventDefinition));
793 class FieldData : MemberData
795 public FieldData (XmlWriter writer, FieldDefinition [] members)
796 : base (writer, members)
800 protected override string GetName (MemberReference memberDefenition)
802 FieldDefinition field = (FieldDefinition) memberDefenition;
803 return field.Name;
806 protected override string GetMemberAttributes (MemberReference memberDefenition)
808 FieldDefinition field = (FieldDefinition) memberDefenition;
809 return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
812 protected override void AddExtraAttributes (MemberReference memberDefinition)
814 base.AddExtraAttributes (memberDefinition);
816 FieldDefinition field = (FieldDefinition) memberDefinition;
817 AddAttribute ("fieldtype", Utils.CleanupTypeName (field.FieldType));
819 if (field.IsLiteral) {
820 object value = field.Constant;//object value = field.GetValue (null);
821 string stringValue = null;
822 //if (value is Enum) {
823 // // FIXME: when Mono bug #60090 has been
824 // // fixed, we should just be able to use
825 // // Convert.ToString
826 // stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
828 //else {
829 stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
832 if (stringValue != null)
833 AddAttribute ("value", stringValue);
837 public override string ParentTag {
838 get { return "fields"; }
841 public override string Tag {
842 get { return "field"; }
846 class PropertyData : MemberData
848 public PropertyData (XmlWriter writer, PropertyDefinition [] members)
849 : base (writer, members)
853 protected override string GetName (MemberReference memberDefenition)
855 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
856 return prop.Name;
859 MethodDefinition [] GetMethods (PropertyDefinition prop, out bool haveParameters)
861 MethodDefinition _get = prop.GetMethod;
862 MethodDefinition _set = prop.SetMethod;
863 bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
864 bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
865 haveParameters = haveGet || (haveSet && _set.Parameters.Count > 1);
866 MethodDefinition [] methods;
868 if (haveGet && haveSet) {
869 methods = new MethodDefinition [] { _get, _set };
870 } else if (haveGet) {
871 methods = new MethodDefinition [] { _get };
872 } else if (haveSet) {
873 methods = new MethodDefinition [] { _set };
874 } else {
875 //odd
876 return null;
879 return methods;
882 protected override void AddExtraAttributes (MemberReference memberDefinition)
884 base.AddExtraAttributes (memberDefinition);
886 PropertyDefinition prop = (PropertyDefinition) memberDefinition;
887 AddAttribute ("ptype", Utils.CleanupTypeName (prop.PropertyType));
889 bool haveParameters;
890 MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefinition, out haveParameters);
892 if (methods != null && haveParameters) {
893 string parms = Parameters.GetSignature (methods [0].Parameters);
894 if (!string.IsNullOrEmpty (parms))
895 AddAttribute ("params", parms);
900 protected override void AddExtraData (MemberReference memberDefenition)
902 base.AddExtraData (memberDefenition);
904 bool haveParameters;
905 MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefenition, out haveParameters);
907 if (methods == null)
908 return;
910 MethodData data = new MethodData (writer, methods);
911 //data.NoMemberAttributes = true;
912 data.DoOutput ();
915 protected override string GetMemberAttributes (MemberReference memberDefenition)
917 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
918 return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
921 public override string ParentTag {
922 get { return "properties"; }
925 public override string Tag {
926 get { return "property"; }
930 class EventData : MemberData
932 public EventData (XmlWriter writer, EventDefinition [] members)
933 : base (writer, members)
937 protected override string GetName (MemberReference memberDefenition)
939 EventDefinition evt = (EventDefinition) memberDefenition;
940 return evt.Name;
943 protected override string GetMemberAttributes (MemberReference memberDefenition)
945 EventDefinition evt = (EventDefinition) memberDefenition;
946 return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
949 protected override void AddExtraAttributes (MemberReference memberDefinition)
951 base.AddExtraAttributes (memberDefinition);
953 EventDefinition evt = (EventDefinition) memberDefinition;
954 AddAttribute ("eventtype", Utils.CleanupTypeName (evt.EventType));
957 public override string ParentTag {
958 get { return "events"; }
961 public override string Tag {
962 get { return "event"; }
966 class MethodData : MemberData
968 bool noAtts;
970 public MethodData (XmlWriter writer, MethodDefinition [] members)
971 : base (writer, members)
975 protected override string GetName (MemberReference memberDefenition)
977 MethodDefinition method = (MethodDefinition) memberDefenition;
978 string name = method.Name;
979 string parms = Parameters.GetSignature (method.Parameters);
981 return string.Format ("{0}({1})", name, parms);
984 protected override string GetMemberAttributes (MemberReference memberDefenition)
986 MethodDefinition method = (MethodDefinition) memberDefenition;
987 return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture);
990 protected override ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member)
992 var mbase = (MethodDefinition) member;
993 return mbase.MethodReturnType;
996 protected override void AddExtraAttributes (MemberReference memberDefinition)
998 base.AddExtraAttributes (memberDefinition);
1000 if (!(memberDefinition is MethodDefinition))
1001 return;
1003 MethodDefinition mbase = (MethodDefinition) memberDefinition;
1005 if (mbase.IsAbstract)
1006 AddAttribute ("abstract", "true");
1007 if (mbase.IsVirtual)
1008 AddAttribute ("virtual", "true");
1009 if (mbase.IsFinal && mbase.IsVirtual && mbase.IsReuseSlot)
1010 AddAttribute ("sealed", "true");
1011 if (mbase.IsStatic)
1012 AddAttribute ("static", "true");
1013 string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType);
1014 if (rettype != "System.Void" || !mbase.IsConstructor)
1015 AddAttribute ("returntype", (rettype));
1017 // if (mbase.MethodReturnType.HasCustomAttributes)
1018 // AttributeData.OutputAttributes (writer, mbase.MethodReturnType);
1021 protected override void AddExtraData (MemberReference memberDefenition)
1023 base.AddExtraData (memberDefenition);
1025 if (!(memberDefenition is MethodDefinition))
1026 return;
1028 MethodDefinition mbase = (MethodDefinition) memberDefenition;
1030 ParameterData parms = new ParameterData (writer, mbase.Parameters);
1031 parms.DoOutput ();
1033 MemberData.OutputGenericParameters (writer, mbase);
1036 public override bool NoMemberAttributes {
1037 get { return noAtts; }
1038 set { noAtts = value; }
1041 public override string ParentTag {
1042 get { return "methods"; }
1045 public override string Tag {
1046 get { return "method"; }
1050 class ConstructorData : MethodData
1052 public ConstructorData (XmlWriter writer, MethodDefinition [] members)
1053 : base (writer, members)
1057 public override string ParentTag {
1058 get { return "constructors"; }
1061 public override string Tag {
1062 get { return "constructor"; }
1066 class ParameterData : BaseData
1068 private IList<ParameterDefinition> parameters;
1070 public ParameterData (XmlWriter writer, IList<ParameterDefinition> parameters)
1071 : base (writer)
1073 this.parameters = parameters;
1076 public override void DoOutput ()
1078 writer.WriteStartElement ("parameters");
1079 foreach (ParameterDefinition parameter in parameters) {
1080 writer.WriteStartElement ("parameter");
1081 AddAttribute ("name", parameter.Name);
1082 AddAttribute ("position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture));
1083 AddAttribute ("attrib", ((int) parameter.Attributes).ToString());
1085 string direction = "in";
1087 if (parameter.ParameterType is ByReferenceType)
1088 direction = parameter.IsOut ? "out" : "ref";
1090 TypeReference t = parameter.ParameterType;
1091 AddAttribute ("type", Utils.CleanupTypeName (t));
1093 if (parameter.IsOptional) {
1094 AddAttribute ("optional", "true");
1095 if (parameter.HasConstant)
1096 AddAttribute ("defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ());
1099 if (direction != "in")
1100 AddAttribute ("direction", direction);
1102 AttributeData.OutputAttributes (writer, parameter);
1103 writer.WriteEndElement (); // parameter
1105 writer.WriteEndElement (); // parameters
1109 class AttributeData
1111 public static void DoOutput (XmlWriter writer, IList<ICustomAttributeProvider> providers)
1113 if (writer == null)
1114 throw new InvalidOperationException ("Document not set");
1116 if (providers == null || providers.Count == 0)
1117 return;
1119 if (!providers.Any ((provider) => provider != null && provider.HasCustomAttributes))
1120 return;
1122 writer.WriteStartElement ("attributes");
1124 foreach (var provider in providers) {
1125 if (provider == null)
1126 continue;
1128 if (!provider.HasCustomAttributes)
1129 continue;
1132 var ass = provider as AssemblyDefinition;
1133 if (ass != null && !Driver.FollowForwarders)
1134 TypeForwardedToData.OutputForwarders (writer, ass);
1136 var attributes = provider.CustomAttributes.
1137 Where ((att) => !SkipAttribute (att)).
1138 OrderBy ((a) => a.Constructor.DeclaringType.FullName, StringComparer.Ordinal);
1140 foreach (var att in attributes) {
1141 string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType);
1143 writer.WriteStartElement ("attribute");
1144 writer.WriteAttributeString ("name", attName);
1146 var attribute_mapping = CreateAttributeMapping (att);
1148 if (attribute_mapping != null) {
1149 var mapping = attribute_mapping.Where ((attr) => attr.Key != "TypeId");
1150 if (mapping.Any ()) {
1151 writer.WriteStartElement ("properties");
1152 foreach (var kvp in mapping) {
1153 string name = kvp.Key;
1154 object o = kvp.Value;
1156 writer.WriteStartElement ("property");
1157 writer.WriteAttributeString ("name", name);
1159 if (o == null) {
1160 writer.WriteAttributeString ("value", "null");
1161 } else {
1162 string value = o.ToString ();
1163 if (attName.EndsWith ("GuidAttribute", StringComparison.Ordinal))
1164 value = value.ToUpper ();
1165 writer.WriteAttributeString ("value", value);
1168 writer.WriteEndElement (); // property
1170 writer.WriteEndElement (); // properties
1173 writer.WriteEndElement (); // attribute
1177 writer.WriteEndElement (); // attributes
1180 static Dictionary<string, object> CreateAttributeMapping (CustomAttribute attribute)
1182 Dictionary<string, object> mapping = null;
1184 PopulateMapping (ref mapping, attribute);
1186 var constructor = attribute.Constructor.Resolve ();
1187 if (constructor == null || !constructor.HasParameters)
1188 return mapping;
1190 PopulateMapping (ref mapping, constructor, attribute);
1192 return mapping;
1195 static void PopulateMapping (ref Dictionary<string, object> mapping, CustomAttribute attribute)
1197 if (!attribute.HasProperties)
1198 return;
1200 foreach (var named_argument in attribute.Properties) {
1201 var name = named_argument.Name;
1202 var arg = named_argument.Argument;
1204 if (arg.Value is CustomAttributeArgument)
1205 arg = (CustomAttributeArgument) arg.Value;
1207 if (mapping == null)
1208 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1209 mapping.Add (name, GetArgumentValue (arg.Type, arg.Value));
1213 static Dictionary<FieldReference, int> CreateArgumentFieldMapping (MethodDefinition constructor)
1215 Dictionary<FieldReference, int> field_mapping = null;
1217 int? argument = null;
1219 foreach (Instruction instruction in constructor.Body.Instructions) {
1220 switch (instruction.OpCode.Code) {
1221 case Code.Ldarg_1:
1222 argument = 1;
1223 break;
1224 case Code.Ldarg_2:
1225 argument = 2;
1226 break;
1227 case Code.Ldarg_3:
1228 argument = 3;
1229 break;
1230 case Code.Ldarg:
1231 case Code.Ldarg_S:
1232 argument = ((ParameterDefinition) instruction.Operand).Index + 1;
1233 break;
1235 case Code.Stfld:
1236 FieldReference field = (FieldReference) instruction.Operand;
1237 if (field.DeclaringType.FullName != constructor.DeclaringType.FullName)
1238 continue;
1240 if (!argument.HasValue)
1241 break;
1243 if (field_mapping == null)
1244 field_mapping = new Dictionary<FieldReference, int> ();
1246 if (!field_mapping.ContainsKey (field))
1247 field_mapping.Add (field, (int) argument - 1);
1249 argument = null;
1250 break;
1254 return field_mapping;
1257 static Dictionary<PropertyDefinition, FieldReference> CreatePropertyFieldMapping (TypeDefinition type)
1259 Dictionary<PropertyDefinition, FieldReference> property_mapping = null;
1261 foreach (PropertyDefinition property in type.Properties) {
1262 if (property.GetMethod == null)
1263 continue;
1264 if (!property.GetMethod.HasBody)
1265 continue;
1267 foreach (Instruction instruction in property.GetMethod.Body.Instructions) {
1268 if (instruction.OpCode.Code != Code.Ldfld)
1269 continue;
1271 FieldReference field = (FieldReference) instruction.Operand;
1272 if (field.DeclaringType.FullName != type.FullName)
1273 continue;
1275 if (property_mapping == null)
1276 property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
1277 property_mapping.Add (property, field);
1278 break;
1282 return property_mapping;
1285 static void PopulateMapping (ref Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
1287 if (!constructor.HasBody)
1288 return;
1290 // Custom handling for attributes with arguments which cannot be easily extracted
1291 var ca = attribute.ConstructorArguments;
1292 switch (constructor.DeclaringType.FullName) {
1293 case "System.Runtime.CompilerServices.DecimalConstantAttribute":
1294 var dca = constructor.Parameters[2].ParameterType == constructor.Module.TypeSystem.Int32 ?
1295 new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (int) ca[2].Value, (int) ca[3].Value, (int) ca[4].Value) :
1296 new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (uint) ca[2].Value, (uint) ca[3].Value, (uint) ca[4].Value);
1298 if (mapping == null)
1299 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1300 mapping.Add ("Value", dca.Value);
1301 return;
1302 case "System.ComponentModel.BindableAttribute":
1303 if (ca.Count != 1)
1304 break;
1306 if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) {
1307 if (mapping == null)
1308 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1309 mapping.Add ("Bindable", ca[0].Value);
1310 } else {
1311 throw new NotImplementedException ();
1314 return;
1317 var field_mapping = CreateArgumentFieldMapping (constructor);
1318 if (field_mapping != null) {
1319 var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
1321 if (property_mapping != null) {
1322 foreach (var pair in property_mapping) {
1323 int argument;
1324 if (!field_mapping.TryGetValue (pair.Value, out argument))
1325 continue;
1327 var ca_arg = ca [argument];
1328 if (ca_arg.Value is CustomAttributeArgument)
1329 ca_arg = (CustomAttributeArgument)ca_arg.Value;
1331 if (mapping == null)
1332 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1333 mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value));
1339 static object GetArgumentValue (TypeReference reference, object value)
1341 var type = reference.Resolve ();
1342 if (type == null)
1343 return value;
1345 if (type.IsEnum) {
1346 if (IsFlaggedEnum (type))
1347 return GetFlaggedEnumValue (type, value);
1349 return GetEnumValue (type, value);
1352 return value;
1355 static bool IsFlaggedEnum (TypeDefinition type)
1357 if (!type.IsEnum)
1358 return false;
1360 if (!type.HasCustomAttributes)
1361 return false;
1363 foreach (CustomAttribute attribute in type.CustomAttributes)
1364 if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute")
1365 return true;
1367 return false;
1370 static object GetFlaggedEnumValue (TypeDefinition type, object value)
1372 if (value is ulong)
1373 return GetFlaggedEnumValue (type, (ulong)value);
1375 long flags = Convert.ToInt64 (value);
1376 var signature = new StringBuilder ();
1378 for (int i = type.Fields.Count - 1; i >= 0; i--) {
1379 FieldDefinition field = type.Fields [i];
1381 if (!field.HasConstant)
1382 continue;
1384 long flag = Convert.ToInt64 (field.Constant);
1386 if (flag == 0)
1387 continue;
1389 if ((flags & flag) == flag) {
1390 if (signature.Length != 0)
1391 signature.Append (", ");
1393 signature.Append (field.Name);
1394 flags -= flag;
1398 return signature.ToString ();
1401 static object GetFlaggedEnumValue (TypeDefinition type, ulong flags)
1403 var signature = new StringBuilder ();
1405 for (int i = type.Fields.Count - 1; i >= 0; i--) {
1406 FieldDefinition field = type.Fields [i];
1408 if (!field.HasConstant)
1409 continue;
1411 ulong flag = Convert.ToUInt64 (field.Constant);
1413 if (flag == 0)
1414 continue;
1416 if ((flags & flag) == flag) {
1417 if (signature.Length != 0)
1418 signature.Append (", ");
1420 signature.Append (field.Name);
1421 flags -= flag;
1425 return signature.ToString ();
1428 static object GetEnumValue (TypeDefinition type, object value)
1430 foreach (FieldDefinition field in type.Fields) {
1431 if (!field.HasConstant)
1432 continue;
1434 if (Comparer.Default.Compare (field.Constant, value) == 0)
1435 return field.Name;
1438 return value;
1441 static bool SkipAttribute (CustomAttribute attribute)
1443 if (!TypeHelper.IsPublic (attribute))
1444 return true;
1446 return attribute.Constructor.DeclaringType.Name.EndsWith ("TODOAttribute", StringComparison.Ordinal);
1449 public static void OutputAttributes (XmlWriter writer, params ICustomAttributeProvider[] providers)
1451 AttributeData.DoOutput (writer, providers);
1455 static class Parameters {
1457 public static string GetSignature (IList<ParameterDefinition> infos)
1459 if (infos == null || infos.Count == 0)
1460 return string.Empty;
1462 var signature = new StringBuilder ();
1463 for (int i = 0; i < infos.Count; i++) {
1465 if (i > 0)
1466 signature.Append (", ");
1468 ParameterDefinition info = infos [i];
1470 string modifier;
1471 if ((info.Attributes & ParameterAttributes.In) != 0)
1472 modifier = "in";
1473 else if ((info.Attributes & ParameterAttributes.Out) != 0)
1474 modifier = "out";
1475 else
1476 modifier = string.Empty;
1478 if (modifier.Length > 0) {
1479 signature.Append (modifier);
1480 signature.Append (" ");
1483 signature.Append (Utils.CleanupTypeName (info.ParameterType));
1486 return signature.ToString ();
1491 class TypeReferenceComparer : IComparer<TypeReference>
1493 public static TypeReferenceComparer Default = new TypeReferenceComparer ();
1495 public int Compare (TypeReference a, TypeReference b)
1497 int result = String.Compare (a.Namespace, b.Namespace, StringComparison.Ordinal);
1498 if (result != 0)
1499 return result;
1501 return String.Compare (a.Name, b.Name, StringComparison.Ordinal);
1505 class MemberReferenceComparer : IComparer
1507 public static MemberReferenceComparer Default = new MemberReferenceComparer ();
1509 public int Compare (object a, object b)
1511 MemberReference ma = (MemberReference) a;
1512 MemberReference mb = (MemberReference) b;
1513 return String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1517 class PropertyDefinitionComparer : IComparer<PropertyDefinition>
1519 public static PropertyDefinitionComparer Default = new PropertyDefinitionComparer ();
1521 public int Compare (PropertyDefinition ma, PropertyDefinition mb)
1523 int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1524 if (res != 0)
1525 return res;
1527 if (!ma.HasParameters && !mb.HasParameters)
1528 return 0;
1530 if (!ma.HasParameters)
1531 return -1;
1533 if (!mb.HasParameters)
1534 return 1;
1536 return MethodDefinitionComparer.Compare (ma.Parameters, mb.Parameters);
1540 class MethodDefinitionComparer : IComparer
1542 public static MethodDefinitionComparer Default = new MethodDefinitionComparer ();
1544 public int Compare (object a, object b)
1546 MethodDefinition ma = (MethodDefinition) a;
1547 MethodDefinition mb = (MethodDefinition) b;
1548 int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1549 if (res != 0)
1550 return res;
1552 if (!ma.HasParameters && !mb.HasParameters)
1553 return 0;
1555 if (!ma.HasParameters)
1556 return -1;
1558 if (!mb.HasParameters)
1559 return 1;
1561 res = Compare (ma.Parameters, mb.Parameters);
1562 if (res != 0)
1563 return res;
1565 // operators can differ by only return type
1566 return string.CompareOrdinal (ma.ReturnType.FullName, mb.ReturnType.FullName);
1569 public static int Compare (IList<ParameterDefinition> pia, IList<ParameterDefinition> pib)
1571 var res = pia.Count - pib.Count;
1572 if (res != 0)
1573 return res;
1575 string siga = Parameters.GetSignature (pia);
1576 string sigb = Parameters.GetSignature (pib);
1577 return String.Compare (siga, sigb, StringComparison.Ordinal);