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)) {
145 if (! ShowMember (ci))
152 OutlineMemberAttribute (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)
173 OutlineMemberAttribute (m);
181 foreach (MethodInfo m in t.GetMethods (DefaultFlags)) {
183 if (! ShowMember (m))
186 if ((m.Attributes & MethodAttributes.SpecialName) == 0)
188 if (!(m.Name.StartsWith ("op_")))
195 OutlineMemberAttribute (m);
203 foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) {
205 if (! ((pi.CanRead && ShowMember (pi.GetGetMethod (true))) ||
206 (pi.CanWrite && ShowMember (pi.GetSetMethod (true)))))
213 OutlineMemberAttribute (pi);
214 OutlineProperty (pi);
221 foreach (FieldInfo fi in t.GetFields (DefaultFlags)) {
223 if (! ShowMember (fi))
230 OutlineMemberAttribute (fi);
238 foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) {
240 if (! ShowMember (ei.GetAddMethod (true)))
247 OutlineMemberAttribute (ei);
255 foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) {
257 if (! ShowMember (ntype))
264 new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType ();
267 o.Indent--; o.WriteLine ("}");
270 BindingFlags DefaultFlags {
272 BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
275 f |= BindingFlags.DeclaredOnly;
281 // FIXME: add other interesting attributes?
282 void OutlineAttributes ()
284 if (t.IsSerializable)
285 o.WriteLine ("[Serializable]");
287 if (t.IsDefined (typeof (System.FlagsAttribute), true))
288 o.WriteLine ("[Flags]");
290 if (t.IsDefined (typeof (System.ObsoleteAttribute), true))
291 o.WriteLine ("[Obsolete]");
294 void OutlineMemberAttribute (MemberInfo mi)
296 if (!mi.IsDefined (typeof (System.ObsoleteAttribute), false))
298 var oa = mi.GetCustomAttributes (typeof (System.ObsoleteAttribute), false) [0] as ObsoleteAttribute;
299 var msg = oa.Message;
300 o.WriteLine ("[Obsolete{0}]", msg == null || msg == "" ? "" : string.Format ("(\"{0}\")", msg));
303 void OutlineEvent (EventInfo ei)
305 MethodBase accessor = ei.GetAddMethod (true);
307 o.Write (GetMethodVisibility (accessor));
309 o.Write (FormatType (ei.EventHandlerType));
315 void OutlineConstructor (ConstructorInfo ci)
317 o.Write (GetMethodVisibility (ci));
318 o.Write (RemoveGenericArity (t.Name));
320 OutlineParams (ci.GetParameters ());
325 void OutlineProperty (PropertyInfo pi)
327 ParameterInfo [] idxp = pi.GetIndexParameters ();
328 MethodBase g = pi.GetGetMethod (true);
329 MethodBase s = pi.GetSetMethod (true);
330 MethodBase accessor = g != null ? g : s;
332 if (pi.CanRead && pi.CanWrite) {
335 // Get the more accessible accessor
336 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
337 (s.Attributes & MethodAttributes.MemberAccessMask)) {
339 if (g.IsPublic) accessor = g;
340 else if (s.IsPublic) accessor = s;
341 else if (g.IsFamilyOrAssembly) accessor = g;
342 else if (s.IsFamilyOrAssembly) accessor = s;
343 else if (g.IsAssembly || g.IsFamily) accessor = g;
344 else if (s.IsAssembly || s.IsFamily) accessor = s;
348 o.Write (GetMethodVisibility (accessor));
349 o.Write (GetMethodModifiers (accessor));
350 o.Write (FormatType (pi.PropertyType));
353 if (idxp.Length == 0)
357 OutlineParams (idxp);
364 if (g != null && ShowMember (g)) {
365 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
366 (accessor.Attributes & MethodAttributes.MemberAccessMask))
367 o.Write (GetMethodVisibility (g));
368 o.WriteLine ("get;");
371 if (s != null && ShowMember (s)) {
372 if ((s.Attributes & MethodAttributes.MemberAccessMask) !=
373 (accessor.Attributes & MethodAttributes.MemberAccessMask))
374 o.Write (GetMethodVisibility (s));
375 o.WriteLine ("set;");
382 void OutlineMethod (MethodInfo mi)
384 if (MethodIsExplicitIfaceImpl (mi)) {
385 o.Write (FormatType (mi.ReturnType));
387 // MSFT has no way to get the method that we are overriding
388 // from the interface. this would allow us to pretty print
389 // the type name (and be more correct if there compiler
390 // were to do some strange naming thing).
392 o.Write (GetMethodVisibility (mi));
393 o.Write (GetMethodModifiers (mi));
394 o.Write (FormatType (mi.ReturnType));
400 o.Write (FormatGenericParams (mi.GetGenericArguments ()));
403 OutlineParams (mi.GetParameters ());
406 WriteGenericConstraints (mi.GetGenericArguments ());
411 void OutlineOperator (MethodInfo mi)
413 o.Write (GetMethodVisibility (mi));
414 o.Write (GetMethodModifiers (mi));
415 if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") {
416 o.Write (mi.Name.Substring (3).ToLower ());
417 o.Write (" operator ");
418 o.Write (FormatType (mi.ReturnType));
420 o.Write (FormatType (mi.ReturnType));
421 o.Write (" operator ");
422 o.Write (OperatorFromName (mi.Name));
425 OutlineParams (mi.GetParameters ());
429 void OutlineParams (ParameterInfo [] pi)
432 foreach (ParameterInfo p in pi) {
433 if (p.ParameterType.IsByRef) {
434 o.Write (p.IsOut ? "out " : "ref ");
435 o.Write (FormatType (p.ParameterType.GetElementType ()));
436 } else if (p.IsDefined (typeof (ParamArrayAttribute), false)) {
438 o.Write (FormatType (p.ParameterType));
440 o.Write (FormatType (p.ParameterType));
445 if (i + 1 < pi.Length)
451 void OutlineField (FieldInfo fi)
453 if (fi.IsPublic) o.Write ("public ");
454 if (fi.IsFamily) o.Write ("protected ");
455 if (fi.IsPrivate) o.Write ("private ");
456 if (fi.IsAssembly) o.Write ("internal ");
457 if (fi.IsLiteral) o.Write ("const ");
458 else if (fi.IsStatic) o.Write ("static ");
459 if (fi.IsInitOnly) o.Write ("readonly ");
461 o.Write (FormatType (fi.FieldType));
465 object v = fi.GetValue (this);
467 // TODO: Escape values here
470 o.Write ("'{0}'", v);
471 else if (v is string)
472 o.Write ("\"{0}\"", v);
474 o.Write (fi.GetValue (this));
479 static string GetMethodVisibility (MethodBase m)
481 // itnerfaces have no modifiers here
482 if (m.DeclaringType.IsInterface)
485 if (m.IsPublic) return "public ";
486 if (m.IsFamily) return "protected ";
487 if (m.IsPrivate) return "private ";
488 if (m.IsAssembly) return "internal ";
493 static string GetMethodModifiers (MethodBase method)
498 if (method.IsFinal) {
499 // This will happen if you have
501 // public void A () {}
502 // static void Main () {}
508 // A needs to be virtual (the CLR requires
509 // methods implementing an iface be virtual),
510 // but can not be inherited. It also can not
511 // be inherited. In C# this is represented
512 // with no special modifiers
514 if (method.IsVirtual)
519 // all interface methods are "virtual" but we don't say that in c#
520 if (method.IsVirtual && !method.DeclaringType.IsInterface) {
521 if (method.IsAbstract)
524 return ((method.Attributes & MethodAttributes.NewSlot) != 0) ?
532 static string GetTypeKind (Type t)
537 if (t.IsSubclassOf (typeof (System.MulticastDelegate)))
549 static string GetTypeVisibility (Type t)
551 switch (t.Attributes & TypeAttributes.VisibilityMask){
552 case TypeAttributes.Public:
553 case TypeAttributes.NestedPublic:
556 case TypeAttributes.NestedFamily:
557 case TypeAttributes.NestedFamANDAssem:
558 case TypeAttributes.NestedFamORAssem:
567 string FormatGenericParams (Type [] args)
569 StringBuilder sb = new StringBuilder ();
570 if (args.Length == 0)
574 for (int i = 0; i < args.Length; i++) {
577 sb.Append (FormatType (args [i]));
580 return sb.ToString ();
584 // TODO: fine tune this so that our output is less verbose. We need to figure
585 // out a way to do this while not making things confusing.
586 string FormatType (Type t)
591 string type = GetFullName (t);
593 return t.ToString ();
595 if (!type.StartsWith ("System.")) {
596 if (t.Namespace == this.t.Namespace)
601 if (t.HasElementType) {
602 Type et = t.GetElementType ();
604 return FormatType (et) + " []";
606 return FormatType (et) + " *";
608 return "ref " + FormatType (et);
612 case "System.Byte": return "byte";
613 case "System.SByte": return "sbyte";
614 case "System.Int16": return "short";
615 case "System.Int32": return "int";
616 case "System.Int64": return "long";
618 case "System.UInt16": return "ushort";
619 case "System.UInt32": return "uint";
620 case "System.UInt64": return "ulong";
622 case "System.Single": return "float";
623 case "System.Double": return "double";
624 case "System.Decimal": return "decimal";
625 case "System.Boolean": return "bool";
626 case "System.Char": return "char";
627 case "System.String": return "string";
629 case "System.Object": return "object";
630 case "System.Void": return "void";
633 if (type.LastIndexOf(".") == 6)
634 return type.Substring(7);
637 // If the namespace of the type is the namespace of what
638 // we are printing (or is a member of one if its children
639 // don't print it. This basically means that in C# we would
640 // automatically get the namespace imported by virtue of the
641 // namespace {} block.
643 if (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace)
644 return type.Substring (t.Namespace.Length + 1);
649 public static string RemoveGenericArity (string name)
652 StringBuilder sb = new StringBuilder ();
653 while (start < name.Length) {
654 int pos = name.IndexOf ('`', start);
656 sb.Append (name.Substring (start));
659 sb.Append (name.Substring (start, pos-start));
663 while ((pos < name.Length) && Char.IsNumber (name [pos]))
669 return sb.ToString ();
672 string GetTypeName (Type t)
674 StringBuilder sb = new StringBuilder ();
676 return sb.ToString ();
679 void GetTypeName (StringBuilder sb, Type t)
681 sb.Append (RemoveGenericArity (t.Name));
683 sb.Append (FormatGenericParams (t.GetGenericArguments ()));
687 string GetFullName (Type t)
689 StringBuilder sb = new StringBuilder ();
690 GetFullName_recursed (sb, t, false);
691 return sb.ToString ();
694 void GetFullName_recursed (StringBuilder sb, Type t, bool recursed)
697 if (t.IsGenericParameter) {
703 if (t.DeclaringType != null) {
704 GetFullName_recursed (sb, t.DeclaringType, true);
709 string ns = t.Namespace;
710 if ((ns != null) && (ns != "")) {
720 void WriteGenericConstraints (Type [] args)
723 foreach (Type t in args) {
725 Type[] ifaces = TypeGetInterfaces (t, true);
727 GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
728 GenericParameterAttributes [] interesting = {
729 GenericParameterAttributes.ReferenceTypeConstraint,
730 GenericParameterAttributes.NotNullableValueTypeConstraint,
731 GenericParameterAttributes.DefaultConstructorConstraint
734 if (t.BaseType != typeof (object) || ifaces.Length != 0 || attrs != 0) {
736 o.Write (FormatType (t));
740 if (t.BaseType != typeof (object)) {
741 o.Write (FormatType (t.BaseType));
745 foreach (Type iface in ifaces) {
750 o.Write (FormatType (iface));
753 foreach (GenericParameterAttributes a in interesting) {
754 if ((attrs & a) == 0)
762 case GenericParameterAttributes.ReferenceTypeConstraint:
765 case GenericParameterAttributes.NotNullableValueTypeConstraint:
768 case GenericParameterAttributes.DefaultConstructorConstraint:
777 string OperatorFromName (string name)
780 case "op_UnaryPlus": return "+";
781 case "op_UnaryNegation": return "-";
782 case "op_LogicalNot": return "!";
783 case "op_OnesComplement": return "~";
784 case "op_Increment": return "++";
785 case "op_Decrement": return "--";
786 case "op_True": return "true";
787 case "op_False": return "false";
788 case "op_Addition": return "+";
789 case "op_Subtraction": return "-";
790 case "op_Multiply": return "*";
791 case "op_Division": return "/";
792 case "op_Modulus": return "%";
793 case "op_BitwiseAnd": return "&";
794 case "op_BitwiseOr": return "|";
795 case "op_ExclusiveOr": return "^";
796 case "op_LeftShift": return "<<";
797 case "op_RightShift": return ">>";
798 case "op_Equality": return "==";
799 case "op_Inequality": return "!=";
800 case "op_GreaterThan": return ">";
801 case "op_LessThan": return "<";
802 case "op_GreaterThanOrEqual": return ">=";
803 case "op_LessThanOrEqual": return "<=";
804 default: return name;
808 bool MethodIsExplicitIfaceImpl (MethodBase mb)
810 if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate))
813 // UGH msft has no way to get the info about what method is
814 // getting overriden. Another reason to use cecil :-)
816 //MethodInfo mi = mb as MethodInfo;
820 //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType);
821 //return mi.GetBaseDefinition ().DeclaringType.IsInterface;
823 // So, we guess that virtual final private methods only come
828 bool ShowMember (MemberInfo mi)
830 if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic)
836 if (filter_obsolete && mi.IsDefined (typeof (ObsoleteAttribute), false))
839 switch (mi.MemberType) {
840 case MemberTypes.Constructor:
841 case MemberTypes.Method:
842 MethodBase mb = mi as MethodBase;
844 if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly)
847 if (MethodIsExplicitIfaceImpl (mb))
853 case MemberTypes.Field:
854 FieldInfo fi = mi as FieldInfo;
856 if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly)
862 case MemberTypes.NestedType:
863 case MemberTypes.TypeInfo:
866 switch (t.Attributes & TypeAttributes.VisibilityMask){
867 case TypeAttributes.Public:
868 case TypeAttributes.NestedPublic:
869 case TypeAttributes.NestedFamily:
870 case TypeAttributes.NestedFamORAssem:
881 static Type [] TypeGetInterfaces (Type t, bool declonly)
883 if (t.IsGenericParameter)
886 Type [] ifaces = t.GetInterfaces ();
890 // Handle Object. Also, optimize for no interfaces
891 if (t.BaseType == null || ifaces.Length == 0)
894 ArrayList ar = new ArrayList ();
896 foreach (Type i in ifaces)
897 if (! i.IsAssignableFrom (t.BaseType))
900 return (Type []) ar.ToArray (typeof (Type));
904 public class Comparer : IComparer {
905 delegate int ComparerFunc (object a, object b);
909 Comparer (ComparerFunc f)
914 public int Compare (object a, object b)
919 static int CompareType (object a, object b)
921 Type type1 = (Type) a;
922 Type type2 = (Type) b;
924 if (type1.IsSubclassOf (typeof (System.MulticastDelegate)) != type2.IsSubclassOf (typeof (System.MulticastDelegate)))
925 return (type1.IsSubclassOf (typeof (System.MulticastDelegate)))? -1:1;
926 return string.Compare (type1.Name, type2.Name);
930 // static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType));
932 // static Type [] Sort (Type [] types)
934 // Array.Sort (types, TypeComparer);
938 static int CompareMemberInfo (object a, object b)
940 return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name);
943 static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo));
945 public static MemberInfo [] Sort (MemberInfo [] inf)
947 Array.Sort (inf, MemberInfoComparer);
951 static int CompareMethodBase (object a, object b)
953 MethodBase aa = (MethodBase) a, bb = (MethodBase) b;
955 if (aa.IsStatic == bb.IsStatic) {
956 int c = CompareMemberInfo (a, b);
959 ParameterInfo [] ap, bp;
962 // Sort overloads by the names of their types
963 // put methods with fewer params first.
966 ap = aa.GetParameters ();
967 bp = bb.GetParameters ();
968 int n = System.Math.Min (ap.Length, bp.Length);
970 for (int i = 0; i < n; i ++)
971 if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0)
974 return ap.Length.CompareTo (bp.Length);
982 static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase));
984 public static MethodBase [] Sort (MethodBase [] inf)
986 Array.Sort (inf, MethodBaseComparer);
990 static int ComparePropertyInfo (object a, object b)
992 PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b;
994 bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic;
995 bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic;
997 if (astatic == bstatic)
998 return CompareMemberInfo (a, b);
1006 static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo));
1008 public static PropertyInfo [] Sort (PropertyInfo [] inf)
1010 Array.Sort (inf, PropertyInfoComparer);
1014 static int CompareEventInfo (object a, object b)
1016 EventInfo aa = (EventInfo) a, bb = (EventInfo) b;
1018 bool astatic = aa.GetAddMethod (true).IsStatic;
1019 bool bstatic = bb.GetAddMethod (true).IsStatic;
1021 if (astatic == bstatic)
1022 return CompareMemberInfo (a, b);
1030 static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo));
1032 public static EventInfo [] Sort (EventInfo [] inf)
1034 Array.Sort (inf, EventInfoComparer);