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.Collections;
34 using System.CodeDom.Compiler;
38 using IKVM.Reflection;
39 using Type=IKVM.Reflection.Type;
41 using System.Reflection;
44 namespace Mono.CSharp {
45 public class Outline {
52 Type type_multicast_delegate, type_object, type_value_type, type_int, type_flags_attribute, type_obsolete_attribute, type_param_array_attribute;
58 public Outline (Universe universe, Assembly mscorlib, Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete)
61 throw new ArgumentNullException ("universe");
63 throw new ArgumentNullException ("mscorlib");
64 this.universe = universe;
65 this.mscorlib = mscorlib;
67 this.o = new IndentedTextWriter (output, "\t");
68 this.declared_only = declared_only;
69 this.show_private = show_private;
70 this.filter_obsolete = filter_obsolete;
72 type_multicast_delegate = mscorlib.GetType("System.MulticastDelegate");
73 type_object = mscorlib.GetType ("System.Object");
74 type_value_type = mscorlib.GetType ("System.ValueType");
75 type_int = mscorlib.GetType ("System.Int32");
76 type_flags_attribute = mscorlib.GetType ("System.FlagsAttribute");
77 type_obsolete_attribute = mscorlib.GetType ("System.ObsoleteAttribute");
78 type_param_array_attribute = mscorlib.GetType ("System.ParamArrayAttribute");
81 public Outline (Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete)
84 this.o = new IndentedTextWriter (output, "\t");
85 this.declared_only = declared_only;
86 this.show_private = show_private;
87 this.filter_obsolete = filter_obsolete;
89 type_multicast_delegate = typeof (System.MulticastDelegate);
90 type_object = typeof (object);
91 type_value_type = typeof (ValueType);
92 type_int = typeof (int);
93 type_flags_attribute = typeof (FlagsAttribute);
94 type_obsolete_attribute = typeof (ObsoleteAttribuet);
95 type_param_array_attribute = typeof (ParamArrayAttribute);
99 public void OutlineType ()
103 OutlineAttributes ();
104 o.Write (GetTypeVisibility (t));
106 if (t.IsClass && !t.IsSubclassOf (type_multicast_delegate)) {
108 o.Write (t.IsAbstract ? " static" : " sealed");
109 else if (t.IsAbstract)
110 o.Write (" abstract");
114 o.Write (GetTypeKind (t));
117 Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, declared_only));
118 Type parent = t.BaseType;
120 if (t.IsSubclassOf (type_multicast_delegate)) {
123 method = t.GetMethod ("Invoke");
125 o.Write (FormatType (method.ReturnType));
127 o.Write (GetTypeName (t));
129 OutlineParams (method.GetParameters ());
132 WriteGenericConstraints (t.GetGenericArguments ());
138 o.Write (GetTypeName (t));
139 if (((parent != null && parent != type_object && parent != type_value_type) || interfaces.Length != 0) && ! t.IsEnum) {
143 if (parent != null && parent != type_object && parent != type_value_type) {
144 o.Write (FormatType (parent));
148 foreach (Type intf in interfaces) {
149 if (!first) o.Write (", ");
152 o.Write (FormatType (intf));
157 Type underlyingType = t.GetEnumUnderlyingType ();
158 if (underlyingType != type_int)
159 o.Write (" : {0}", FormatType (underlyingType));
161 WriteGenericConstraints (t.GetGenericArguments ());
166 bool is_first = true;
167 foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Static)) {
175 o.Indent--; o.WriteLine ("}");
181 foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) {
182 if (! ShowMember (ci))
189 OutlineMemberAttribute (ci);
190 OutlineConstructor (ci);
198 foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) {
200 if (! ShowMember (m))
203 if ((m.Attributes & MethodAttributes.SpecialName) != 0)
210 OutlineMemberAttribute (m);
218 foreach (MethodInfo m in t.GetMethods (DefaultFlags)) {
220 if (! ShowMember (m))
223 if ((m.Attributes & MethodAttributes.SpecialName) == 0)
225 if (!(m.Name.StartsWith ("op_")))
232 OutlineMemberAttribute (m);
240 foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) {
242 if (! ((pi.CanRead && ShowMember (pi.GetGetMethod (true))) ||
243 (pi.CanWrite && ShowMember (pi.GetSetMethod (true)))))
250 OutlineMemberAttribute (pi);
251 OutlineProperty (pi);
258 foreach (FieldInfo fi in t.GetFields (DefaultFlags)) {
260 if (! ShowMember (fi))
267 OutlineMemberAttribute (fi);
275 foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) {
277 if (! ShowMember (ei.GetAddMethod (true)))
284 OutlineMemberAttribute (ei);
292 foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) {
294 if (! ShowMember (ntype))
302 new Outline (universe, mscorlib, ntype, o, declared_only, show_private, filter_obsolete).OutlineType ();
304 new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType ();
308 o.Indent--; o.WriteLine ("}");
311 BindingFlags DefaultFlags {
313 BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
316 f |= BindingFlags.DeclaredOnly;
322 // FIXME: add other interesting attributes?
323 void OutlineAttributes ()
325 if (t.IsSerializable)
326 o.WriteLine ("[Serializable]");
328 if (t.IsDefined (type_flags_attribute, true))
329 o.WriteLine ("[Flags]");
331 if (t.IsDefined (type_obsolete_attribute, true))
332 o.WriteLine ("[Obsolete]");
335 void OutlineMemberAttribute (MemberInfo mi)
337 var attrs = mi.GetCustomAttributesData ();
341 foreach (var attr in attrs)
345 void OutlineEvent (EventInfo ei)
347 MethodBase accessor = ei.GetAddMethod (true);
349 o.Write (GetMethodVisibility (accessor));
351 o.Write (FormatType (ei.EventHandlerType));
357 void OutlineConstructor (ConstructorInfo ci)
359 o.Write (GetMethodVisibility (ci));
360 o.Write (RemoveGenericArity (t.Name));
362 OutlineParams (ci.GetParameters ());
367 void OutlineProperty (PropertyInfo pi)
369 ParameterInfo [] idxp = pi.GetIndexParameters ();
370 MethodBase g = pi.GetGetMethod (true);
371 MethodBase s = pi.GetSetMethod (true);
372 MethodBase accessor = g != null ? g : s;
374 if (pi.CanRead && pi.CanWrite) {
377 // Get the more accessible accessor
378 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
379 (s.Attributes & MethodAttributes.MemberAccessMask)) {
381 if (g.IsPublic) accessor = g;
382 else if (s.IsPublic) accessor = s;
383 else if (g.IsFamilyOrAssembly) accessor = g;
384 else if (s.IsFamilyOrAssembly) accessor = s;
385 else if (g.IsAssembly || g.IsFamily) accessor = g;
386 else if (s.IsAssembly || s.IsFamily) accessor = s;
390 o.Write (GetMethodVisibility (accessor));
391 o.Write (GetMethodModifiers (accessor));
392 o.Write (FormatType (pi.PropertyType));
395 if (idxp.Length == 0)
399 OutlineParams (idxp);
406 if (g != null && ShowMember (g)) {
407 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
408 (accessor.Attributes & MethodAttributes.MemberAccessMask))
409 o.Write (GetMethodVisibility (g));
410 o.WriteLine ("get;");
413 if (s != null && ShowMember (s)) {
414 if ((s.Attributes & MethodAttributes.MemberAccessMask) !=
415 (accessor.Attributes & MethodAttributes.MemberAccessMask))
416 o.Write (GetMethodVisibility (s));
417 o.WriteLine ("set;");
424 void OutlineMethod (MethodInfo mi)
426 if (MethodIsExplicitIfaceImpl (mi)) {
427 o.Write (FormatType (mi.ReturnType));
429 // MSFT has no way to get the method that we are overriding
430 // from the interface. this would allow us to pretty print
431 // the type name (and be more correct if there compiler
432 // were to do some strange naming thing).
434 o.Write (GetMethodVisibility (mi));
435 o.Write (GetMethodModifiers (mi));
436 o.Write (FormatType (mi.ReturnType));
441 o.Write (FormatGenericParams (mi.GetGenericArguments ()));
443 OutlineParams (mi.GetParameters ());
445 WriteGenericConstraints (mi.GetGenericArguments ());
449 void OutlineOperator (MethodInfo mi)
451 o.Write (GetMethodVisibility (mi));
452 o.Write (GetMethodModifiers (mi));
453 if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") {
454 o.Write (mi.Name.Substring (3).ToLower ());
455 o.Write (" operator ");
456 o.Write (FormatType (mi.ReturnType));
458 o.Write (FormatType (mi.ReturnType));
459 o.Write (" operator ");
460 o.Write (OperatorFromName (mi.Name));
463 OutlineParams (mi.GetParameters ());
467 void OutlineParams (ParameterInfo [] pi)
470 foreach (ParameterInfo p in pi) {
471 if (p.ParameterType.IsByRef) {
472 o.Write (p.IsOut ? "out " : "ref ");
473 o.Write (FormatType (p.ParameterType.GetElementType ()));
474 } else if (p.IsDefined (type_param_array_attribute, false)) {
476 o.Write (FormatType (p.ParameterType));
478 o.Write (FormatType (p.ParameterType));
483 if (i + 1 < pi.Length)
489 void OutlineField (FieldInfo fi)
491 if (fi.IsPublic) o.Write ("public ");
492 if (fi.IsFamily) o.Write ("protected ");
493 if (fi.IsPrivate) o.Write ("private ");
494 if (fi.IsAssembly) o.Write ("internal ");
495 if (fi.IsLiteral) o.Write ("const ");
496 else if (fi.IsStatic) o.Write ("static ");
497 if (fi.IsInitOnly) o.Write ("readonly ");
499 o.Write (FormatType (fi.FieldType));
503 object v = fi.GetRawConstantValue ();
505 // TODO: Escape values here
508 o.Write ("'{0}'", v);
509 else if (v is string)
510 o.Write ("\"{0}\"", v);
512 o.Write (fi.GetRawConstantValue ());
517 static string GetMethodVisibility (MethodBase m)
519 // itnerfaces have no modifiers here
520 if (m.DeclaringType.IsInterface)
523 if (m.IsPublic) return "public ";
524 if (m.IsFamily) return "protected ";
525 if (m.IsPrivate) return "private ";
526 if (m.IsAssembly) return "internal ";
531 static string GetMethodModifiers (MethodBase method)
536 if (method.IsFinal) {
537 // This will happen if you have
539 // public void A () {}
540 // static void Main () {}
546 // A needs to be virtual (the CLR requires
547 // methods implementing an iface be virtual),
548 // but can not be inherited. It also can not
549 // be inherited. In C# this is represented
550 // with no special modifiers
552 if (method.IsVirtual)
557 // all interface methods are "virtual" but we don't say that in c#
558 if (method.IsVirtual && !method.DeclaringType.IsInterface) {
559 if (method.IsAbstract)
562 return ((method.Attributes & MethodAttributes.NewSlot) != 0) ?
570 string GetTypeKind (Type t)
575 if (t.IsSubclassOf (type_multicast_delegate))
587 static string GetTypeVisibility (Type t)
589 switch (t.Attributes & TypeAttributes.VisibilityMask){
590 case TypeAttributes.Public:
591 case TypeAttributes.NestedPublic:
594 case TypeAttributes.NestedFamily:
595 case TypeAttributes.NestedFamANDAssem:
596 case TypeAttributes.NestedFamORAssem:
604 string FormatGenericParams (Type [] args)
606 StringBuilder sb = new StringBuilder ();
607 if (args.Length == 0)
611 for (int i = 0; i < args.Length; i++) {
614 sb.Append (FormatType (args [i]));
617 return sb.ToString ();
620 // TODO: fine tune this so that our output is less verbose. We need to figure
621 // out a way to do this while not making things confusing.
622 string FormatType (Type t)
627 string type = GetFullName (t);
629 return t.ToString ();
631 if (!type.StartsWith ("System.")) {
632 if (type.IndexOf (".") == -1)
634 if (t.Namespace == this.t.Namespace)
639 if (t.HasElementType) {
640 Type et = t.GetElementType ();
642 return FormatType (et) + " []";
644 return FormatType (et) + " *";
646 return "ref " + FormatType (et);
650 case "System.Byte": return "byte";
651 case "System.SByte": return "sbyte";
652 case "System.Int16": return "short";
653 case "System.Int32": return "int";
654 case "System.Int64": return "long";
656 case "System.UInt16": return "ushort";
657 case "System.UInt32": return "uint";
658 case "System.UInt64": return "ulong";
660 case "System.Single": return "float";
661 case "System.Double": return "double";
662 case "System.Decimal": return "decimal";
663 case "System.Boolean": return "bool";
664 case "System.Char": return "char";
665 case "System.String": return "string";
667 case "System.Object": return "object";
668 case "System.Void": return "void";
671 if (type.LastIndexOf(".") == 6)
672 return type.Substring(7);
675 // If the namespace of the type is the namespace of what
676 // we are printing (or is a member of one if its children
677 // don't print it. This basically means that in C# we would
678 // automatically get the namespace imported by virtue of the
679 // namespace {} block.
681 if (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace)
682 return type.Substring (t.Namespace.Length + 1);
687 public static string RemoveGenericArity (string name)
690 StringBuilder sb = new StringBuilder ();
691 while (start < name.Length) {
692 int pos = name.IndexOf ('`', start);
694 sb.Append (name.Substring (start));
697 sb.Append (name.Substring (start, pos-start));
701 while ((pos < name.Length) && Char.IsNumber (name [pos]))
707 return sb.ToString ();
710 string GetTypeName (Type t)
712 StringBuilder sb = new StringBuilder ();
714 return sb.ToString ();
717 void GetTypeName (StringBuilder sb, Type t)
719 sb.Append (RemoveGenericArity (t.Name));
720 sb.Append (FormatGenericParams (t.GetGenericArguments ()));
723 string GetFullName (Type t)
725 StringBuilder sb = new StringBuilder ();
726 GetFullName_recursed (sb, t, false);
727 return sb.ToString ();
730 void GetFullName_recursed (StringBuilder sb, Type t, bool recursed)
732 if (t.IsGenericParameter) {
737 if (t.DeclaringType != null) {
738 GetFullName_recursed (sb, t.DeclaringType, true);
743 string ns = t.Namespace;
744 if ((ns != null) && (ns != "")) {
753 void WriteGenericConstraints (Type [] args)
756 foreach (Type t in args) {
758 Type[] ifaces = TypeGetInterfaces (t, true);
760 GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
761 GenericParameterAttributes [] interesting = {
762 GenericParameterAttributes.ReferenceTypeConstraint,
763 GenericParameterAttributes.NotNullableValueTypeConstraint,
764 GenericParameterAttributes.DefaultConstructorConstraint
767 if (t.BaseType != type_object || ifaces.Length != 0 || attrs != 0) {
769 o.Write (FormatType (t));
773 if (t.BaseType != type_object) {
774 o.Write (FormatType (t.BaseType));
778 foreach (Type iface in ifaces) {
783 o.Write (FormatType (iface));
786 foreach (GenericParameterAttributes a in interesting) {
787 if ((attrs & a) == 0)
795 case GenericParameterAttributes.ReferenceTypeConstraint:
798 case GenericParameterAttributes.NotNullableValueTypeConstraint:
801 case GenericParameterAttributes.DefaultConstructorConstraint:
809 string OperatorFromName (string name)
812 case "op_UnaryPlus": return "+";
813 case "op_UnaryNegation": return "-";
814 case "op_LogicalNot": return "!";
815 case "op_OnesComplement": return "~";
816 case "op_Increment": return "++";
817 case "op_Decrement": return "--";
818 case "op_True": return "true";
819 case "op_False": return "false";
820 case "op_Addition": return "+";
821 case "op_Subtraction": return "-";
822 case "op_Multiply": return "*";
823 case "op_Division": return "/";
824 case "op_Modulus": return "%";
825 case "op_BitwiseAnd": return "&";
826 case "op_BitwiseOr": return "|";
827 case "op_ExclusiveOr": return "^";
828 case "op_LeftShift": return "<<";
829 case "op_RightShift": return ">>";
830 case "op_Equality": return "==";
831 case "op_Inequality": return "!=";
832 case "op_GreaterThan": return ">";
833 case "op_LessThan": return "<";
834 case "op_GreaterThanOrEqual": return ">=";
835 case "op_LessThanOrEqual": return "<=";
836 default: return name;
840 bool MethodIsExplicitIfaceImpl (MethodBase mb)
842 if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate))
845 // UGH msft has no way to get the info about what method is
846 // getting overriden. Another reason to use cecil :-)
848 //MethodInfo mi = mb as MethodInfo;
852 //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType);
853 //return mi.GetBaseDefinition ().DeclaringType.IsInterface;
855 // So, we guess that virtual final private methods only come
860 bool ShowMember (MemberInfo mi)
862 if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic)
868 if (filter_obsolete && mi.IsDefined (type_obsolete_attribute, false))
871 switch (mi.MemberType) {
872 case MemberTypes.Constructor:
873 case MemberTypes.Method:
874 MethodBase mb = mi as MethodBase;
876 if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly)
879 if (MethodIsExplicitIfaceImpl (mb))
885 case MemberTypes.Field:
886 FieldInfo fi = mi as FieldInfo;
888 if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly)
894 case MemberTypes.NestedType:
895 case MemberTypes.TypeInfo:
898 switch (t.Attributes & TypeAttributes.VisibilityMask){
899 case TypeAttributes.Public:
900 case TypeAttributes.NestedPublic:
901 case TypeAttributes.NestedFamily:
902 case TypeAttributes.NestedFamORAssem:
913 static Type [] TypeGetInterfaces (Type t, bool declonly)
915 if (t.IsGenericParameter)
918 Type [] ifaces = t.GetInterfaces ();
922 // Handle Object. Also, optimize for no interfaces
923 if (t.BaseType == null || ifaces.Length == 0)
926 ArrayList ar = new ArrayList ();
928 foreach (Type i in ifaces)
929 if (! i.IsAssignableFrom (t.BaseType))
932 return (Type []) ar.ToArray (typeof (Type));
936 public class Comparer : IComparer {
937 delegate int ComparerFunc (object a, object b);
941 Comparer (ComparerFunc f)
946 public int Compare (object a, object b)
951 static int CompareType (object a, object b)
953 Type type1 = (Type) a;
954 Type type2 = (Type) b;
956 return string.Compare (type1.Name, type2.Name);
960 // static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType));
962 // static Type [] Sort (Type [] types)
964 // Array.Sort (types, TypeComparer);
968 static int CompareMemberInfo (object a, object b)
970 return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name);
973 static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo));
975 public static MemberInfo [] Sort (MemberInfo [] inf)
977 Array.Sort (inf, MemberInfoComparer);
981 static int CompareMethodBase (object a, object b)
983 MethodBase aa = (MethodBase) a, bb = (MethodBase) b;
985 if (aa.IsStatic == bb.IsStatic) {
986 int c = CompareMemberInfo (a, b);
989 ParameterInfo [] ap, bp;
992 // Sort overloads by the names of their types
993 // put methods with fewer params first.
996 ap = aa.GetParameters ();
997 bp = bb.GetParameters ();
998 int n = System.Math.Min (ap.Length, bp.Length);
1000 for (int i = 0; i < n; i ++)
1001 if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0)
1004 return ap.Length.CompareTo (bp.Length);
1012 static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase));
1014 public static MethodBase [] Sort (MethodBase [] inf)
1016 Array.Sort (inf, MethodBaseComparer);
1020 static int ComparePropertyInfo (object a, object b)
1022 PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b;
1024 bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic;
1025 bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic;
1027 if (astatic == bstatic)
1028 return CompareMemberInfo (a, b);
1036 static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo));
1038 public static PropertyInfo [] Sort (PropertyInfo [] inf)
1040 Array.Sort (inf, PropertyInfoComparer);
1044 static int CompareEventInfo (object a, object b)
1046 EventInfo aa = (EventInfo) a, bb = (EventInfo) b;
1048 bool astatic = aa.GetAddMethod (true).IsStatic;
1049 bool bstatic = bb.GetAddMethod (true).IsStatic;
1051 if (astatic == bstatic)
1052 return CompareMemberInfo (a, b);
1060 static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo));
1062 public static EventInfo [] Sort (EventInfo [] inf)
1064 Array.Sort (inf, EventInfoComparer);