2 // outline -- support for rendering in monop
3 // Some code stolen from updater.cs in monodoc.
6 // Ben Maurer (bmaurer@users.sourceforge.net)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Reflection;
34 using System.Collections;
35 using System.CodeDom.Compiler;
39 namespace Mono.CSharp {
40 public class Outline {
49 public Outline (Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete)
52 this.o = new IndentedTextWriter (output, "\t");
53 this.declared_only = declared_only;
54 this.show_private = show_private;
55 this.filter_obsolete = filter_obsolete;
58 public void OutlineType ()
63 o.Write (GetTypeVisibility (t));
65 if (t.IsClass && !t.IsSubclassOf (typeof (System.MulticastDelegate))) {
67 o.Write (t.IsAbstract ? " static" : " sealed");
68 else if (t.IsAbstract)
69 o.Write (" abstract");
73 o.Write (GetTypeKind (t));
76 Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, declared_only));
77 Type parent = t.BaseType;
79 if (t.IsSubclassOf (typeof (System.MulticastDelegate))) {
82 method = t.GetMethod ("Invoke");
84 o.Write (FormatType (method.ReturnType));
86 o.Write (GetTypeName (t));
88 OutlineParams (method.GetParameters ());
92 WriteGenericConstraints (t.GetGenericArguments ());
99 o.Write (GetTypeName (t));
100 if (((parent != null && parent != typeof (object) && parent != typeof (ValueType)) || interfaces.Length != 0) && ! t.IsEnum) {
104 if (parent != null && parent != typeof (object) && parent != typeof (ValueType)) {
105 o.Write (FormatType (parent));
109 foreach (Type intf in interfaces) {
110 if (!first) o.Write (", ");
113 o.Write (FormatType (intf));
118 Type underlyingType = System.Enum.GetUnderlyingType (t);
119 if (underlyingType != typeof (int))
120 o.Write (" : {0}", FormatType (underlyingType));
123 WriteGenericConstraints (t.GetGenericArguments ());
129 bool is_first = true;
130 foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Static)) {
138 o.Indent--; o.WriteLine ("}");
144 foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) {
146 if (! ShowMember (ci))
153 OutlineConstructor (ci);
161 foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) {
163 if (! ShowMember (m))
166 if ((m.Attributes & MethodAttributes.SpecialName) != 0)
180 foreach (MethodInfo m in t.GetMethods (DefaultFlags)) {
182 if (! ShowMember (m))
185 if ((m.Attributes & MethodAttributes.SpecialName) == 0)
187 if (!(m.Name.StartsWith ("op_")))
201 foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) {
203 if (! ((pi.CanRead && ShowMember (pi.GetGetMethod (true))) ||
204 (pi.CanWrite && ShowMember (pi.GetSetMethod (true)))))
211 OutlineProperty (pi);
218 foreach (FieldInfo fi in t.GetFields (DefaultFlags)) {
220 if (! ShowMember (fi))
234 foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) {
236 if (! ShowMember (ei.GetAddMethod (true)))
250 foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) {
252 if (! ShowMember (ntype))
259 new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType ();
262 o.Indent--; o.WriteLine ("}");
265 BindingFlags DefaultFlags {
267 BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
270 f |= BindingFlags.DeclaredOnly;
276 // FIXME: add other interesting attributes?
277 void OutlineAttributes ()
279 if (t.IsSerializable)
280 o.WriteLine ("[Serializable]");
282 if (t.IsDefined (typeof (System.FlagsAttribute), true))
283 o.WriteLine ("[Flags]");
285 if (t.IsDefined (typeof (System.ObsoleteAttribute), true))
286 o.WriteLine ("[Obsolete]");
289 void OutlineEvent (EventInfo ei)
291 MethodBase accessor = ei.GetAddMethod (true);
293 o.Write (GetMethodVisibility (accessor));
295 o.Write (FormatType (ei.EventHandlerType));
301 void OutlineConstructor (ConstructorInfo ci)
303 o.Write (GetMethodVisibility (ci));
304 o.Write (RemoveGenericArity (t.Name));
306 OutlineParams (ci.GetParameters ());
311 void OutlineProperty (PropertyInfo pi)
313 ParameterInfo [] idxp = pi.GetIndexParameters ();
314 MethodBase g = pi.GetGetMethod (true);
315 MethodBase s = pi.GetSetMethod (true);
316 MethodBase accessor = g != null ? g : s;
318 if (pi.CanRead && pi.CanWrite) {
321 // Get the more accessible accessor
322 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
323 (s.Attributes & MethodAttributes.MemberAccessMask)) {
325 if (g.IsPublic) accessor = g;
326 else if (s.IsPublic) accessor = s;
327 else if (g.IsFamilyOrAssembly) accessor = g;
328 else if (s.IsFamilyOrAssembly) accessor = s;
329 else if (g.IsAssembly || g.IsFamily) accessor = g;
330 else if (s.IsAssembly || s.IsFamily) accessor = s;
334 o.Write (GetMethodVisibility (accessor));
335 o.Write (GetMethodModifiers (accessor));
336 o.Write (FormatType (pi.PropertyType));
339 if (idxp.Length == 0)
343 OutlineParams (idxp);
350 if (g != null && ShowMember (g)) {
351 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
352 (accessor.Attributes & MethodAttributes.MemberAccessMask))
353 o.Write (GetMethodVisibility (g));
354 o.WriteLine ("get;");
357 if (s != null && ShowMember (s)) {
358 if ((s.Attributes & MethodAttributes.MemberAccessMask) !=
359 (accessor.Attributes & MethodAttributes.MemberAccessMask))
360 o.Write (GetMethodVisibility (s));
361 o.WriteLine ("set;");
368 void OutlineMethod (MethodInfo mi)
370 if (MethodIsExplicitIfaceImpl (mi)) {
371 o.Write (FormatType (mi.ReturnType));
373 // MSFT has no way to get the method that we are overriding
374 // from the interface. this would allow us to pretty print
375 // the type name (and be more correct if there compiler
376 // were to do some strange naming thing).
378 o.Write (GetMethodVisibility (mi));
379 o.Write (GetMethodModifiers (mi));
380 o.Write (FormatType (mi.ReturnType));
386 o.Write (FormatGenericParams (mi.GetGenericArguments ()));
389 OutlineParams (mi.GetParameters ());
392 WriteGenericConstraints (mi.GetGenericArguments ());
397 void OutlineOperator (MethodInfo mi)
399 o.Write (GetMethodVisibility (mi));
400 o.Write (GetMethodModifiers (mi));
401 if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") {
402 o.Write (mi.Name.Substring (3).ToLower ());
403 o.Write (" operator ");
404 o.Write (FormatType (mi.ReturnType));
406 o.Write (FormatType (mi.ReturnType));
407 o.Write (" operator ");
408 o.Write (OperatorFromName (mi.Name));
411 OutlineParams (mi.GetParameters ());
415 void OutlineParams (ParameterInfo [] pi)
418 foreach (ParameterInfo p in pi) {
419 if (p.ParameterType.IsByRef) {
420 o.Write (p.IsOut ? "out " : "ref ");
421 o.Write (FormatType (p.ParameterType.GetElementType ()));
422 } else if (p.IsDefined (typeof (ParamArrayAttribute), false)) {
424 o.Write (FormatType (p.ParameterType));
426 o.Write (FormatType (p.ParameterType));
431 if (i + 1 < pi.Length)
437 void OutlineField (FieldInfo fi)
439 if (fi.IsPublic) o.Write ("public ");
440 if (fi.IsFamily) o.Write ("protected ");
441 if (fi.IsPrivate) o.Write ("private ");
442 if (fi.IsAssembly) o.Write ("internal ");
443 if (fi.IsLiteral) o.Write ("const ");
444 else if (fi.IsStatic) o.Write ("static ");
445 if (fi.IsInitOnly) o.Write ("readonly ");
447 o.Write (FormatType (fi.FieldType));
451 object v = fi.GetValue (this);
453 // TODO: Escape values here
456 o.Write ("'{0}'", v);
457 else if (v is string)
458 o.Write ("\"{0}\"", v);
460 o.Write (fi.GetValue (this));
465 static string GetMethodVisibility (MethodBase m)
467 // itnerfaces have no modifiers here
468 if (m.DeclaringType.IsInterface)
471 if (m.IsPublic) return "public ";
472 if (m.IsFamily) return "protected ";
473 if (m.IsPrivate) return "private ";
474 if (m.IsAssembly) return "internal ";
479 static string GetMethodModifiers (MethodBase method)
484 if (method.IsFinal) {
485 // This will happen if you have
487 // public void A () {}
488 // static void Main () {}
494 // A needs to be virtual (the CLR requires
495 // methods implementing an iface be virtual),
496 // but can not be inherited. It also can not
497 // be inherited. In C# this is represented
498 // with no special modifiers
500 if (method.IsVirtual)
505 // all interface methods are "virtual" but we don't say that in c#
506 if (method.IsVirtual && !method.DeclaringType.IsInterface) {
507 if (method.IsAbstract)
510 return ((method.Attributes & MethodAttributes.NewSlot) != 0) ?
518 static string GetTypeKind (Type t)
523 if (t.IsSubclassOf (typeof (System.MulticastDelegate)))
535 static string GetTypeVisibility (Type t)
537 switch (t.Attributes & TypeAttributes.VisibilityMask){
538 case TypeAttributes.Public:
539 case TypeAttributes.NestedPublic:
542 case TypeAttributes.NestedFamily:
543 case TypeAttributes.NestedFamANDAssem:
544 case TypeAttributes.NestedFamORAssem:
553 string FormatGenericParams (Type [] args)
555 StringBuilder sb = new StringBuilder ();
556 if (args.Length == 0)
560 for (int i = 0; i < args.Length; i++) {
563 sb.Append (FormatType (args [i]));
566 return sb.ToString ();
570 // TODO: fine tune this so that our output is less verbose. We need to figure
571 // out a way to do this while not making things confusing.
572 string FormatType (Type t)
574 string type = GetFullName (t);
576 if (!type.StartsWith ("System.")) {
577 if (t.Namespace == this.t.Namespace)
582 if (t.HasElementType) {
583 Type et = t.GetElementType ();
585 return FormatType (et) + " []";
587 return FormatType (et) + " *";
589 return "ref " + FormatType (et);
593 case "System.Byte": return "byte";
594 case "System.SByte": return "sbyte";
595 case "System.Int16": return "short";
596 case "System.Int32": return "int";
597 case "System.Int64": return "long";
599 case "System.UInt16": return "ushort";
600 case "System.UInt32": return "uint";
601 case "System.UInt64": return "ulong";
603 case "System.Single": return "float";
604 case "System.Double": return "double";
605 case "System.Decimal": return "decimal";
606 case "System.Boolean": return "bool";
607 case "System.Char": return "char";
608 case "System.String": return "string";
610 case "System.Object": return "object";
611 case "System.Void": return "void";
614 if (type.LastIndexOf(".") == 6)
615 return type.Substring(7);
618 // If the namespace of the type is the namespace of what
619 // we are printing (or is a member of one if its children
620 // don't print it. This basically means that in C# we would
621 // automatically get the namespace imported by virtue of the
622 // namespace {} block.
624 if (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace)
625 return type.Substring (t.Namespace.Length + 1);
630 public static string RemoveGenericArity (string name)
633 StringBuilder sb = new StringBuilder ();
634 while (start < name.Length) {
635 int pos = name.IndexOf ('`', start);
637 sb.Append (name.Substring (start));
640 sb.Append (name.Substring (start, pos-start));
644 while ((pos < name.Length) && Char.IsNumber (name [pos]))
650 return sb.ToString ();
653 string GetTypeName (Type t)
655 StringBuilder sb = new StringBuilder ();
657 return sb.ToString ();
660 void GetTypeName (StringBuilder sb, Type t)
662 sb.Append (RemoveGenericArity (t.Name));
664 sb.Append (FormatGenericParams (t.GetGenericArguments ()));
668 string GetFullName (Type t)
670 StringBuilder sb = new StringBuilder ();
671 GetFullName_recursed (sb, t, false);
672 return sb.ToString ();
675 void GetFullName_recursed (StringBuilder sb, Type t, bool recursed)
678 if (t.IsGenericParameter) {
684 if (t.DeclaringType != null) {
685 GetFullName_recursed (sb, t.DeclaringType, true);
690 string ns = t.Namespace;
691 if ((ns != null) && (ns != "")) {
701 void WriteGenericConstraints (Type [] args)
704 foreach (Type t in args) {
706 Type[] ifaces = TypeGetInterfaces (t, true);
708 GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
709 GenericParameterAttributes [] interesting = {
710 GenericParameterAttributes.ReferenceTypeConstraint,
711 GenericParameterAttributes.NotNullableValueTypeConstraint,
712 GenericParameterAttributes.DefaultConstructorConstraint
715 if (t.BaseType != typeof (object) || ifaces.Length != 0 || attrs != 0) {
717 o.Write (FormatType (t));
721 if (t.BaseType != typeof (object)) {
722 o.Write (FormatType (t.BaseType));
726 foreach (Type iface in ifaces) {
731 o.Write (FormatType (iface));
734 foreach (GenericParameterAttributes a in interesting) {
735 if ((attrs & a) == 0)
743 case GenericParameterAttributes.ReferenceTypeConstraint:
746 case GenericParameterAttributes.NotNullableValueTypeConstraint:
749 case GenericParameterAttributes.DefaultConstructorConstraint:
758 string OperatorFromName (string name)
761 case "op_UnaryPlus": return "+";
762 case "op_UnaryNegation": return "-";
763 case "op_LogicalNot": return "!";
764 case "op_OnesComplement": return "~";
765 case "op_Increment": return "++";
766 case "op_Decrement": return "--";
767 case "op_True": return "true";
768 case "op_False": return "false";
769 case "op_Addition": return "+";
770 case "op_Subtraction": return "-";
771 case "op_Multiply": return "*";
772 case "op_Division": return "/";
773 case "op_Modulus": return "%";
774 case "op_BitwiseAnd": return "&";
775 case "op_BitwiseOr": return "|";
776 case "op_ExclusiveOr": return "^";
777 case "op_LeftShift": return "<<";
778 case "op_RightShift": return ">>";
779 case "op_Equality": return "==";
780 case "op_Inequality": return "!=";
781 case "op_GreaterThan": return ">";
782 case "op_LessThan": return "<";
783 case "op_GreaterThanOrEqual": return ">=";
784 case "op_LessThanOrEqual": return "<=";
785 default: return name;
789 bool MethodIsExplicitIfaceImpl (MethodBase mb)
791 if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate))
794 // UGH msft has no way to get the info about what method is
795 // getting overriden. Another reason to use cecil :-)
797 //MethodInfo mi = mb as MethodInfo;
801 //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType);
802 //return mi.GetBaseDefinition ().DeclaringType.IsInterface;
804 // So, we guess that virtual final private methods only come
809 bool ShowMember (MemberInfo mi)
811 if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic)
817 if (filter_obsolete && mi.IsDefined (typeof (ObsoleteAttribute), false))
820 switch (mi.MemberType) {
821 case MemberTypes.Constructor:
822 case MemberTypes.Method:
823 MethodBase mb = mi as MethodBase;
825 if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly)
828 if (MethodIsExplicitIfaceImpl (mb))
834 case MemberTypes.Field:
835 FieldInfo fi = mi as FieldInfo;
837 if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly)
843 case MemberTypes.NestedType:
844 case MemberTypes.TypeInfo:
847 switch (t.Attributes & TypeAttributes.VisibilityMask){
848 case TypeAttributes.Public:
849 case TypeAttributes.NestedPublic:
850 case TypeAttributes.NestedFamily:
851 case TypeAttributes.NestedFamORAssem:
862 static Type [] TypeGetInterfaces (Type t, bool declonly)
864 Type [] ifaces = t.GetInterfaces ();
868 // Handle Object. Also, optimize for no interfaces
869 if (t.BaseType == null || ifaces.Length == 0)
872 ArrayList ar = new ArrayList ();
874 foreach (Type i in ifaces)
875 if (! i.IsAssignableFrom (t.BaseType))
878 return (Type []) ar.ToArray (typeof (Type));
882 public class Comparer : IComparer {
883 delegate int ComparerFunc (object a, object b);
887 Comparer (ComparerFunc f)
892 public int Compare (object a, object b)
897 static int CompareType (object a, object b)
899 Type type1 = (Type) a;
900 Type type2 = (Type) b;
902 if (type1.IsSubclassOf (typeof (System.MulticastDelegate)) != type2.IsSubclassOf (typeof (System.MulticastDelegate)))
903 return (type1.IsSubclassOf (typeof (System.MulticastDelegate)))? -1:1;
904 return string.Compare (type1.Name, type2.Name);
908 // static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType));
910 // static Type [] Sort (Type [] types)
912 // Array.Sort (types, TypeComparer);
916 static int CompareMemberInfo (object a, object b)
918 return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name);
921 static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo));
923 public static MemberInfo [] Sort (MemberInfo [] inf)
925 Array.Sort (inf, MemberInfoComparer);
929 static int CompareMethodBase (object a, object b)
931 MethodBase aa = (MethodBase) a, bb = (MethodBase) b;
933 if (aa.IsStatic == bb.IsStatic) {
934 int c = CompareMemberInfo (a, b);
937 ParameterInfo [] ap, bp;
940 // Sort overloads by the names of their types
941 // put methods with fewer params first.
944 ap = aa.GetParameters ();
945 bp = bb.GetParameters ();
946 int n = System.Math.Min (ap.Length, bp.Length);
948 for (int i = 0; i < n; i ++)
949 if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0)
952 return ap.Length.CompareTo (bp.Length);
960 static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase));
962 public static MethodBase [] Sort (MethodBase [] inf)
964 Array.Sort (inf, MethodBaseComparer);
968 static int ComparePropertyInfo (object a, object b)
970 PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b;
972 bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic;
973 bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic;
975 if (astatic == bstatic)
976 return CompareMemberInfo (a, b);
984 static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo));
986 public static PropertyInfo [] Sort (PropertyInfo [] inf)
988 Array.Sort (inf, PropertyInfoComparer);
992 static int CompareEventInfo (object a, object b)
994 EventInfo aa = (EventInfo) a, bb = (EventInfo) b;
996 bool astatic = aa.GetAddMethod (true).IsStatic;
997 bool bstatic = bb.GetAddMethod (true).IsStatic;
999 if (astatic == bstatic)
1000 return CompareMemberInfo (a, b);
1008 static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo));
1010 public static EventInfo [] Sort (EventInfo [] inf)
1012 Array.Sort (inf, EventInfoComparer);