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 {
46 public static class TypeExtensions {
47 public static string GetNamespace (this Type t)
49 // IKVM crashes here with a null ref sometimes
58 public class Outline {
65 Type type_multicast_delegate, type_object, type_value_type, type_int, type_flags_attribute, type_obsolete_attribute, type_param_array_attribute;
71 public Outline (System.Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete)
73 throw new NotImplementedException ();
76 public Outline (Universe universe, Assembly mscorlib, Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete)
79 throw new ArgumentNullException ("universe");
81 throw new ArgumentNullException ("mscorlib");
82 this.universe = universe;
83 this.mscorlib = mscorlib;
85 this.o = new IndentedTextWriter (output, "\t");
86 this.declared_only = declared_only;
87 this.show_private = show_private;
88 this.filter_obsolete = filter_obsolete;
90 type_multicast_delegate = mscorlib.GetType("System.MulticastDelegate");
91 type_object = mscorlib.GetType ("System.Object");
92 type_value_type = mscorlib.GetType ("System.ValueType");
93 type_int = mscorlib.GetType ("System.Int32");
94 type_flags_attribute = mscorlib.GetType ("System.FlagsAttribute");
95 type_obsolete_attribute = mscorlib.GetType ("System.ObsoleteAttribute");
96 type_param_array_attribute = mscorlib.GetType ("System.ParamArrayAttribute");
99 public Outline (Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete)
102 this.o = new IndentedTextWriter (output, "\t");
103 this.declared_only = declared_only;
104 this.show_private = show_private;
105 this.filter_obsolete = filter_obsolete;
107 type_multicast_delegate = typeof (System.MulticastDelegate);
108 type_object = typeof (object);
109 type_value_type = typeof (ValueType);
110 type_int = typeof (int);
111 type_flags_attribute = typeof (FlagsAttribute);
112 type_obsolete_attribute = typeof (ObsoleteAttribute);
113 type_param_array_attribute = typeof (ParamArrayAttribute);
117 public void OutlineType ()
121 OutlineAttributes ();
122 o.Write (GetTypeVisibility (t));
124 if (t.IsClass && !t.IsSubclassOf (type_multicast_delegate)) {
126 o.Write (t.IsAbstract ? " static" : " sealed");
127 else if (t.IsAbstract)
128 o.Write (" abstract");
132 o.Write (GetTypeKind (t));
135 Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, declared_only));
136 Type parent = t.BaseType;
138 if (t.IsSubclassOf (type_multicast_delegate)) {
141 method = t.GetMethod ("Invoke");
143 o.Write (FormatType (method.ReturnType));
145 o.Write (GetTypeName (t));
147 OutlineParams (method.GetParameters ());
150 WriteGenericConstraints (t.GetGenericArguments ());
156 o.Write (GetTypeName (t));
157 if (((parent != null && parent != type_object && parent != type_value_type) || interfaces.Length != 0) && ! t.IsEnum) {
161 if (parent != null && parent != type_object && parent != type_value_type) {
162 o.Write (FormatType (parent));
166 foreach (Type intf in interfaces) {
167 if (!first) o.Write (", ");
170 o.Write (FormatType (intf));
175 Type underlyingType = t.GetEnumUnderlyingType ();
176 if (underlyingType != type_int)
177 o.Write (" : {0}", FormatType (underlyingType));
179 WriteGenericConstraints (t.GetGenericArguments ());
184 bool is_first = true;
185 foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Static)) {
193 o.Indent--; o.WriteLine ("}");
199 foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) {
200 if (! ShowMember (ci))
207 OutlineMemberAttribute (ci);
208 OutlineConstructor (ci);
216 foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) {
218 if (! ShowMember (m))
221 if ((m.Attributes & MethodAttributes.SpecialName) != 0)
228 OutlineMemberAttribute (m);
236 foreach (MethodInfo m in t.GetMethods (DefaultFlags)) {
238 if (! ShowMember (m))
241 if ((m.Attributes & MethodAttributes.SpecialName) == 0)
243 if (!(m.Name.StartsWith ("op_")))
250 OutlineMemberAttribute (m);
258 foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) {
260 if (! ((pi.CanRead && ShowMember (pi.GetGetMethod (true))) ||
261 (pi.CanWrite && ShowMember (pi.GetSetMethod (true)))))
268 OutlineMemberAttribute (pi);
269 OutlineProperty (pi);
276 foreach (FieldInfo fi in t.GetFields (DefaultFlags)) {
278 if (! ShowMember (fi))
285 OutlineMemberAttribute (fi);
293 foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) {
295 if (! ShowMember (ei.GetAddMethod (true)))
302 OutlineMemberAttribute (ei);
310 foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) {
312 if (! ShowMember (ntype))
320 new Outline (universe, mscorlib, ntype, o, declared_only, show_private, filter_obsolete).OutlineType ();
322 new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType ();
326 o.Indent--; o.WriteLine ("}");
329 BindingFlags DefaultFlags {
331 BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
334 f |= BindingFlags.DeclaredOnly;
340 // FIXME: add other interesting attributes?
341 void OutlineAttributes ()
343 if (t.IsSerializable)
344 o.WriteLine ("[Serializable]");
346 if (t.IsDefined (type_flags_attribute, true))
347 o.WriteLine ("[Flags]");
349 if (t.IsDefined (type_obsolete_attribute, true))
350 o.WriteLine ("[Obsolete]");
353 void OutlineMemberAttribute (MemberInfo mi)
355 var attrs = mi.GetCustomAttributesData ();
359 foreach (var attr in attrs)
363 void OutlineEvent (EventInfo ei)
365 MethodBase accessor = ei.GetAddMethod (true);
367 o.Write (GetMethodVisibility (accessor));
369 o.Write (FormatType (ei.EventHandlerType));
375 void OutlineConstructor (ConstructorInfo ci)
377 o.Write (GetMethodVisibility (ci));
378 o.Write (RemoveGenericArity (t.Name));
380 OutlineParams (ci.GetParameters ());
385 void OutlineProperty (PropertyInfo pi)
387 ParameterInfo [] idxp = pi.GetIndexParameters ();
388 MethodBase g = pi.GetGetMethod (true);
389 MethodBase s = pi.GetSetMethod (true);
390 MethodBase accessor = g != null ? g : s;
392 if (pi.CanRead && pi.CanWrite) {
395 // Get the more accessible accessor
396 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
397 (s.Attributes & MethodAttributes.MemberAccessMask)) {
399 if (g.IsPublic) accessor = g;
400 else if (s.IsPublic) accessor = s;
401 else if (g.IsFamilyOrAssembly) accessor = g;
402 else if (s.IsFamilyOrAssembly) accessor = s;
403 else if (g.IsAssembly || g.IsFamily) accessor = g;
404 else if (s.IsAssembly || s.IsFamily) accessor = s;
408 o.Write (GetMethodVisibility (accessor));
409 o.Write (GetMethodModifiers (accessor));
410 o.Write (FormatType (pi.PropertyType));
413 if (idxp.Length == 0)
417 OutlineParams (idxp);
424 if (g != null && ShowMember (g)) {
425 if ((g.Attributes & MethodAttributes.MemberAccessMask) !=
426 (accessor.Attributes & MethodAttributes.MemberAccessMask))
427 o.Write (GetMethodVisibility (g));
428 o.WriteLine ("get;");
431 if (s != null && ShowMember (s)) {
432 if ((s.Attributes & MethodAttributes.MemberAccessMask) !=
433 (accessor.Attributes & MethodAttributes.MemberAccessMask))
434 o.Write (GetMethodVisibility (s));
435 o.WriteLine ("set;");
442 void OutlineMethod (MethodInfo mi)
444 if (MethodIsExplicitIfaceImpl (mi)) {
445 o.Write (FormatType (mi.ReturnType));
447 // MSFT has no way to get the method that we are overriding
448 // from the interface. this would allow us to pretty print
449 // the type name (and be more correct if there compiler
450 // were to do some strange naming thing).
452 o.Write (GetMethodVisibility (mi));
453 o.Write (GetMethodModifiers (mi));
454 o.Write (FormatType (mi.ReturnType));
459 o.Write (FormatGenericParams (mi.GetGenericArguments ()));
461 OutlineParams (mi.GetParameters ());
463 WriteGenericConstraints (mi.GetGenericArguments ());
467 void OutlineOperator (MethodInfo mi)
469 o.Write (GetMethodVisibility (mi));
470 o.Write (GetMethodModifiers (mi));
471 if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") {
472 o.Write (mi.Name.Substring (3).ToLower ());
473 o.Write (" operator ");
474 o.Write (FormatType (mi.ReturnType));
476 o.Write (FormatType (mi.ReturnType));
477 o.Write (" operator ");
478 o.Write (OperatorFromName (mi.Name));
481 OutlineParams (mi.GetParameters ());
485 void OutlineParams (ParameterInfo [] pi)
488 foreach (ParameterInfo p in pi) {
489 if (p.ParameterType.IsByRef) {
490 o.Write (p.IsOut ? "out " : "ref ");
491 o.Write (FormatType (p.ParameterType.GetElementType ()));
492 } else if (p.IsDefined (type_param_array_attribute, false)) {
494 o.Write (FormatType (p.ParameterType));
496 o.Write (FormatType (p.ParameterType));
501 if (i + 1 < pi.Length)
507 void OutlineField (FieldInfo fi)
509 if (fi.IsPublic) o.Write ("public ");
510 if (fi.IsFamily) o.Write ("protected ");
511 if (fi.IsPrivate) o.Write ("private ");
512 if (fi.IsAssembly) o.Write ("internal ");
513 if (fi.IsLiteral) o.Write ("const ");
514 else if (fi.IsStatic) o.Write ("static ");
515 if (fi.IsInitOnly) o.Write ("readonly ");
517 o.Write (FormatType (fi.FieldType));
521 object v = fi.GetRawConstantValue ();
523 // TODO: Escape values here
526 o.Write ("'{0}'", v);
527 else if (v is string)
528 o.Write ("\"{0}\"", v);
530 o.Write (fi.GetRawConstantValue ());
535 static string GetMethodVisibility (MethodBase m)
537 // itnerfaces have no modifiers here
538 if (m.DeclaringType.IsInterface)
541 if (m.IsPublic) return "public ";
542 if (m.IsFamily) return "protected ";
543 if (m.IsPrivate) return "private ";
544 if (m.IsAssembly) return "internal ";
549 static string GetMethodModifiers (MethodBase method)
554 if (method.IsFinal) {
555 // This will happen if you have
557 // public void A () {}
558 // static void Main () {}
564 // A needs to be virtual (the CLR requires
565 // methods implementing an iface be virtual),
566 // but can not be inherited. It also can not
567 // be inherited. In C# this is represented
568 // with no special modifiers
570 if (method.IsVirtual)
575 // all interface methods are "virtual" but we don't say that in c#
576 if (method.IsVirtual && !method.DeclaringType.IsInterface) {
577 if (method.IsAbstract)
580 return ((method.Attributes & MethodAttributes.NewSlot) != 0) ?
588 string GetTypeKind (Type t)
593 if (t.IsSubclassOf (type_multicast_delegate))
605 static string GetTypeVisibility (Type t)
607 switch (t.Attributes & TypeAttributes.VisibilityMask){
608 case TypeAttributes.Public:
609 case TypeAttributes.NestedPublic:
612 case TypeAttributes.NestedFamily:
613 case TypeAttributes.NestedFamANDAssem:
614 case TypeAttributes.NestedFamORAssem:
622 string FormatGenericParams (Type [] args)
624 StringBuilder sb = new StringBuilder ();
625 if (args.Length == 0)
629 for (int i = 0; i < args.Length; i++) {
632 sb.Append (FormatType (args [i]));
635 return sb.ToString ();
638 // TODO: fine tune this so that our output is less verbose. We need to figure
639 // out a way to do this while not making things confusing.
640 string FormatType (Type t)
645 string type = GetFullName (t);
647 return t.ToString ();
649 if (!type.StartsWith ("System.")) {
650 if (type.IndexOf (".") == -1)
652 if (t.GetNamespace () == this.t.GetNamespace ())
657 if (t.HasElementType) {
658 Type et = t.GetElementType ();
660 return FormatType (et) + " []";
662 return FormatType (et) + " *";
664 return "ref " + FormatType (et);
668 case "System.Byte": return "byte";
669 case "System.SByte": return "sbyte";
670 case "System.Int16": return "short";
671 case "System.Int32": return "int";
672 case "System.Int64": return "long";
674 case "System.UInt16": return "ushort";
675 case "System.UInt32": return "uint";
676 case "System.UInt64": return "ulong";
678 case "System.Single": return "float";
679 case "System.Double": return "double";
680 case "System.Decimal": return "decimal";
681 case "System.Boolean": return "bool";
682 case "System.Char": return "char";
683 case "System.String": return "string";
685 case "System.Object": return "object";
686 case "System.Void": return "void";
689 if (type.LastIndexOf(".") == 6)
690 return type.Substring(7);
693 // If the namespace of the type is the namespace of what
694 // we are printing (or is a member of one if its children
695 // don't print it. This basically means that in C# we would
696 // automatically get the namespace imported by virtue of the
697 // namespace {} block.
699 if (this.t.Namespace != null && (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace))
700 return type.Substring (t.Namespace.Length + 1);
705 public static string RemoveGenericArity (string name)
708 StringBuilder sb = new StringBuilder ();
709 while (start < name.Length) {
710 int pos = name.IndexOf ('`', start);
712 sb.Append (name.Substring (start));
715 sb.Append (name.Substring (start, pos-start));
719 while ((pos < name.Length) && Char.IsNumber (name [pos]))
725 return sb.ToString ();
728 string GetTypeName (Type t)
730 StringBuilder sb = new StringBuilder ();
732 return sb.ToString ();
735 void GetTypeName (StringBuilder sb, Type t)
737 sb.Append (RemoveGenericArity (t.Name));
738 sb.Append (FormatGenericParams (t.GetGenericArguments ()));
741 string GetFullName (Type t)
743 StringBuilder sb = new StringBuilder ();
744 GetFullName_recursed (sb, t, false);
745 return sb.ToString ();
748 void GetFullName_recursed (StringBuilder sb, Type t, bool recursed)
750 if (t.IsGenericParameter) {
755 if (t.DeclaringType != null) {
756 GetFullName_recursed (sb, t.DeclaringType, true);
762 ns = t.GetNamespace ();
763 if ((ns != null) && (ns != "")) {
772 void WriteGenericConstraints (Type [] args)
775 foreach (Type t in args) {
777 Type[] ifaces = TypeGetInterfaces (t, true);
779 GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
780 GenericParameterAttributes [] interesting = {
781 GenericParameterAttributes.ReferenceTypeConstraint,
782 GenericParameterAttributes.NotNullableValueTypeConstraint,
783 GenericParameterAttributes.DefaultConstructorConstraint
786 if (t.BaseType != type_object || ifaces.Length != 0 || attrs != 0) {
788 o.Write (FormatType (t));
792 if (t.BaseType != type_object) {
793 o.Write (FormatType (t.BaseType));
797 foreach (Type iface in ifaces) {
802 o.Write (FormatType (iface));
805 foreach (GenericParameterAttributes a in interesting) {
806 if ((attrs & a) == 0)
814 case GenericParameterAttributes.ReferenceTypeConstraint:
817 case GenericParameterAttributes.NotNullableValueTypeConstraint:
820 case GenericParameterAttributes.DefaultConstructorConstraint:
828 string OperatorFromName (string name)
831 case "op_UnaryPlus": return "+";
832 case "op_UnaryNegation": return "-";
833 case "op_LogicalNot": return "!";
834 case "op_OnesComplement": return "~";
835 case "op_Increment": return "++";
836 case "op_Decrement": return "--";
837 case "op_True": return "true";
838 case "op_False": return "false";
839 case "op_Addition": return "+";
840 case "op_Subtraction": return "-";
841 case "op_Multiply": return "*";
842 case "op_Division": return "/";
843 case "op_Modulus": return "%";
844 case "op_BitwiseAnd": return "&";
845 case "op_BitwiseOr": return "|";
846 case "op_ExclusiveOr": return "^";
847 case "op_LeftShift": return "<<";
848 case "op_RightShift": return ">>";
849 case "op_Equality": return "==";
850 case "op_Inequality": return "!=";
851 case "op_GreaterThan": return ">";
852 case "op_LessThan": return "<";
853 case "op_GreaterThanOrEqual": return ">=";
854 case "op_LessThanOrEqual": return "<=";
855 default: return name;
859 bool MethodIsExplicitIfaceImpl (MethodBase mb)
861 if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate))
864 // UGH msft has no way to get the info about what method is
865 // getting overriden. Another reason to use cecil :-)
867 //MethodInfo mi = mb as MethodInfo;
871 //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType);
872 //return mi.GetBaseDefinition ().DeclaringType.IsInterface;
874 // So, we guess that virtual final private methods only come
879 bool ShowMember (MemberInfo mi)
881 if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic)
887 if (filter_obsolete && mi.IsDefined (type_obsolete_attribute, false))
890 switch (mi.MemberType) {
891 case MemberTypes.Constructor:
892 case MemberTypes.Method:
893 MethodBase mb = mi as MethodBase;
895 if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly)
898 if (MethodIsExplicitIfaceImpl (mb))
904 case MemberTypes.Field:
905 FieldInfo fi = mi as FieldInfo;
907 if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly)
913 case MemberTypes.NestedType:
914 case MemberTypes.TypeInfo:
917 switch (t.Attributes & TypeAttributes.VisibilityMask){
918 case TypeAttributes.Public:
919 case TypeAttributes.NestedPublic:
920 case TypeAttributes.NestedFamily:
921 case TypeAttributes.NestedFamORAssem:
932 static Type [] TypeGetInterfaces (Type t, bool declonly)
934 if (t.IsGenericParameter)
937 Type [] ifaces = t.GetInterfaces ();
941 // Handle Object. Also, optimize for no interfaces
942 if (t.BaseType == null || ifaces.Length == 0)
945 ArrayList ar = new ArrayList ();
947 foreach (Type i in ifaces)
948 if (! i.IsAssignableFrom (t.BaseType))
951 return (Type []) ar.ToArray (typeof (Type));
955 public class Comparer : IComparer {
956 delegate int ComparerFunc (object a, object b);
960 Comparer (ComparerFunc f)
965 public int Compare (object a, object b)
970 static int CompareType (object a, object b)
972 Type type1 = (Type) a;
973 Type type2 = (Type) b;
975 return string.Compare (type1.Name, type2.Name);
979 // static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType));
981 // static Type [] Sort (Type [] types)
983 // Array.Sort (types, TypeComparer);
987 static int CompareMemberInfo (object a, object b)
989 return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name);
992 static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo));
994 public static MemberInfo [] Sort (MemberInfo [] inf)
996 Array.Sort (inf, MemberInfoComparer);
1000 static int CompareMethodBase (object a, object b)
1002 MethodBase aa = (MethodBase) a, bb = (MethodBase) b;
1004 if (aa.IsStatic == bb.IsStatic) {
1005 int c = CompareMemberInfo (a, b);
1008 ParameterInfo [] ap, bp;
1011 // Sort overloads by the names of their types
1012 // put methods with fewer params first.
1015 ap = aa.GetParameters ();
1016 bp = bb.GetParameters ();
1017 int n = System.Math.Min (ap.Length, bp.Length);
1019 for (int i = 0; i < n; i ++)
1020 if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0)
1023 return ap.Length.CompareTo (bp.Length);
1031 static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase));
1033 public static MethodBase [] Sort (MethodBase [] inf)
1035 Array.Sort (inf, MethodBaseComparer);
1039 static int ComparePropertyInfo (object a, object b)
1041 PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b;
1043 bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic;
1044 bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic;
1046 if (astatic == bstatic)
1047 return CompareMemberInfo (a, b);
1055 static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo));
1057 public static PropertyInfo [] Sort (PropertyInfo [] inf)
1059 Array.Sort (inf, PropertyInfoComparer);
1063 static int CompareEventInfo (object a, object b)
1065 EventInfo aa = (EventInfo) a, bb = (EventInfo) b;
1067 bool astatic = aa.GetAddMethod (true).IsStatic;
1068 bool bstatic = bb.GetAddMethod (true).IsStatic;
1070 if (astatic == bstatic)
1071 return CompareMemberInfo (a, b);
1079 static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo));
1081 public static EventInfo [] Sort (EventInfo [] inf)
1083 Array.Sort (inf, EventInfoComparer);