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
)
577 string type
= GetFullName (t
);
579 return t
.ToString ();
581 if (!type
.StartsWith ("System.")) {
582 if (t
.Namespace
== this.t
.Namespace
)
587 if (t
.HasElementType
) {
588 Type et
= t
.GetElementType ();
590 return FormatType (et
) + " []";
592 return FormatType (et
) + " *";
594 return "ref " + FormatType (et
);
598 case "System.Byte": return "byte";
599 case "System.SByte": return "sbyte";
600 case "System.Int16": return "short";
601 case "System.Int32": return "int";
602 case "System.Int64": return "long";
604 case "System.UInt16": return "ushort";
605 case "System.UInt32": return "uint";
606 case "System.UInt64": return "ulong";
608 case "System.Single": return "float";
609 case "System.Double": return "double";
610 case "System.Decimal": return "decimal";
611 case "System.Boolean": return "bool";
612 case "System.Char": return "char";
613 case "System.String": return "string";
615 case "System.Object": return "object";
616 case "System.Void": return "void";
619 if (type
.LastIndexOf(".") == 6)
620 return type
.Substring(7);
623 // If the namespace of the type is the namespace of what
624 // we are printing (or is a member of one if its children
625 // don't print it. This basically means that in C# we would
626 // automatically get the namespace imported by virtue of the
627 // namespace {} block.
629 if (this.t
.Namespace
.StartsWith (t
.Namespace
+ ".") || t
.Namespace
== this.t
.Namespace
)
630 return type
.Substring (t
.Namespace
.Length
+ 1);
635 public static string RemoveGenericArity (string name
)
638 StringBuilder sb
= new StringBuilder ();
639 while (start
< name
.Length
) {
640 int pos
= name
.IndexOf ('`', start
);
642 sb
.Append (name
.Substring (start
));
645 sb
.Append (name
.Substring (start
, pos
-start
));
649 while ((pos
< name
.Length
) && Char
.IsNumber (name
[pos
]))
655 return sb
.ToString ();
658 string GetTypeName (Type t
)
660 StringBuilder sb
= new StringBuilder ();
662 return sb
.ToString ();
665 void GetTypeName (StringBuilder sb
, Type t
)
667 sb
.Append (RemoveGenericArity (t
.Name
));
669 sb
.Append (FormatGenericParams (t
.GetGenericArguments ()));
673 string GetFullName (Type t
)
675 StringBuilder sb
= new StringBuilder ();
676 GetFullName_recursed (sb
, t
, false);
677 return sb
.ToString ();
680 void GetFullName_recursed (StringBuilder sb
, Type t
, bool recursed
)
683 if (t
.IsGenericParameter
) {
689 if (t
.DeclaringType
!= null) {
690 GetFullName_recursed (sb
, t
.DeclaringType
, true);
695 string ns
= t
.Namespace
;
696 if ((ns
!= null) && (ns
!= "")) {
706 void WriteGenericConstraints (Type
[] args
)
709 foreach (Type t
in args
) {
711 Type
[] ifaces
= TypeGetInterfaces (t
, true);
713 GenericParameterAttributes attrs
= t
.GenericParameterAttributes
& GenericParameterAttributes
.SpecialConstraintMask
;
714 GenericParameterAttributes
[] interesting
= {
715 GenericParameterAttributes
.ReferenceTypeConstraint
,
716 GenericParameterAttributes
.NotNullableValueTypeConstraint
,
717 GenericParameterAttributes
.DefaultConstructorConstraint
720 if (t
.BaseType
!= typeof (object) || ifaces
.Length
!= 0 || attrs
!= 0) {
722 o
.Write (FormatType (t
));
726 if (t
.BaseType
!= typeof (object)) {
727 o
.Write (FormatType (t
.BaseType
));
731 foreach (Type iface
in ifaces
) {
736 o
.Write (FormatType (iface
));
739 foreach (GenericParameterAttributes a
in interesting
) {
740 if ((attrs
& a
) == 0)
748 case GenericParameterAttributes
.ReferenceTypeConstraint
:
751 case GenericParameterAttributes
.NotNullableValueTypeConstraint
:
754 case GenericParameterAttributes
.DefaultConstructorConstraint
:
763 string OperatorFromName (string name
)
766 case "op_UnaryPlus": return "+";
767 case "op_UnaryNegation": return "-";
768 case "op_LogicalNot": return "!";
769 case "op_OnesComplement": return "~";
770 case "op_Increment": return "++";
771 case "op_Decrement": return "--";
772 case "op_True": return "true";
773 case "op_False": return "false";
774 case "op_Addition": return "+";
775 case "op_Subtraction": return "-";
776 case "op_Multiply": return "*";
777 case "op_Division": return "/";
778 case "op_Modulus": return "%";
779 case "op_BitwiseAnd": return "&";
780 case "op_BitwiseOr": return "|";
781 case "op_ExclusiveOr": return "^";
782 case "op_LeftShift": return "<<";
783 case "op_RightShift": return ">>";
784 case "op_Equality": return "==";
785 case "op_Inequality": return "!=";
786 case "op_GreaterThan": return ">";
787 case "op_LessThan": return "<";
788 case "op_GreaterThanOrEqual": return ">=";
789 case "op_LessThanOrEqual": return "<=";
790 default: return name
;
794 bool MethodIsExplicitIfaceImpl (MethodBase mb
)
796 if (!(mb
.IsFinal
&& mb
.IsVirtual
&& mb
.IsPrivate
))
799 // UGH msft has no way to get the info about what method is
800 // getting overriden. Another reason to use cecil :-)
802 //MethodInfo mi = mb as MethodInfo;
806 //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType);
807 //return mi.GetBaseDefinition ().DeclaringType.IsInterface;
809 // So, we guess that virtual final private methods only come
814 bool ShowMember (MemberInfo mi
)
816 if (mi
.MemberType
== MemberTypes
.Constructor
&& ((MethodBase
) mi
).IsStatic
)
822 if (filter_obsolete
&& mi
.IsDefined (typeof (ObsoleteAttribute
), false))
825 switch (mi
.MemberType
) {
826 case MemberTypes
.Constructor
:
827 case MemberTypes
.Method
:
828 MethodBase mb
= mi
as MethodBase
;
830 if (mb
.IsFamily
|| mb
.IsPublic
|| mb
.IsFamilyOrAssembly
)
833 if (MethodIsExplicitIfaceImpl (mb
))
839 case MemberTypes
.Field
:
840 FieldInfo fi
= mi
as FieldInfo
;
842 if (fi
.IsFamily
|| fi
.IsPublic
|| fi
.IsFamilyOrAssembly
)
848 case MemberTypes
.NestedType
:
849 case MemberTypes
.TypeInfo
:
852 switch (t
.Attributes
& TypeAttributes
.VisibilityMask
){
853 case TypeAttributes
.Public
:
854 case TypeAttributes
.NestedPublic
:
855 case TypeAttributes
.NestedFamily
:
856 case TypeAttributes
.NestedFamORAssem
:
867 static Type
[] TypeGetInterfaces (Type t
, bool declonly
)
869 if (t
.IsGenericParameter
)
872 Type
[] ifaces
= t
.GetInterfaces ();
876 // Handle Object. Also, optimize for no interfaces
877 if (t
.BaseType
== null || ifaces
.Length
== 0)
880 ArrayList ar
= new ArrayList ();
882 foreach (Type i
in ifaces
)
883 if (! i
.IsAssignableFrom (t
.BaseType
))
886 return (Type
[]) ar
.ToArray (typeof (Type
));
890 public class Comparer
: IComparer
{
891 delegate int ComparerFunc (object a
, object b
);
895 Comparer (ComparerFunc f
)
900 public int Compare (object a
, object b
)
905 static int CompareType (object a
, object b
)
907 Type type1
= (Type
) a
;
908 Type type2
= (Type
) b
;
910 if (type1
.IsSubclassOf (typeof (System
.MulticastDelegate
)) != type2
.IsSubclassOf (typeof (System
.MulticastDelegate
)))
911 return (type1
.IsSubclassOf (typeof (System
.MulticastDelegate
)))? -1:1;
912 return string.Compare (type1
.Name
, type2
.Name
);
916 // static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType));
918 // static Type [] Sort (Type [] types)
920 // Array.Sort (types, TypeComparer);
924 static int CompareMemberInfo (object a
, object b
)
926 return string.Compare (((MemberInfo
) a
).Name
, ((MemberInfo
) b
).Name
);
929 static Comparer MemberInfoComparer
= new Comparer (new ComparerFunc (CompareMemberInfo
));
931 public static MemberInfo
[] Sort (MemberInfo
[] inf
)
933 Array
.Sort (inf
, MemberInfoComparer
);
937 static int CompareMethodBase (object a
, object b
)
939 MethodBase aa
= (MethodBase
) a
, bb
= (MethodBase
) b
;
941 if (aa
.IsStatic
== bb
.IsStatic
) {
942 int c
= CompareMemberInfo (a
, b
);
945 ParameterInfo
[] ap
, bp
;
948 // Sort overloads by the names of their types
949 // put methods with fewer params first.
952 ap
= aa
.GetParameters ();
953 bp
= bb
.GetParameters ();
954 int n
= System
.Math
.Min (ap
.Length
, bp
.Length
);
956 for (int i
= 0; i
< n
; i
++)
957 if ((c
= CompareType (ap
[i
].ParameterType
, bp
[i
].ParameterType
)) != 0)
960 return ap
.Length
.CompareTo (bp
.Length
);
968 static Comparer MethodBaseComparer
= new Comparer (new ComparerFunc (CompareMethodBase
));
970 public static MethodBase
[] Sort (MethodBase
[] inf
)
972 Array
.Sort (inf
, MethodBaseComparer
);
976 static int ComparePropertyInfo (object a
, object b
)
978 PropertyInfo aa
= (PropertyInfo
) a
, bb
= (PropertyInfo
) b
;
980 bool astatic
= (aa
.CanRead
? aa
.GetGetMethod (true) : aa
.GetSetMethod (true)).IsStatic
;
981 bool bstatic
= (bb
.CanRead
? bb
.GetGetMethod (true) : bb
.GetSetMethod (true)).IsStatic
;
983 if (astatic
== bstatic
)
984 return CompareMemberInfo (a
, b
);
992 static Comparer PropertyInfoComparer
= new Comparer (new ComparerFunc (ComparePropertyInfo
));
994 public static PropertyInfo
[] Sort (PropertyInfo
[] inf
)
996 Array
.Sort (inf
, PropertyInfoComparer
);
1000 static int CompareEventInfo (object a
, object b
)
1002 EventInfo aa
= (EventInfo
) a
, bb
= (EventInfo
) b
;
1004 bool astatic
= aa
.GetAddMethod (true).IsStatic
;
1005 bool bstatic
= bb
.GetAddMethod (true).IsStatic
;
1007 if (astatic
== bstatic
)
1008 return CompareMemberInfo (a
, b
);
1016 static Comparer EventInfoComparer
= new Comparer (new ComparerFunc (CompareEventInfo
));
1018 public static EventInfo
[] Sort (EventInfo
[] inf
)
1020 Array
.Sort (inf
, EventInfoComparer
);