2 // mono-api-info.cs - Dumps public assembly information to an xml file.
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Copyright (C) 2003-2008 Novell, Inc (http://www.novell.com)
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Globalization;
15 using System.Runtime.CompilerServices;
16 using System.Runtime.InteropServices;
17 using System.Security.Permissions;
29 public static int Main (string [] args)
31 bool showHelp = false;
33 FollowForwarders = false;
36 var acoll = new AssemblyCollection ();
38 var options = new Mono.Options.OptionSet {
39 "usage: mono-api-info [OPTIONS+] ASSEMBLY+",
41 "Expose IL structure of CLR assemblies as XML.",
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) },
54 "Read and register the file {ASSEMBLY}, and add the directory containing ASSEMBLY to the search path.",
55 v => TypeHelper.Resolver.ResolveFile (v) },
57 "The output file. If not specified the output will be written to stdout.",
60 "Show this message and exit.",
61 v => showHelp = v != null },
63 "Produces contract API with all members at each level of inheritance hierarchy",
64 v => FullAPISet = v != null },
67 var asms = options.Parse (args);
69 if (showHelp || asms.Count == 0) {
70 options.WriteOptionDescriptions (Console.Out);
72 return showHelp? 0 :1;
75 string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
76 string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
77 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"assembly\GAC\MSDATASRC\7.0.3300.0__b03f5f7f11d50a3a"));
79 foreach (string arg in asms) {
82 if (arg.Contains ("v3.0")) {
83 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
84 } else if (arg.Contains ("v3.5")) {
85 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
86 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v3.0\Windows Communication Foundation"));
87 } else if (arg.Contains ("v4.0")) {
88 if (arg.Contains ("Silverlight")) {
89 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (pf, @"Microsoft Silverlight\4.0.51204.0"));
91 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319"));
92 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319\WPF"));
95 TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg));
99 StreamWriter outputStream = null;
100 if (!string.IsNullOrEmpty (output))
101 outputStream = new StreamWriter (output);
103 TextWriter outStream = outputStream ?? Console.Out;
104 var settings = new XmlWriterSettings ();
105 settings.Indent = true;
106 var textWriter = XmlWriter.Create (outStream, settings);
107 var writer = new WellFormedXmlWriter (textWriter);
108 writer.WriteStartDocument ();
109 acoll.Writer = writer;
111 writer.WriteEndDocument ();
114 if (outputStream != null)
115 outputStream.Dispose ();
120 internal static bool AbiMode { get; private set; }
121 internal static bool FollowForwarders { get; private set; }
122 internal static bool FullAPISet { get; set; }
126 static char[] CharsToCleanup = new char[] { '<', '>', '/' };
128 public static string CleanupTypeName (TypeReference type)
130 return CleanupTypeName (type.FullName);
133 public static string CleanupTypeName (string t)
135 if (t.IndexOfAny (CharsToCleanup) == -1)
137 var sb = new StringBuilder (t.Length);
138 for (int i = 0; i < t.Length; i++) {
155 return sb.ToString ();
159 class AssemblyCollection
162 List<AssemblyDefinition> assemblies = new List<AssemblyDefinition> ();
164 public AssemblyCollection ()
168 public bool Add (string name)
170 AssemblyDefinition ass = LoadAssembly (name);
172 Console.Error.WriteLine ("Cannot load assembly file " + name);
176 assemblies.Add (ass);
180 public void DoOutput ()
183 throw new InvalidOperationException ("Document not set");
185 writer.WriteStartElement ("assemblies");
186 foreach (AssemblyDefinition a in assemblies) {
187 AssemblyData data = new AssemblyData (writer, a);
190 writer.WriteEndElement ();
193 public XmlWriter Writer {
194 set { writer = value; }
197 AssemblyDefinition LoadAssembly (string assembly)
200 if (File.Exists (assembly))
201 return TypeHelper.Resolver.ResolveFile (assembly);
203 return TypeHelper.Resolver.Resolve (assembly);
204 } catch (Exception e) {
205 Console.WriteLine (e);
211 abstract class BaseData
213 protected XmlWriter writer;
215 protected BaseData (XmlWriter writer)
217 this.writer = writer;
220 public abstract void DoOutput ();
222 protected void AddAttribute (string name, string value)
224 writer.WriteAttributeString (name, value);
228 class TypeForwardedToData : BaseData
230 AssemblyDefinition ass;
232 public TypeForwardedToData (XmlWriter writer, AssemblyDefinition ass)
238 public override void DoOutput ()
240 foreach (ExportedType type in ass.MainModule.ExportedTypes) {
242 if (((uint)type.Attributes & 0x200000u) == 0)
245 writer.WriteStartElement ("attribute");
246 AddAttribute ("name", typeof (TypeForwardedToAttribute).FullName);
247 writer.WriteStartElement ("properties");
248 writer.WriteStartElement ("property");
249 AddAttribute ("name", "Destination");
250 AddAttribute ("value", Utils.CleanupTypeName (type.FullName));
251 writer.WriteEndElement (); // properties
252 writer.WriteEndElement (); // properties
253 writer.WriteEndElement (); // attribute
257 public static void OutputForwarders (XmlWriter writer, AssemblyDefinition ass)
259 TypeForwardedToData tftd = new TypeForwardedToData (writer, ass);
264 class AssemblyData : BaseData
266 AssemblyDefinition ass;
268 public AssemblyData (XmlWriter writer, AssemblyDefinition ass)
274 public override void DoOutput ()
277 throw new InvalidOperationException ("Document not set");
279 writer.WriteStartElement ("assembly");
280 AssemblyNameDefinition aname = ass.Name;
281 AddAttribute ("name", aname.Name);
282 AddAttribute ("version", aname.Version.ToString ());
284 AttributeData.OutputAttributes (writer, ass);
286 var types = new List<TypeDefinition> ();
287 if (ass.MainModule.Types != null) {
288 types.AddRange (ass.MainModule.Types);
291 if (Driver.FollowForwarders && ass.MainModule.ExportedTypes != null) {
292 foreach (var t in ass.MainModule.ExportedTypes) {
293 var forwarded = t.Resolve ();
294 if (forwarded == null) {
295 throw new Exception ("Could not resolve forwarded type " + t.FullName + " in " + ass.Name);
297 types.Add (forwarded);
301 if (types.Count == 0) {
302 writer.WriteEndElement (); // assembly
306 types.Sort (TypeReferenceComparer.Default);
308 writer.WriteStartElement ("namespaces");
310 string current_namespace = "$%&$&";
311 bool in_namespace = false;
312 foreach (TypeDefinition t in types) {
313 if (string.IsNullOrEmpty (t.Namespace))
316 if (!Driver.AbiMode && ((t.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public))
319 if (t.DeclaringType != null)
320 continue; // enforce !nested
322 if (t.Namespace != current_namespace) {
323 current_namespace = t.Namespace;
325 writer.WriteEndElement (); // classes
326 writer.WriteEndElement (); // namespace
330 writer.WriteStartElement ("namespace");
331 AddAttribute ("name", current_namespace);
332 writer.WriteStartElement ("classes");
335 TypeData bd = new TypeData (writer, t);
341 writer.WriteEndElement (); // classes
342 writer.WriteEndElement (); // namespace
345 writer.WriteEndElement (); // namespaces
347 writer.WriteEndElement (); // assembly
351 abstract class MemberData : BaseData
353 MemberReference [] members;
355 public MemberData (XmlWriter writer, MemberReference [] members)
358 this.members = members;
361 protected virtual ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member)
366 public override void DoOutput ()
368 writer.WriteStartElement (ParentTag);
370 foreach (MemberReference member in members) {
371 writer.WriteStartElement (Tag);
372 AddAttribute ("name", GetName (member));
373 if (!NoMemberAttributes)
374 AddAttribute ("attrib", GetMemberAttributes (member));
375 AddExtraAttributes (member);
377 AttributeData.OutputAttributes (writer, (ICustomAttributeProvider) member, GetAdditionalCustomAttributeProvider (member));
379 AddExtraData (member);
380 writer.WriteEndElement (); // Tag
383 writer.WriteEndElement (); // ParentTag
386 protected virtual void AddExtraData (MemberReference memberDefenition)
390 protected virtual void AddExtraAttributes (MemberReference memberDefinition)
394 protected virtual string GetName (MemberReference memberDefenition)
399 protected virtual string GetMemberAttributes (MemberReference memberDefenition)
404 public virtual bool NoMemberAttributes {
405 get { return false; }
409 public virtual string ParentTag {
410 get { return "NoPARENTTAG"; }
413 public virtual string Tag {
414 get { return "NoTAG"; }
417 public static void OutputGenericParameters (XmlWriter writer, IGenericParameterProvider provider)
419 if (provider.GenericParameters.Count == 0)
422 var gparameters = provider.GenericParameters;
424 writer.WriteStartElement ("generic-parameters");
426 foreach (GenericParameter gp in gparameters) {
427 writer.WriteStartElement ("generic-parameter");
428 writer.WriteAttributeString ("name", gp.Name);
429 writer.WriteAttributeString ("attributes", ((int) gp.Attributes).ToString ());
431 AttributeData.OutputAttributes (writer, gp);
433 var constraints = gp.Constraints;
434 if (constraints.Count == 0) {
435 writer.WriteEndElement (); // generic-parameter
439 writer.WriteStartElement ("generic-parameter-constraints");
441 foreach (TypeReference constraint in constraints) {
442 writer.WriteStartElement ("generic-parameter-constraint");
443 writer.WriteAttributeString ("name", Utils.CleanupTypeName (constraint));
444 writer.WriteEndElement (); // generic-parameter-constraint
447 writer.WriteEndElement (); // generic-parameter-constraints
449 writer.WriteEndElement (); // generic-parameter
452 writer.WriteEndElement (); // generic-parameters
456 class TypeData : MemberData
460 public TypeData (XmlWriter writer, TypeDefinition type)
461 : base (writer, null)
465 public override void DoOutput ()
468 throw new InvalidOperationException ("Document not set");
470 writer.WriteStartElement ("class");
471 AddAttribute ("name", type.Name);
472 string classType = GetClassType (type);
473 AddAttribute ("type", classType);
475 if (type.BaseType != null)
476 AddAttribute ("base", Utils.CleanupTypeName (type.BaseType));
479 AddAttribute ("sealed", "true");
482 AddAttribute ("abstract", "true");
484 if ( (type.Attributes & TypeAttributes.Serializable) != 0 || type.IsEnum)
485 AddAttribute ("serializable", "true");
487 string charSet = GetCharSet (type);
488 AddAttribute ("charset", charSet);
490 string layout = GetLayout (type);
492 AddAttribute ("layout", layout);
494 if (type.PackingSize >= 0) {
495 AddAttribute ("pack", type.PackingSize.ToString ());
498 if (type.ClassSize >= 0) {
499 AddAttribute ("size", type.ClassSize.ToString ());
503 var value_type = GetEnumValueField (type);
504 if (value_type == null)
505 throw new NotSupportedException ();
507 AddAttribute ("enumtype", Utils.CleanupTypeName (value_type.FieldType));
510 AttributeData.OutputAttributes (writer, type);
512 var ifaces = TypeHelper.GetInterfaces (type).
513 Where ((iface) => TypeHelper.IsPublic (iface)). // we're only interested in public interfaces
514 OrderBy (s => s.FullName, StringComparer.Ordinal);
517 writer.WriteStartElement ("interfaces");
518 foreach (TypeReference iface in ifaces) {
519 writer.WriteStartElement ("interface");
520 AddAttribute ("name", Utils.CleanupTypeName (iface));
521 writer.WriteEndElement (); // interface
523 writer.WriteEndElement (); // interfaces
526 MemberData.OutputGenericParameters (writer, type);
528 ArrayList members = new ArrayList ();
530 FieldDefinition [] fields = GetFields (type);
531 if (fields.Length > 0) {
532 Array.Sort (fields, MemberReferenceComparer.Default);
533 FieldData fd = new FieldData (writer, fields);
537 if (!Driver.AbiMode) {
539 MethodDefinition [] ctors = GetConstructors (type);
540 if (ctors.Length > 0) {
541 Array.Sort (ctors, MethodDefinitionComparer.Default);
542 members.Add (new ConstructorData (writer, ctors));
545 PropertyDefinition[] properties = GetProperties (type, Driver.FullAPISet);
546 if (properties.Length > 0) {
547 Array.Sort (properties, PropertyDefinitionComparer.Default);
548 members.Add (new PropertyData (writer, properties));
551 EventDefinition [] events = GetEvents (type);
552 if (events.Length > 0) {
553 Array.Sort (events, MemberReferenceComparer.Default);
554 members.Add (new EventData (writer, events));
557 MethodDefinition [] methods = GetMethods (type, Driver.FullAPISet);
558 if (methods.Length > 0) {
559 Array.Sort (methods, MethodDefinitionComparer.Default);
560 members.Add (new MethodData (writer, methods));
564 foreach (MemberData md in members)
567 var nested = type.NestedTypes;
568 //remove non public(familiy) and nested in second degree
569 for (int i = nested.Count - 1; i >= 0; i--) {
570 TypeDefinition t = nested [i];
571 if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic ||
572 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily ||
573 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) {
575 if (t.DeclaringType == type)
576 continue; // not nested of nested
582 if (nested.Count > 0) {
583 var nestedArray = nested.ToArray ();
584 Array.Sort (nestedArray, TypeReferenceComparer.Default);
586 writer.WriteStartElement ("classes");
587 foreach (TypeDefinition t in nestedArray) {
588 TypeData td = new TypeData (writer, t);
591 writer.WriteEndElement (); // classes
594 writer.WriteEndElement (); // class
597 static FieldReference GetEnumValueField (TypeDefinition type)
599 foreach (FieldDefinition field in type.Fields)
600 if (field.IsSpecialName && field.Name == "value__")
606 protected override string GetMemberAttributes (MemberReference member)
609 throw new InvalidOperationException ("odd");
611 return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
614 public static bool MustDocumentMethod (MethodDefinition method) {
616 MethodAttributes maskedAccess = method.Attributes & MethodAttributes.MemberAccessMask;
617 return maskedAccess == MethodAttributes.Public
618 || maskedAccess == MethodAttributes.Family
619 || maskedAccess == MethodAttributes.FamORAssem;
622 static string GetClassType (TypeDefinition t)
633 if (TypeHelper.IsDelegate(t))
642 static string GetCharSet (TypeDefinition type)
644 TypeAttributes maskedStringFormat = type.Attributes & TypeAttributes.StringFormatMask;
645 if (maskedStringFormat == TypeAttributes.AnsiClass)
646 return CharSet.Ansi.ToString ();
648 if (maskedStringFormat == TypeAttributes.AutoClass)
649 return CharSet.Auto.ToString ();
651 if (maskedStringFormat == TypeAttributes.UnicodeClass)
652 return CharSet.Unicode.ToString ();
654 return CharSet.None.ToString ();
657 static string GetLayout (TypeDefinition type)
659 TypeAttributes maskedLayout = type.Attributes & TypeAttributes.LayoutMask;
660 if (maskedLayout == TypeAttributes.AutoLayout)
661 return LayoutKind.Auto.ToString ();
663 if (maskedLayout == TypeAttributes.ExplicitLayout)
664 return LayoutKind.Explicit.ToString ();
666 if (maskedLayout == TypeAttributes.SequentialLayout)
667 return LayoutKind.Sequential.ToString ();
672 FieldDefinition [] GetFields (TypeDefinition type) {
673 ArrayList list = new ArrayList ();
675 var fields = type.Fields;
676 foreach (FieldDefinition field in fields) {
677 if (field.IsSpecialName)
680 if (Driver.AbiMode && field.IsStatic)
683 // we're only interested in public or protected members
684 FieldAttributes maskedVisibility = (field.Attributes & FieldAttributes.FieldAccessMask);
685 if (Driver.AbiMode && !field.IsNotSerialized) {
688 if (maskedVisibility == FieldAttributes.Public
689 || maskedVisibility == FieldAttributes.Family
690 || maskedVisibility == FieldAttributes.FamORAssem) {
696 return (FieldDefinition []) list.ToArray (typeof (FieldDefinition));
700 internal static PropertyDefinition [] GetProperties (TypeDefinition type, bool fullAPI) {
701 var list = new List<PropertyDefinition> ();
705 var properties = t.Properties;//type.GetProperties (flags);
706 foreach (PropertyDefinition property in properties) {
707 MethodDefinition getMethod = property.GetMethod;
708 MethodDefinition setMethod = property.SetMethod;
710 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
711 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
713 // if neither the getter or setter should be documented, then
715 if (hasGetter || hasSetter) {
717 if (t != type && list.Any (l => l.Name == property.Name))
727 if (t.IsInterface || t.IsEnum)
730 if (t.BaseType == null || t.BaseType.FullName == "System.Object")
733 t = t.BaseType.Resolve ();
737 return list.ToArray ();
740 private MethodDefinition[] GetMethods (TypeDefinition type, bool fullAPI)
742 var list = new List<MethodDefinition> ();
746 var methods = t.Methods;//type.GetMethods (flags);
747 foreach (MethodDefinition method in methods) {
748 if (method.IsSpecialName && !method.Name.StartsWith ("op_", StringComparison.Ordinal))
751 // we're only interested in public or protected members
752 if (!MustDocumentMethod (method))
755 if (t == type && IsFinalizer (method)) {
756 string name = method.DeclaringType.Name;
757 int arity = name.IndexOf ('`');
759 name = name.Substring (0, arity);
761 method.Name = "~" + name;
764 if (t != type && list.Any (l => l.DeclaringType != method.DeclaringType && l.Name == method.Name && l.Parameters.Count == method.Parameters.Count &&
765 l.Parameters.SequenceEqual (method.Parameters, new ParameterComparer ())))
774 if (t.IsInterface || t.IsEnum)
777 if (t.BaseType == null || t.BaseType.FullName == "System.Object")
780 t = t.BaseType.Resolve ();
784 return list.ToArray ();
787 sealed class ParameterComparer : IEqualityComparer<ParameterDefinition>
789 public bool Equals (ParameterDefinition x, ParameterDefinition y)
791 return x.ParameterType.Name == y.ParameterType.Name;
794 public int GetHashCode (ParameterDefinition obj)
796 return obj.ParameterType.Name.GetHashCode ();
800 static bool IsFinalizer (MethodDefinition method)
802 if (method.Name != "Finalize")
805 if (!method.IsVirtual)
808 if (method.Parameters.Count != 0)
814 private MethodDefinition [] GetConstructors (TypeDefinition type)
816 ArrayList list = new ArrayList ();
818 var ctors = type.Methods.Where (m => m.IsConstructor);//type.GetConstructors (flags);
819 foreach (MethodDefinition constructor in ctors) {
820 // we're only interested in public or protected members
821 if (!MustDocumentMethod(constructor))
824 list.Add (constructor);
827 return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
830 private EventDefinition[] GetEvents (TypeDefinition type)
832 ArrayList list = new ArrayList ();
834 var events = type.Events;//type.GetEvents (flags);
835 foreach (EventDefinition eventDef in events) {
836 MethodDefinition addMethod = eventDef.AddMethod;//eventInfo.GetAddMethod (true);
838 if (addMethod == null || !MustDocumentMethod (addMethod))
844 return (EventDefinition []) list.ToArray (typeof (EventDefinition));
848 class FieldData : MemberData
850 public FieldData (XmlWriter writer, FieldDefinition [] members)
851 : base (writer, members)
855 protected override string GetName (MemberReference memberDefenition)
857 FieldDefinition field = (FieldDefinition) memberDefenition;
861 protected override string GetMemberAttributes (MemberReference memberDefenition)
863 FieldDefinition field = (FieldDefinition) memberDefenition;
864 return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
867 protected override void AddExtraAttributes (MemberReference memberDefinition)
869 base.AddExtraAttributes (memberDefinition);
871 FieldDefinition field = (FieldDefinition) memberDefinition;
872 AddAttribute ("fieldtype", Utils.CleanupTypeName (field.FieldType));
874 if (field.IsLiteral) {
875 object value = field.Constant;//object value = field.GetValue (null);
876 string stringValue = null;
877 //if (value is Enum) {
878 // // FIXME: when Mono bug #60090 has been
879 // // fixed, we should just be able to use
880 // // Convert.ToString
881 // stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
884 stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
887 if (stringValue != null)
888 AddAttribute ("value", stringValue);
892 public override string ParentTag {
893 get { return "fields"; }
896 public override string Tag {
897 get { return "field"; }
901 class PropertyData : MemberData
903 public PropertyData (XmlWriter writer, PropertyDefinition [] members)
904 : base (writer, members)
908 protected override string GetName (MemberReference memberDefenition)
910 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
914 MethodDefinition [] GetMethods (PropertyDefinition prop, out bool haveParameters)
916 MethodDefinition _get = prop.GetMethod;
917 MethodDefinition _set = prop.SetMethod;
918 bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
919 bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
920 haveParameters = haveGet || (haveSet && _set.Parameters.Count > 1);
921 MethodDefinition [] methods;
923 if (haveGet && haveSet) {
924 methods = new MethodDefinition [] { _get, _set };
925 } else if (haveGet) {
926 methods = new MethodDefinition [] { _get };
927 } else if (haveSet) {
928 methods = new MethodDefinition [] { _set };
937 protected override void AddExtraAttributes (MemberReference memberDefinition)
939 base.AddExtraAttributes (memberDefinition);
941 PropertyDefinition prop = (PropertyDefinition) memberDefinition;
942 AddAttribute ("ptype", Utils.CleanupTypeName (prop.PropertyType));
945 MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefinition, out haveParameters);
947 if (methods != null && haveParameters) {
948 string parms = Parameters.GetSignature (methods [0].Parameters);
949 if (!string.IsNullOrEmpty (parms))
950 AddAttribute ("params", parms);
955 protected override void AddExtraData (MemberReference memberDefenition)
957 base.AddExtraData (memberDefenition);
960 MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefenition, out haveParameters);
965 MethodData data = new MethodData (writer, methods);
966 //data.NoMemberAttributes = true;
970 protected override string GetMemberAttributes (MemberReference memberDefenition)
972 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
973 return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
976 public override string ParentTag {
977 get { return "properties"; }
980 public override string Tag {
981 get { return "property"; }
985 class EventData : MemberData
987 public EventData (XmlWriter writer, EventDefinition [] members)
988 : base (writer, members)
992 protected override string GetName (MemberReference memberDefenition)
994 EventDefinition evt = (EventDefinition) memberDefenition;
998 protected override string GetMemberAttributes (MemberReference memberDefenition)
1000 EventDefinition evt = (EventDefinition) memberDefenition;
1001 return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
1004 protected override void AddExtraAttributes (MemberReference memberDefinition)
1006 base.AddExtraAttributes (memberDefinition);
1008 EventDefinition evt = (EventDefinition) memberDefinition;
1009 AddAttribute ("eventtype", Utils.CleanupTypeName (evt.EventType));
1012 public override string ParentTag {
1013 get { return "events"; }
1016 public override string Tag {
1017 get { return "event"; }
1021 class MethodData : MemberData
1025 public MethodData (XmlWriter writer, MethodDefinition [] members)
1026 : base (writer, members)
1030 protected override string GetName (MemberReference memberDefenition)
1032 MethodDefinition method = (MethodDefinition) memberDefenition;
1033 string name = method.Name;
1034 string parms = Parameters.GetSignature (method.Parameters);
1036 return string.Format ("{0}({1})", name, parms);
1039 protected override string GetMemberAttributes (MemberReference memberDefenition)
1041 MethodDefinition method = (MethodDefinition) memberDefenition;
1042 return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture);
1045 protected override ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member)
1047 var mbase = (MethodDefinition) member;
1048 return mbase.MethodReturnType;
1051 protected override void AddExtraAttributes (MemberReference memberDefinition)
1053 base.AddExtraAttributes (memberDefinition);
1055 if (!(memberDefinition is MethodDefinition))
1058 MethodDefinition mbase = (MethodDefinition) memberDefinition;
1060 if (mbase.IsAbstract)
1061 AddAttribute ("abstract", "true");
1062 if (mbase.IsVirtual)
1063 AddAttribute ("virtual", "true");
1064 if (mbase.IsFinal && mbase.IsVirtual && mbase.IsReuseSlot)
1065 AddAttribute ("sealed", "true");
1067 AddAttribute ("static", "true");
1068 var baseMethod = TypeHelper.GetBaseMethodInTypeHierarchy (mbase);
1069 if (baseMethod != null && baseMethod != mbase) {
1070 // This indicates whether this method is an override of another method.
1071 // This information is not necessarily available in the api info for any
1072 // particular assembly, because a method is only overriding another if
1073 // there is a base virtual function with the same signature, and that
1074 // base method can come from another assembly.
1075 AddAttribute ("is-override", "true");
1077 string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType);
1078 if (rettype != "System.Void" || !mbase.IsConstructor)
1079 AddAttribute ("returntype", (rettype));
1081 // if (mbase.MethodReturnType.HasCustomAttributes)
1082 // AttributeData.OutputAttributes (writer, mbase.MethodReturnType);
1085 protected override void AddExtraData (MemberReference memberDefenition)
1087 base.AddExtraData (memberDefenition);
1089 if (!(memberDefenition is MethodDefinition))
1092 MethodDefinition mbase = (MethodDefinition)memberDefenition;
1094 ParameterData parms = new ParameterData (writer, mbase.Parameters) {
1095 HasExtensionParameter = mbase.CustomAttributes.Any (l => l.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
1100 MemberData.OutputGenericParameters (writer, mbase);
1103 public override bool NoMemberAttributes {
1104 get { return noAtts; }
1105 set { noAtts = value; }
1108 public override string ParentTag {
1109 get { return "methods"; }
1112 public override string Tag {
1113 get { return "method"; }
1117 class ConstructorData : MethodData
1119 public ConstructorData (XmlWriter writer, MethodDefinition [] members)
1120 : base (writer, members)
1124 public override string ParentTag {
1125 get { return "constructors"; }
1128 public override string Tag {
1129 get { return "constructor"; }
1133 class ParameterData : BaseData
1135 private IList<ParameterDefinition> parameters;
1137 public ParameterData (XmlWriter writer, IList<ParameterDefinition> parameters)
1140 this.parameters = parameters;
1143 public bool HasExtensionParameter { get; set; }
1145 public override void DoOutput ()
1148 writer.WriteStartElement ("parameters");
1149 foreach (ParameterDefinition parameter in parameters) {
1150 writer.WriteStartElement ("parameter");
1151 AddAttribute ("name", parameter.Name);
1152 AddAttribute ("position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture));
1153 AddAttribute ("attrib", ((int) parameter.Attributes).ToString());
1155 string direction = first && HasExtensionParameter ? "this" : "in";
1158 if (parameter.ParameterType is ByReferenceType)
1159 direction = parameter.IsOut ? "out" : "ref";
1161 TypeReference t = parameter.ParameterType;
1162 AddAttribute ("type", Utils.CleanupTypeName (t));
1164 if (parameter.IsOptional) {
1165 AddAttribute ("optional", "true");
1166 if (parameter.HasConstant)
1167 AddAttribute ("defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ());
1170 if (direction != "in")
1171 AddAttribute ("direction", direction);
1173 AttributeData.OutputAttributes (writer, parameter);
1174 writer.WriteEndElement (); // parameter
1176 writer.WriteEndElement (); // parameters
1182 public static void DoOutput (XmlWriter writer, IList<ICustomAttributeProvider> providers)
1185 throw new InvalidOperationException ("Document not set");
1187 if (providers == null || providers.Count == 0)
1190 if (!providers.Any ((provider) => provider != null && provider.HasCustomAttributes))
1193 writer.WriteStartElement ("attributes");
1195 foreach (var provider in providers) {
1196 if (provider == null)
1199 if (!provider.HasCustomAttributes)
1203 var ass = provider as AssemblyDefinition;
1204 if (ass != null && !Driver.FollowForwarders)
1205 TypeForwardedToData.OutputForwarders (writer, ass);
1207 var attributes = provider.CustomAttributes.
1208 Where ((att) => !SkipAttribute (att)).
1209 OrderBy ((a) => a.Constructor.DeclaringType.FullName, StringComparer.Ordinal);
1211 foreach (var att in attributes) {
1212 string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType);
1214 writer.WriteStartElement ("attribute");
1215 writer.WriteAttributeString ("name", attName);
1217 var attribute_mapping = CreateAttributeMapping (att);
1219 if (attribute_mapping != null) {
1220 var mapping = attribute_mapping.Where ((attr) => attr.Key != "TypeId");
1221 if (mapping.Any ()) {
1222 writer.WriteStartElement ("properties");
1223 foreach (var kvp in mapping) {
1224 string name = kvp.Key;
1225 object o = kvp.Value;
1227 writer.WriteStartElement ("property");
1228 writer.WriteAttributeString ("name", name);
1231 writer.WriteAttributeString ("value", "null");
1233 string value = o.ToString ();
1234 if (attName.EndsWith ("GuidAttribute", StringComparison.Ordinal))
1235 value = value.ToUpper ();
1236 writer.WriteAttributeString ("value", value);
1239 writer.WriteEndElement (); // property
1241 writer.WriteEndElement (); // properties
1244 writer.WriteEndElement (); // attribute
1248 writer.WriteEndElement (); // attributes
1251 static Dictionary<string, object> CreateAttributeMapping (CustomAttribute attribute)
1253 Dictionary<string, object> mapping = null;
1255 PopulateMapping (ref mapping, attribute);
1257 var constructor = attribute.Constructor.Resolve ();
1258 if (constructor == null || !constructor.HasParameters)
1261 PopulateMapping (ref mapping, constructor, attribute);
1266 static void PopulateMapping (ref Dictionary<string, object> mapping, CustomAttribute attribute)
1268 if (!attribute.HasProperties)
1271 foreach (var named_argument in attribute.Properties) {
1272 var name = named_argument.Name;
1273 var arg = named_argument.Argument;
1275 if (arg.Value is CustomAttributeArgument)
1276 arg = (CustomAttributeArgument) arg.Value;
1278 if (mapping == null)
1279 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1280 mapping.Add (name, GetArgumentValue (arg.Type, arg.Value));
1284 static Dictionary<FieldReference, int> CreateArgumentFieldMapping (MethodDefinition constructor)
1286 Dictionary<FieldReference, int> field_mapping = null;
1288 int? argument = null;
1290 foreach (Instruction instruction in constructor.Body.Instructions) {
1291 switch (instruction.OpCode.Code) {
1303 argument = ((ParameterDefinition) instruction.Operand).Index + 1;
1307 FieldReference field = (FieldReference) instruction.Operand;
1308 if (field.DeclaringType.FullName != constructor.DeclaringType.FullName)
1311 if (!argument.HasValue)
1314 if (field_mapping == null)
1315 field_mapping = new Dictionary<FieldReference, int> ();
1317 if (!field_mapping.ContainsKey (field))
1318 field_mapping.Add (field, (int) argument - 1);
1325 return field_mapping;
1328 static Dictionary<PropertyDefinition, FieldReference> CreatePropertyFieldMapping (TypeDefinition type)
1330 Dictionary<PropertyDefinition, FieldReference> property_mapping = null;
1332 foreach (PropertyDefinition property in type.Properties) {
1333 if (property.GetMethod == null)
1335 if (!property.GetMethod.HasBody)
1338 foreach (Instruction instruction in property.GetMethod.Body.Instructions) {
1339 if (instruction.OpCode.Code != Code.Ldfld)
1342 FieldReference field = (FieldReference) instruction.Operand;
1343 if (field.DeclaringType.FullName != type.FullName)
1346 if (property_mapping == null)
1347 property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
1348 property_mapping.Add (property, field);
1353 return property_mapping;
1356 static void PopulateMapping (ref Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
1358 if (!constructor.HasBody)
1361 // Custom handling for attributes with arguments which cannot be easily extracted
1362 var ca = attribute.ConstructorArguments;
1363 switch (constructor.DeclaringType.FullName) {
1364 case "System.Runtime.CompilerServices.DecimalConstantAttribute":
1365 var dca = constructor.Parameters[2].ParameterType == constructor.Module.TypeSystem.Int32 ?
1366 new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (int) ca[2].Value, (int) ca[3].Value, (int) ca[4].Value) :
1367 new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (uint) ca[2].Value, (uint) ca[3].Value, (uint) ca[4].Value);
1369 if (mapping == null)
1370 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1371 mapping.Add ("Value", dca.Value);
1373 case "System.ComponentModel.BindableAttribute":
1377 if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) {
1378 if (mapping == null)
1379 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1380 mapping.Add ("Bindable", ca[0].Value);
1382 throw new NotImplementedException ();
1388 var field_mapping = CreateArgumentFieldMapping (constructor);
1389 if (field_mapping != null) {
1390 var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
1392 if (property_mapping != null) {
1393 foreach (var pair in property_mapping) {
1395 if (!field_mapping.TryGetValue (pair.Value, out argument))
1398 var ca_arg = ca [argument];
1399 if (ca_arg.Value is CustomAttributeArgument)
1400 ca_arg = (CustomAttributeArgument)ca_arg.Value;
1402 if (mapping == null)
1403 mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1404 mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value));
1410 static object GetArgumentValue (TypeReference reference, object value)
1412 var type = reference.Resolve ();
1417 if (IsFlaggedEnum (type))
1418 return GetFlaggedEnumValue (type, value);
1420 return GetEnumValue (type, value);
1426 static bool IsFlaggedEnum (TypeDefinition type)
1431 if (!type.HasCustomAttributes)
1434 foreach (CustomAttribute attribute in type.CustomAttributes)
1435 if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute")
1441 static object GetFlaggedEnumValue (TypeDefinition type, object value)
1444 return GetFlaggedEnumValue (type, (ulong)value);
1446 long flags = Convert.ToInt64 (value);
1447 var signature = new StringBuilder ();
1449 for (int i = type.Fields.Count - 1; i >= 0; i--) {
1450 FieldDefinition field = type.Fields [i];
1452 if (!field.HasConstant)
1455 long flag = Convert.ToInt64 (field.Constant);
1460 if ((flags & flag) == flag) {
1461 if (signature.Length != 0)
1462 signature.Append (", ");
1464 signature.Append (field.Name);
1469 return signature.ToString ();
1472 static object GetFlaggedEnumValue (TypeDefinition type, ulong flags)
1474 var signature = new StringBuilder ();
1476 for (int i = type.Fields.Count - 1; i >= 0; i--) {
1477 FieldDefinition field = type.Fields [i];
1479 if (!field.HasConstant)
1482 ulong flag = Convert.ToUInt64 (field.Constant);
1487 if ((flags & flag) == flag) {
1488 if (signature.Length != 0)
1489 signature.Append (", ");
1491 signature.Append (field.Name);
1496 return signature.ToString ();
1499 static object GetEnumValue (TypeDefinition type, object value)
1501 foreach (FieldDefinition field in type.Fields) {
1502 if (!field.HasConstant)
1505 if (Comparer.Default.Compare (field.Constant, value) == 0)
1512 static bool SkipAttribute (CustomAttribute attribute)
1514 if (!TypeHelper.IsPublic (attribute))
1517 return attribute.Constructor.DeclaringType.Name.EndsWith ("TODOAttribute", StringComparison.Ordinal);
1520 public static void OutputAttributes (XmlWriter writer, params ICustomAttributeProvider[] providers)
1522 AttributeData.DoOutput (writer, providers);
1526 static class Parameters {
1528 public static string GetSignature (IList<ParameterDefinition> infos)
1530 if (infos == null || infos.Count == 0)
1531 return string.Empty;
1533 var signature = new StringBuilder ();
1534 for (int i = 0; i < infos.Count; i++) {
1537 signature.Append (", ");
1539 ParameterDefinition info = infos [i];
1541 if (info.ParameterType.IsByReference) {
1543 if ((info.Attributes & (ParameterAttributes.Out | ParameterAttributes.In)) == ParameterAttributes.Out)
1548 signature.Append (modifier);
1549 signature.Append (" ");
1552 signature.Append (Utils.CleanupTypeName (info.ParameterType));
1555 return signature.ToString ();
1560 class TypeReferenceComparer : IComparer<TypeReference>
1562 public static TypeReferenceComparer Default = new TypeReferenceComparer ();
1564 public int Compare (TypeReference a, TypeReference b)
1566 int result = String.Compare (a.Namespace, b.Namespace, StringComparison.Ordinal);
1570 return String.Compare (a.Name, b.Name, StringComparison.Ordinal);
1574 class MemberReferenceComparer : IComparer
1576 public static MemberReferenceComparer Default = new MemberReferenceComparer ();
1578 public int Compare (object a, object b)
1580 MemberReference ma = (MemberReference) a;
1581 MemberReference mb = (MemberReference) b;
1582 return String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1586 class PropertyDefinitionComparer : IComparer<PropertyDefinition>
1588 public static PropertyDefinitionComparer Default = new PropertyDefinitionComparer ();
1590 public int Compare (PropertyDefinition ma, PropertyDefinition mb)
1592 int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1596 if (!ma.HasParameters && !mb.HasParameters)
1599 if (!ma.HasParameters)
1602 if (!mb.HasParameters)
1605 return MethodDefinitionComparer.Compare (ma.Parameters, mb.Parameters);
1609 class MethodDefinitionComparer : IComparer
1611 public static MethodDefinitionComparer Default = new MethodDefinitionComparer ();
1613 public int Compare (object a, object b)
1615 MethodDefinition ma = (MethodDefinition) a;
1616 MethodDefinition mb = (MethodDefinition) b;
1617 int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1621 if (!ma.HasParameters && !mb.HasParameters)
1624 if (!ma.HasParameters)
1627 if (!mb.HasParameters)
1630 res = Compare (ma.Parameters, mb.Parameters);
1634 if (ma.HasGenericParameters != mb.HasGenericParameters)
1635 return ma.HasGenericParameters ? -1 : 1;
1637 if (ma.HasGenericParameters && mb.HasGenericParameters) {
1638 res = ma.GenericParameters.Count - mb.GenericParameters.Count;
1643 // operators can differ by only return type
1644 return string.CompareOrdinal (ma.ReturnType.FullName, mb.ReturnType.FullName);
1647 public static int Compare (IList<ParameterDefinition> pia, IList<ParameterDefinition> pib)
1649 var res = pia.Count - pib.Count;
1653 string siga = Parameters.GetSignature (pia);
1654 string sigb = Parameters.GetSignature (pib);
1655 return String.Compare (siga, sigb, StringComparison.Ordinal);