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;
35 var acoll = new AssemblyCollection ();
37 var options = new Mono.Options.OptionSet {
38 "usage: mono-api-info [OPTIONS+] ASSEMBLY+",
40 "Expose IL structure of CLR assemblies as XML.",
44 "Generate ABI, not API; contains only classes with instance fields which are not [NonSerialized].",
45 v => AbiMode = v != null },
46 { "f|follow-forwarders",
47 "Follow type forwarders.",
48 v => FollowForwarders = v != null },
49 { "d|L|lib|search-directory=",
50 "Check for assembly references in {DIRECTORY}.",
51 v => TypeHelper.Resolver.AddSearchDirectory (v) },
53 "Read and register the file {ASSEMBLY}, and add the directory containing ASSEMBLY to the search path.",
54 v => TypeHelper.Resolver.ResolveFile (v) },
56 "Show this message and exit.",
57 v => showHelp = v != null },
60 var asms = options.Parse (args);
62 if (showHelp || asms.Count == 0) {
63 options.WriteOptionDescriptions (Console.Out);
65 return showHelp? 0 :1;
68 string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
69 string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
70 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"assembly\GAC\MSDATASRC\7.0.3300.0__b03f5f7f11d50a3a"));
72 foreach (string arg in asms) {
75 if (arg.Contains ("v3.0")) {
76 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
77 } else if (arg.Contains ("v3.5")) {
78 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
79 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v3.0\Windows Communication Foundation"));
80 } else if (arg.Contains ("v4.0")) {
81 if (arg.Contains ("Silverlight")) {
82 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (pf, @"Microsoft Silverlight\4.0.51204.0"));
84 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319"));
85 TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319\WPF"));
88 TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg));
92 XmlDocument doc = new XmlDocument ();
96 var writer = new WellFormedXmlWriter (new XmlTextWriter (Console.Out) { Formatting = Formatting.Indented });
97 XmlNode decl = doc.CreateXmlDeclaration ("1.0", "utf-8", null);
98 doc.InsertBefore (decl, doc.DocumentElement);
103 internal static bool AbiMode { get; private set; }
104 internal static bool FollowForwarders { get; private set; }
109 public static string CleanupTypeName (TypeReference type)
111 return CleanupTypeName (type.FullName);
114 public static string CleanupTypeName (string t)
116 return t.Replace ('<', '[').Replace ('>', ']').Replace ('/', '+');
120 class AssemblyCollection
122 XmlDocument document;
123 List<AssemblyDefinition> assemblies = new List<AssemblyDefinition> ();
125 public AssemblyCollection ()
129 public bool Add (string name)
131 AssemblyDefinition ass = LoadAssembly (name);
133 Console.Error.WriteLine ("Cannot load assembly file " + name);
137 assemblies.Add (ass);
141 public void DoOutput ()
143 if (document == null)
144 throw new InvalidOperationException ("Document not set");
146 XmlNode nassemblies = document.CreateElement ("assemblies", null);
147 document.AppendChild (nassemblies);
148 foreach (AssemblyDefinition a in assemblies) {
149 AssemblyData data = new AssemblyData (document, nassemblies, a);
154 public XmlDocument Document {
155 set { document = value; }
158 AssemblyDefinition LoadAssembly (string assembly)
161 if (File.Exists (assembly))
162 return TypeHelper.Resolver.ResolveFile (assembly);
164 return TypeHelper.Resolver.Resolve (assembly);
165 } catch (Exception e) {
166 Console.WriteLine (e);
172 abstract class BaseData
174 protected XmlDocument document;
175 protected XmlNode parent;
177 protected BaseData (XmlDocument doc, XmlNode parent)
180 this.parent = parent;
183 public abstract void DoOutput ();
185 protected void AddAttribute (XmlNode node, string name, string value)
187 XmlAttribute attr = document.CreateAttribute (name);
189 node.Attributes.Append (attr);
193 class TypeForwardedToData : BaseData
195 AssemblyDefinition ass;
197 public TypeForwardedToData (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
198 : base (document, parent)
203 public override void DoOutput ()
205 XmlNode natts = parent.SelectSingleNode("attributes");
207 natts = document.CreateElement ("attributes", null);
208 parent.AppendChild (natts);
211 foreach (ExportedType type in ass.MainModule.ExportedTypes) {
213 if (((uint)type.Attributes & 0x200000u) == 0)
216 XmlNode node = document.CreateElement ("attribute");
217 AddAttribute (node, "name", typeof (TypeForwardedToAttribute).FullName);
218 XmlNode properties = node.AppendChild (document.CreateElement ("properties"));
219 XmlNode property = properties.AppendChild (document.CreateElement ("property"));
220 AddAttribute (property, "name", "Destination");
221 AddAttribute (property, "value", Utils.CleanupTypeName (type.FullName));
222 natts.AppendChild (node);
226 public static void OutputForwarders (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
228 TypeForwardedToData tftd = new TypeForwardedToData (document, parent, ass);
233 class AssemblyData : BaseData
235 AssemblyDefinition ass;
237 public AssemblyData (XmlDocument document, XmlNode parent, AssemblyDefinition ass)
238 : base (document, parent)
243 public override void DoOutput ()
245 if (document == null)
246 throw new InvalidOperationException ("Document not set");
248 XmlNode nassembly = document.CreateElement ("assembly", null);
249 AssemblyNameDefinition aname = ass.Name;
250 AddAttribute (nassembly, "name", aname.Name);
251 AddAttribute (nassembly, "version", aname.Version.ToString ());
252 parent.AppendChild (nassembly);
254 if (!Driver.FollowForwarders) {
255 TypeForwardedToData.OutputForwarders (document, nassembly, ass);
258 AttributeData.OutputAttributes (document, nassembly, ass);
260 var types = new List<TypeDefinition> ();
261 if (ass.MainModule.Types != null) {
262 types.AddRange (ass.MainModule.Types);
265 if (Driver.FollowForwarders && ass.MainModule.ExportedTypes != null) {
266 foreach (var t in ass.MainModule.ExportedTypes) {
267 var forwarded = t.Resolve ();
268 if (forwarded == null) {
269 throw new Exception ("Could not resolve forwarded type " + t.FullName + " in " + ass.Name);
271 types.Add (forwarded);
275 if (types.Count == 0) {
279 types.Sort (TypeReferenceComparer.Default);
281 XmlNode nss = document.CreateElement ("namespaces", null);
282 nassembly.AppendChild (nss);
284 string current_namespace = "$%&$&";
286 XmlNode classes = null;
287 foreach (TypeDefinition t in types) {
288 if (string.IsNullOrEmpty (t.Namespace))
291 if (!Driver.AbiMode && ((t.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public))
294 if (t.DeclaringType != null)
295 continue; // enforce !nested
297 if (t.Namespace != current_namespace) {
298 current_namespace = t.Namespace;
299 ns = document.CreateElement ("namespace", null);
300 AddAttribute (ns, "name", current_namespace);
301 nss.AppendChild (ns);
302 classes = document.CreateElement ("classes", null);
303 ns.AppendChild (classes);
306 TypeData bd = new TypeData (document, classes, t);
312 abstract class MemberData : BaseData
314 MemberReference [] members;
316 public MemberData (XmlDocument document, XmlNode parent, MemberReference [] members)
317 : base (document, parent)
319 this.members = members;
322 public override void DoOutput ()
324 XmlNode mclass = document.CreateElement (ParentTag, null);
325 parent.AppendChild (mclass);
327 foreach (MemberReference member in members) {
328 XmlNode mnode = document.CreateElement (Tag, null);
329 mclass.AppendChild (mnode);
330 AddAttribute (mnode, "name", GetName (member));
331 if (!NoMemberAttributes)
332 AddAttribute (mnode, "attrib", GetMemberAttributes (member));
334 AttributeData.OutputAttributes (document, mnode, (ICustomAttributeProvider) member);
336 AddExtraData (mnode, member);
340 protected virtual void AddExtraData (XmlNode p, MemberReference memberDefenition)
344 protected virtual string GetName (MemberReference memberDefenition)
349 protected virtual string GetMemberAttributes (MemberReference memberDefenition)
354 public virtual bool NoMemberAttributes {
355 get { return false; }
359 public virtual string ParentTag {
360 get { return "NoPARENTTAG"; }
363 public virtual string Tag {
364 get { return "NoTAG"; }
367 public static void OutputGenericParameters (XmlDocument document, XmlNode nclass, IGenericParameterProvider provider)
369 if (provider.GenericParameters.Count == 0)
372 var gparameters = provider.GenericParameters;
374 XmlElement ngeneric = document.CreateElement ("generic-parameters");
375 nclass.AppendChild (ngeneric);
377 foreach (GenericParameter gp in gparameters) {
378 XmlElement nparam = document.CreateElement ("generic-parameter");
379 nparam.SetAttribute ("name", gp.Name);
380 nparam.SetAttribute ("attributes", ((int) gp.Attributes).ToString ());
382 AttributeData.OutputAttributes (document, nparam, gp);
384 ngeneric.AppendChild (nparam);
386 var constraints = gp.Constraints;
387 if (constraints.Count == 0)
390 XmlElement nconstraint = document.CreateElement ("generic-parameter-constraints");
392 foreach (TypeReference constraint in constraints) {
393 XmlElement ncons = document.CreateElement ("generic-parameter-constraint");
394 ncons.SetAttribute ("name", Utils.CleanupTypeName (constraint));
395 nconstraint.AppendChild (ncons);
398 nparam.AppendChild (nconstraint);
403 class TypeData : MemberData
407 public TypeData (XmlDocument document, XmlNode parent, TypeDefinition type)
408 : base (document, parent, null)
412 public override void DoOutput ()
414 if (document == null)
415 throw new InvalidOperationException ("Document not set");
417 XmlNode nclass = document.CreateElement ("class", null);
418 AddAttribute (nclass, "name", type.Name);
419 string classType = GetClassType (type);
420 AddAttribute (nclass, "type", classType);
422 if (type.BaseType != null)
423 AddAttribute (nclass, "base", Utils.CleanupTypeName (type.BaseType));
426 AddAttribute (nclass, "sealed", "true");
429 AddAttribute (nclass, "abstract", "true");
431 if ( (type.Attributes & TypeAttributes.Serializable) != 0 || type.IsEnum)
432 AddAttribute (nclass, "serializable", "true");
434 string charSet = GetCharSet (type);
435 AddAttribute (nclass, "charset", charSet);
437 string layout = GetLayout (type);
439 AddAttribute (nclass, "layout", layout);
441 if (type.PackingSize >= 0) {
442 AddAttribute (nclass, "pack", type.PackingSize.ToString ());
445 if (type.ClassSize >= 0) {
446 AddAttribute (nclass, "size", type.ClassSize.ToString ());
449 parent.AppendChild (nclass);
451 AttributeData.OutputAttributes (document, nclass, type);
453 XmlNode ifaces = null;
455 foreach (TypeReference iface in TypeHelper.GetInterfaces (type).OrderBy (s => s.FullName)) {
456 if (!TypeHelper.IsPublic (iface))
457 // we're only interested in public interfaces
460 if (ifaces == null) {
461 ifaces = document.CreateElement ("interfaces", null);
462 nclass.AppendChild (ifaces);
465 XmlNode iface_node = document.CreateElement ("interface", null);
466 AddAttribute (iface_node, "name", Utils.CleanupTypeName (iface));
467 ifaces.AppendChild (iface_node);
470 MemberData.OutputGenericParameters (document, nclass, type);
472 ArrayList members = new ArrayList ();
474 FieldDefinition [] fields = GetFields (type);
475 if (fields.Length > 0) {
476 Array.Sort (fields, MemberReferenceComparer.Default);
477 FieldData fd = new FieldData (document, nclass, fields);
482 var value_type = GetEnumValueField (type);
483 if (value_type == null)
484 throw new NotSupportedException ();
486 AddAttribute (nclass, "enumtype", Utils.CleanupTypeName (value_type.FieldType));
489 if (!Driver.AbiMode) {
491 MethodDefinition [] ctors = GetConstructors (type);
492 if (ctors.Length > 0) {
493 Array.Sort (ctors, MethodDefinitionComparer.Default);
494 members.Add (new ConstructorData (document, nclass, ctors));
497 PropertyDefinition[] properties = GetProperties (type);
498 if (properties.Length > 0) {
499 Array.Sort (properties, PropertyDefinitionComparer.Default);
500 members.Add (new PropertyData (document, nclass, properties));
503 EventDefinition [] events = GetEvents (type);
504 if (events.Length > 0) {
505 Array.Sort (events, MemberReferenceComparer.Default);
506 members.Add (new EventData (document, nclass, events));
509 MethodDefinition [] methods = GetMethods (type);
510 if (methods.Length > 0) {
511 Array.Sort (methods, MethodDefinitionComparer.Default);
512 members.Add (new MethodData (document, nclass, methods));
516 foreach (MemberData md in members)
519 var nested = type.NestedTypes;
520 //remove non public(familiy) and nested in second degree
521 for (int i = nested.Count - 1; i >= 0; i--) {
522 TypeDefinition t = nested [i];
523 if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic ||
524 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily ||
525 (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) {
527 if (t.DeclaringType == type)
528 continue; // not nested of nested
534 if (nested.Count > 0) {
535 var nestedArray = nested.ToArray ();
536 Array.Sort (nestedArray, TypeReferenceComparer.Default);
538 XmlNode classes = document.CreateElement ("classes", null);
539 nclass.AppendChild (classes);
540 foreach (TypeDefinition t in nestedArray) {
541 TypeData td = new TypeData (document, classes, t);
547 static FieldReference GetEnumValueField (TypeDefinition type)
549 foreach (FieldDefinition field in type.Fields)
550 if (field.IsSpecialName && field.Name == "value__")
556 protected override string GetMemberAttributes (MemberReference member)
559 throw new InvalidOperationException ("odd");
561 return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
564 public static bool MustDocumentMethod (MethodDefinition method) {
566 MethodAttributes maskedAccess = method.Attributes & MethodAttributes.MemberAccessMask;
567 return maskedAccess == MethodAttributes.Public
568 || maskedAccess == MethodAttributes.Family
569 || maskedAccess == MethodAttributes.FamORAssem;
572 static string GetClassType (TypeDefinition t)
583 if (TypeHelper.IsDelegate(t))
592 static string GetCharSet (TypeDefinition type)
594 TypeAttributes maskedStringFormat = type.Attributes & TypeAttributes.StringFormatMask;
595 if (maskedStringFormat == TypeAttributes.AnsiClass)
596 return CharSet.Ansi.ToString ();
598 if (maskedStringFormat == TypeAttributes.AutoClass)
599 return CharSet.Auto.ToString ();
601 if (maskedStringFormat == TypeAttributes.UnicodeClass)
602 return CharSet.Unicode.ToString ();
604 return CharSet.None.ToString ();
607 static string GetLayout (TypeDefinition type)
609 TypeAttributes maskedLayout = type.Attributes & TypeAttributes.LayoutMask;
610 if (maskedLayout == TypeAttributes.AutoLayout)
611 return LayoutKind.Auto.ToString ();
613 if (maskedLayout == TypeAttributes.ExplicitLayout)
614 return LayoutKind.Explicit.ToString ();
616 if (maskedLayout == TypeAttributes.SequentialLayout)
617 return LayoutKind.Sequential.ToString ();
622 FieldDefinition [] GetFields (TypeDefinition type) {
623 ArrayList list = new ArrayList ();
625 var fields = type.Fields;
626 foreach (FieldDefinition field in fields) {
627 if (field.IsSpecialName)
630 if (Driver.AbiMode && field.IsStatic)
633 // we're only interested in public or protected members
634 FieldAttributes maskedVisibility = (field.Attributes & FieldAttributes.FieldAccessMask);
635 if (Driver.AbiMode && !field.IsNotSerialized) {
638 if (maskedVisibility == FieldAttributes.Public
639 || maskedVisibility == FieldAttributes.Family
640 || maskedVisibility == FieldAttributes.FamORAssem) {
646 return (FieldDefinition []) list.ToArray (typeof (FieldDefinition));
650 internal static PropertyDefinition [] GetProperties (TypeDefinition type) {
651 ArrayList list = new ArrayList ();
653 var properties = type.Properties;//type.GetProperties (flags);
654 foreach (PropertyDefinition property in properties) {
655 MethodDefinition getMethod = property.GetMethod;
656 MethodDefinition setMethod = property.SetMethod;
658 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
659 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
661 // if neither the getter or setter should be documented, then
663 if (hasGetter || hasSetter) {
668 return (PropertyDefinition []) list.ToArray (typeof (PropertyDefinition));
671 private MethodDefinition[] GetMethods (TypeDefinition type)
673 ArrayList list = new ArrayList ();
675 var methods = type.Methods;//type.GetMethods (flags);
676 foreach (MethodDefinition method in methods) {
677 if (method.IsSpecialName && !method.Name.StartsWith ("op_"))
680 // we're only interested in public or protected members
681 if (!MustDocumentMethod(method))
684 if (IsFinalizer (method)) {
685 string name = method.DeclaringType.Name;
686 int arity = name.IndexOf ('`');
688 name = name.Substring (0, arity);
690 method.Name = "~" + name;
696 return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
699 static bool IsFinalizer (MethodDefinition method)
701 if (method.Name != "Finalize")
704 if (!method.IsVirtual)
707 if (method.Parameters.Count != 0)
713 private MethodDefinition [] GetConstructors (TypeDefinition type)
715 ArrayList list = new ArrayList ();
717 var ctors = type.Methods.Where (m => m.IsConstructor);//type.GetConstructors (flags);
718 foreach (MethodDefinition constructor in ctors) {
719 // we're only interested in public or protected members
720 if (!MustDocumentMethod(constructor))
723 list.Add (constructor);
726 return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
729 private EventDefinition[] GetEvents (TypeDefinition type)
731 ArrayList list = new ArrayList ();
733 var events = type.Events;//type.GetEvents (flags);
734 foreach (EventDefinition eventDef in events) {
735 MethodDefinition addMethod = eventDef.AddMethod;//eventInfo.GetAddMethod (true);
737 if (addMethod == null || !MustDocumentMethod (addMethod))
743 return (EventDefinition []) list.ToArray (typeof (EventDefinition));
747 class FieldData : MemberData
749 public FieldData (XmlDocument document, XmlNode parent, FieldDefinition [] members)
750 : base (document, parent, members)
754 protected override string GetName (MemberReference memberDefenition)
756 FieldDefinition field = (FieldDefinition) memberDefenition;
760 protected override string GetMemberAttributes (MemberReference memberDefenition)
762 FieldDefinition field = (FieldDefinition) memberDefenition;
763 return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
766 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
768 base.AddExtraData (p, memberDefenition);
769 FieldDefinition field = (FieldDefinition) memberDefenition;
770 AddAttribute (p, "fieldtype", Utils.CleanupTypeName (field.FieldType));
772 if (field.IsLiteral) {
773 object value = field.Constant;//object value = field.GetValue (null);
774 string stringValue = null;
775 //if (value is Enum) {
776 // // FIXME: when Mono bug #60090 has been
777 // // fixed, we should just be able to use
778 // // Convert.ToString
779 // stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
782 stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
785 if (stringValue != null)
786 AddAttribute (p, "value", stringValue);
790 public override string ParentTag {
791 get { return "fields"; }
794 public override string Tag {
795 get { return "field"; }
799 class PropertyData : MemberData
801 public PropertyData (XmlDocument document, XmlNode parent, PropertyDefinition [] members)
802 : base (document, parent, members)
806 protected override string GetName (MemberReference memberDefenition)
808 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
812 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
814 base.AddExtraData (p, memberDefenition);
815 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
816 AddAttribute (p, "ptype", Utils.CleanupTypeName (prop.PropertyType));
817 MethodDefinition _get = prop.GetMethod;
818 MethodDefinition _set = prop.SetMethod;
819 bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
820 bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
821 MethodDefinition [] methods;
823 if (haveGet && haveSet) {
824 methods = new MethodDefinition [] { _get, _set };
825 } else if (haveGet) {
826 methods = new MethodDefinition [] { _get };
827 } else if (haveSet) {
828 methods = new MethodDefinition [] { _set };
834 if (haveGet || _set.Parameters.Count > 1) {
835 string parms = Parameters.GetSignature (methods [0].Parameters);
836 if (!string.IsNullOrEmpty (parms))
837 AddAttribute (p, "params", parms);
840 MethodData data = new MethodData (document, p, methods);
841 //data.NoMemberAttributes = true;
845 protected override string GetMemberAttributes (MemberReference memberDefenition)
847 PropertyDefinition prop = (PropertyDefinition) memberDefenition;
848 return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
851 public override string ParentTag {
852 get { return "properties"; }
855 public override string Tag {
856 get { return "property"; }
860 class EventData : MemberData
862 public EventData (XmlDocument document, XmlNode parent, EventDefinition [] members)
863 : base (document, parent, members)
867 protected override string GetName (MemberReference memberDefenition)
869 EventDefinition evt = (EventDefinition) memberDefenition;
873 protected override string GetMemberAttributes (MemberReference memberDefenition)
875 EventDefinition evt = (EventDefinition) memberDefenition;
876 return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
879 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
881 base.AddExtraData (p, memberDefenition);
882 EventDefinition evt = (EventDefinition) memberDefenition;
883 AddAttribute (p, "eventtype", Utils.CleanupTypeName (evt.EventType));
886 public override string ParentTag {
887 get { return "events"; }
890 public override string Tag {
891 get { return "event"; }
895 class MethodData : MemberData
899 public MethodData (XmlDocument document, XmlNode parent, MethodDefinition [] members)
900 : base (document, parent, members)
904 protected override string GetName (MemberReference memberDefenition)
906 MethodDefinition method = (MethodDefinition) memberDefenition;
907 string name = method.Name;
908 string parms = Parameters.GetSignature (method.Parameters);
910 return string.Format ("{0}({1})", name, parms);
913 protected override string GetMemberAttributes (MemberReference memberDefenition)
915 MethodDefinition method = (MethodDefinition) memberDefenition;
916 return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture);
919 protected override void AddExtraData (XmlNode p, MemberReference memberDefenition)
921 base.AddExtraData (p, memberDefenition);
923 if (!(memberDefenition is MethodDefinition))
926 MethodDefinition mbase = (MethodDefinition) memberDefenition;
928 ParameterData parms = new ParameterData (document, p, mbase.Parameters);
931 if (mbase.IsAbstract)
932 AddAttribute (p, "abstract", "true");
934 AddAttribute (p, "virtual", "true");
935 if (mbase.IsFinal && mbase.IsVirtual && mbase.IsReuseSlot)
936 AddAttribute (p, "sealed", "true");
938 AddAttribute (p, "static", "true");
940 string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType);
941 if (rettype != "System.Void" || !mbase.IsConstructor)
942 AddAttribute (p, "returntype", (rettype));
944 AttributeData.OutputAttributes (document, p, mbase.MethodReturnType);
946 MemberData.OutputGenericParameters (document, p, mbase);
949 public override bool NoMemberAttributes {
950 get { return noAtts; }
951 set { noAtts = value; }
954 public override string ParentTag {
955 get { return "methods"; }
958 public override string Tag {
959 get { return "method"; }
963 class ConstructorData : MethodData
965 public ConstructorData (XmlDocument document, XmlNode parent, MethodDefinition [] members)
966 : base (document, parent, members)
970 public override string ParentTag {
971 get { return "constructors"; }
974 public override string Tag {
975 get { return "constructor"; }
979 class ParameterData : BaseData
981 private IList<ParameterDefinition> parameters;
983 public ParameterData (XmlDocument document, XmlNode parent, IList<ParameterDefinition> parameters)
984 : base (document, parent)
986 this.parameters = parameters;
989 public override void DoOutput ()
991 XmlNode parametersNode = document.CreateElement ("parameters");
992 parent.AppendChild (parametersNode);
994 foreach (ParameterDefinition parameter in parameters) {
995 XmlNode paramNode = document.CreateElement ("parameter");
996 parametersNode.AppendChild (paramNode);
997 AddAttribute (paramNode, "name", parameter.Name);
998 AddAttribute (paramNode, "position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture));
999 AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString());
1001 string direction = "in";
1003 if (parameter.ParameterType is ByReferenceType)
1004 direction = parameter.IsOut ? "out" : "ref";
1006 TypeReference t = parameter.ParameterType;
1007 AddAttribute (paramNode, "type", Utils.CleanupTypeName (t));
1009 if (parameter.IsOptional) {
1010 AddAttribute (paramNode, "optional", "true");
1011 if (parameter.HasConstant)
1012 AddAttribute (paramNode, "defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ());
1015 if (direction != "in")
1016 AddAttribute (paramNode, "direction", direction);
1018 AttributeData.OutputAttributes (document, paramNode, parameter);
1023 class AttributeData : BaseData
1025 IList<CustomAttribute> atts;
1027 AttributeData (XmlDocument doc, XmlNode parent, IList<CustomAttribute> attributes)
1028 : base (doc, parent)
1033 public override void DoOutput ()
1035 if (document == null)
1036 throw new InvalidOperationException ("Document not set");
1038 if (atts == null || atts.Count == 0)
1041 XmlNode natts = parent.SelectSingleNode("attributes");
1042 if (natts == null) {
1043 natts = document.CreateElement ("attributes", null);
1044 parent.AppendChild (natts);
1047 foreach (var att in atts.OrderBy ((a) => a.Constructor.DeclaringType.FullName)) {
1048 string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType);
1049 if (SkipAttribute (att))
1052 XmlNode node = document.CreateElement ("attribute");
1053 AddAttribute (node, "name", attName);
1055 XmlNode properties = null;
1057 Dictionary<string, object> attribute_mapping = CreateAttributeMapping (att);
1059 foreach (string name in attribute_mapping.Keys) {
1060 if (name == "TypeId")
1063 if (properties == null) {
1064 properties = node.AppendChild (document.CreateElement ("properties"));
1067 object o = attribute_mapping [name];
1069 XmlNode n = properties.AppendChild (document.CreateElement ("property"));
1070 AddAttribute (n, "name", name);
1073 AddAttribute (n, "value", "null");
1077 string value = o.ToString ();
1078 if (attName.EndsWith ("GuidAttribute"))
1079 value = value.ToUpper ();
1080 AddAttribute (n, "value", value);
1083 natts.AppendChild (node);
1087 static Dictionary<string, object> CreateAttributeMapping (CustomAttribute attribute)
1089 var mapping = new Dictionary<string, object> ();
1091 PopulateMapping (mapping, attribute);
1093 var constructor = attribute.Constructor.Resolve ();
1094 if (constructor == null || !constructor.HasParameters)
1097 PopulateMapping (mapping, constructor, attribute);
1102 static void PopulateMapping (Dictionary<string, object> mapping, CustomAttribute attribute)
1104 if (!attribute.HasProperties)
1107 foreach (var named_argument in attribute.Properties) {
1108 var name = named_argument.Name;
1109 var arg = named_argument.Argument;
1111 if (arg.Value is CustomAttributeArgument)
1112 arg = (CustomAttributeArgument) arg.Value;
1114 mapping.Add (name, GetArgumentValue (arg.Type, arg.Value));
1118 static Dictionary<FieldReference, int> CreateArgumentFieldMapping (MethodDefinition constructor)
1120 Dictionary<FieldReference, int> field_mapping = new Dictionary<FieldReference, int> ();
1122 int? argument = null;
1124 foreach (Instruction instruction in constructor.Body.Instructions) {
1125 switch (instruction.OpCode.Code) {
1137 argument = ((ParameterDefinition) instruction.Operand).Index + 1;
1141 FieldReference field = (FieldReference) instruction.Operand;
1142 if (field.DeclaringType.FullName != constructor.DeclaringType.FullName)
1145 if (!argument.HasValue)
1148 if (!field_mapping.ContainsKey (field))
1149 field_mapping.Add (field, (int) argument - 1);
1156 return field_mapping;
1159 static Dictionary<PropertyDefinition, FieldReference> CreatePropertyFieldMapping (TypeDefinition type)
1161 Dictionary<PropertyDefinition, FieldReference> property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
1163 foreach (PropertyDefinition property in type.Properties) {
1164 if (property.GetMethod == null)
1166 if (!property.GetMethod.HasBody)
1169 foreach (Instruction instruction in property.GetMethod.Body.Instructions) {
1170 if (instruction.OpCode.Code != Code.Ldfld)
1173 FieldReference field = (FieldReference) instruction.Operand;
1174 if (field.DeclaringType.FullName != type.FullName)
1177 property_mapping.Add (property, field);
1182 return property_mapping;
1185 static void PopulateMapping (Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
1187 if (!constructor.HasBody)
1190 // Custom handling for attributes with arguments which cannot be easily extracted
1191 var ca = attribute.ConstructorArguments;
1192 switch (constructor.DeclaringType.FullName) {
1193 case "System.Runtime.CompilerServices.DecimalConstantAttribute":
1194 var dca = constructor.Parameters[2].ParameterType == constructor.Module.TypeSystem.Int32 ?
1195 new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (int) ca[2].Value, (int) ca[3].Value, (int) ca[4].Value) :
1196 new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (uint) ca[2].Value, (uint) ca[3].Value, (uint) ca[4].Value);
1198 mapping.Add ("Value", dca.Value);
1200 case "System.ComponentModel.BindableAttribute":
1204 if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) {
1205 mapping.Add ("Bindable", ca[0].Value);
1207 throw new NotImplementedException ();
1213 var field_mapping = CreateArgumentFieldMapping (constructor);
1214 var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
1216 foreach (var pair in property_mapping) {
1218 if (!field_mapping.TryGetValue (pair.Value, out argument))
1221 var ca_arg = ca [argument];
1222 if (ca_arg.Value is CustomAttributeArgument)
1223 ca_arg = (CustomAttributeArgument) ca_arg.Value;
1225 mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value));
1229 static object GetArgumentValue (TypeReference reference, object value)
1231 var type = reference.Resolve ();
1236 if (IsFlaggedEnum (type))
1237 return GetFlaggedEnumValue (type, value);
1239 return GetEnumValue (type, value);
1245 static bool IsFlaggedEnum (TypeDefinition type)
1250 if (!type.HasCustomAttributes)
1253 foreach (CustomAttribute attribute in type.CustomAttributes)
1254 if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute")
1260 static object GetFlaggedEnumValue (TypeDefinition type, object value)
1263 return GetFlaggedEnumValue (type, (ulong)value);
1265 long flags = Convert.ToInt64 (value);
1266 var signature = new StringBuilder ();
1268 for (int i = type.Fields.Count - 1; i >= 0; i--) {
1269 FieldDefinition field = type.Fields [i];
1271 if (!field.HasConstant)
1274 long flag = Convert.ToInt64 (field.Constant);
1279 if ((flags & flag) == flag) {
1280 if (signature.Length != 0)
1281 signature.Append (", ");
1283 signature.Append (field.Name);
1288 return signature.ToString ();
1291 static object GetFlaggedEnumValue (TypeDefinition type, ulong flags)
1293 var signature = new StringBuilder ();
1295 for (int i = type.Fields.Count - 1; i >= 0; i--) {
1296 FieldDefinition field = type.Fields [i];
1298 if (!field.HasConstant)
1301 ulong flag = Convert.ToUInt64 (field.Constant);
1306 if ((flags & flag) == flag) {
1307 if (signature.Length != 0)
1308 signature.Append (", ");
1310 signature.Append (field.Name);
1315 return signature.ToString ();
1318 static object GetEnumValue (TypeDefinition type, object value)
1320 foreach (FieldDefinition field in type.Fields) {
1321 if (!field.HasConstant)
1324 if (Comparer.Default.Compare (field.Constant, value) == 0)
1331 static bool SkipAttribute (CustomAttribute attribute)
1333 var type_name = Utils.CleanupTypeName (attribute.Constructor.DeclaringType);
1335 return !TypeHelper.IsPublic (attribute)
1336 || type_name.EndsWith ("TODOAttribute");
1339 public static void OutputAttributes (XmlDocument doc, XmlNode parent, ICustomAttributeProvider provider)
1341 if (!provider.HasCustomAttributes)
1344 AttributeData ad = new AttributeData (doc, parent, provider.CustomAttributes);
1349 static class Parameters {
1351 public static string GetSignature (IList<ParameterDefinition> infos)
1353 if (infos == null || infos.Count == 0)
1354 return string.Empty;
1356 var signature = new StringBuilder ();
1357 for (int i = 0; i < infos.Count; i++) {
1360 signature.Append (", ");
1362 ParameterDefinition info = infos [i];
1365 if ((info.Attributes & ParameterAttributes.In) != 0)
1367 else if ((info.Attributes & ParameterAttributes.Out) != 0)
1370 modifier = string.Empty;
1372 if (modifier.Length > 0) {
1373 signature.Append (modifier);
1374 signature.Append (" ");
1377 signature.Append (Utils.CleanupTypeName (info.ParameterType));
1380 return signature.ToString ();
1385 class TypeReferenceComparer : IComparer<TypeReference>
1387 public static TypeReferenceComparer Default = new TypeReferenceComparer ();
1389 public int Compare (TypeReference a, TypeReference b)
1391 int result = String.Compare (a.Namespace, b.Namespace, StringComparison.Ordinal);
1395 return String.Compare (a.Name, b.Name, StringComparison.Ordinal);
1399 class MemberReferenceComparer : IComparer
1401 public static MemberReferenceComparer Default = new MemberReferenceComparer ();
1403 public int Compare (object a, object b)
1405 MemberReference ma = (MemberReference) a;
1406 MemberReference mb = (MemberReference) b;
1407 return String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1411 class PropertyDefinitionComparer : IComparer<PropertyDefinition>
1413 public static PropertyDefinitionComparer Default = new PropertyDefinitionComparer ();
1415 public int Compare (PropertyDefinition ma, PropertyDefinition mb)
1417 int res = String.Compare (ma.Name, mb.Name);
1421 if (!ma.HasParameters && !mb.HasParameters)
1424 if (!ma.HasParameters)
1427 if (!mb.HasParameters)
1430 return MethodDefinitionComparer.Compare (ma.Parameters, mb.Parameters);
1434 class MethodDefinitionComparer : IComparer
1436 public static MethodDefinitionComparer Default = new MethodDefinitionComparer ();
1438 public int Compare (object a, object b)
1440 MethodDefinition ma = (MethodDefinition) a;
1441 MethodDefinition mb = (MethodDefinition) b;
1442 int res = String.Compare (ma.Name, mb.Name);
1446 if (!ma.HasParameters && !mb.HasParameters)
1449 if (!ma.HasParameters)
1452 if (!mb.HasParameters)
1455 res = Compare (ma.Parameters, mb.Parameters);
1459 // operators can differ by only return type
1460 return string.CompareOrdinal (ma.ReturnType.FullName, mb.ReturnType.FullName);
1463 public static int Compare (IList<ParameterDefinition> pia, IList<ParameterDefinition> pib)
1465 var res = pia.Count - pib.Count;
1469 string siga = Parameters.GetSignature (pia);
1470 string sigb = Parameters.GetSignature (pib);
1471 return String.Compare (siga, sigb);