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 public class Outline
{
45 public Outline (Type t
, TextWriter output
, Options options
)
48 this.o
= new IndentedTextWriter (output
, "\t");
49 this.options
= options
;
52 public void OutlineType ()
57 o
.Write (GetTypeVisibility (t
));
59 if (t
.IsClass
&& !t
.IsSubclassOf (typeof (System
.MulticastDelegate
))) {
61 o
.Write (t
.IsAbstract
? " static" : " sealed");
62 else if (t
.IsAbstract
)
63 o
.Write (" abstract");
67 o
.Write (GetTypeKind (t
));
70 Type
[] interfaces
= (Type
[]) Comparer
.Sort (TypeGetInterfaces (t
, options
.DeclaredOnly
));
71 Type parent
= t
.BaseType
;
73 if (t
.IsSubclassOf (typeof (System
.MulticastDelegate
))) {
76 method
= t
.GetMethod ("Invoke");
78 o
.Write (FormatType (method
.ReturnType
));
80 o
.Write (GetTypeName (t
));
82 OutlineParams (method
.GetParameters ());
86 WriteGenericConstraints (t
.GetGenericArguments ());
93 o
.Write (GetTypeName (t
));
94 if (((parent
!= null && parent
!= typeof (object) && parent
!= typeof (ValueType
)) || interfaces
.Length
!= 0) && ! t
.IsEnum
) {
98 if (parent
!= null && parent
!= typeof (object) && parent
!= typeof (ValueType
)) {
99 o
.Write (FormatType (parent
));
103 foreach (Type intf
in interfaces
) {
104 if (!first
) o
.Write (", ");
107 o
.Write (FormatType (intf
));
112 Type underlyingType
= Enum
.GetUnderlyingType (t
);
113 if (underlyingType
!= typeof (int))
114 o
.Write (" : {0}", FormatType (underlyingType
));
117 WriteGenericConstraints (t
.GetGenericArguments ());
123 bool is_first
= true;
124 foreach (FieldInfo fi
in t
.GetFields (BindingFlags
.Public
| BindingFlags
.Static
)) {
132 o
.Indent
--; o
.WriteLine ("}");
138 foreach (ConstructorInfo ci
in t
.GetConstructors (DefaultFlags
)) {
140 if (! ShowMember (ci
))
147 OutlineConstructor (ci
);
155 foreach (MethodInfo m
in Comparer
.Sort (t
.GetMethods (DefaultFlags
))) {
157 if (! ShowMember (m
))
160 if ((m
.Attributes
& MethodAttributes
.SpecialName
) != 0)
174 foreach (MethodInfo m
in t
.GetMethods (DefaultFlags
)) {
176 if (! ShowMember (m
))
179 if ((m
.Attributes
& MethodAttributes
.SpecialName
) == 0)
181 if (!(m
.Name
.StartsWith ("op_")))
195 foreach (PropertyInfo pi
in Comparer
.Sort (t
.GetProperties (DefaultFlags
))) {
197 if (! ((pi
.CanRead
&& ShowMember (pi
.GetGetMethod (true))) ||
198 (pi
.CanWrite
&& ShowMember (pi
.GetSetMethod (true)))))
205 OutlineProperty (pi
);
212 foreach (FieldInfo fi
in t
.GetFields (DefaultFlags
)) {
214 if (! ShowMember (fi
))
228 foreach (EventInfo ei
in Comparer
.Sort (t
.GetEvents (DefaultFlags
))) {
230 if (! ShowMember (ei
.GetAddMethod (true)))
244 foreach (Type ntype
in Comparer
.Sort (t
.GetNestedTypes (DefaultFlags
))) {
246 if (! ShowMember (ntype
))
253 new Outline (ntype
, o
, options
).OutlineType ();
256 o
.Indent
--; o
.WriteLine ("}");
259 BindingFlags DefaultFlags
{
261 BindingFlags f
= BindingFlags
.Instance
| BindingFlags
.Static
| BindingFlags
.Public
| BindingFlags
.NonPublic
;
263 if (options
.DeclaredOnly
)
264 f
|= BindingFlags
.DeclaredOnly
;
270 // FIXME: add other interesting attributes?
271 void OutlineAttributes ()
273 if (t
.IsSerializable
)
274 o
.WriteLine ("[Serializable]");
276 if (t
.IsDefined (typeof (System
.FlagsAttribute
), true))
277 o
.WriteLine ("[Flags]");
279 if (t
.IsDefined (typeof (System
.ObsoleteAttribute
), true))
280 o
.WriteLine ("[Obsolete]");
283 void OutlineEvent (EventInfo ei
)
285 MethodBase accessor
= ei
.GetAddMethod (true);
287 o
.Write (GetMethodVisibility (accessor
));
289 o
.Write (FormatType (ei
.EventHandlerType
));
295 void OutlineConstructor (ConstructorInfo ci
)
297 o
.Write (GetMethodVisibility (ci
));
298 o
.Write (RemoveGenericArity (t
.Name
));
300 OutlineParams (ci
.GetParameters ());
305 void OutlineProperty (PropertyInfo pi
)
307 ParameterInfo
[] idxp
= pi
.GetIndexParameters ();
308 MethodBase g
= pi
.GetGetMethod (true);
309 MethodBase s
= pi
.GetSetMethod (true);
310 MethodBase accessor
= g
!= null ? g
: s
;
312 if (pi
.CanRead
&& pi
.CanWrite
) {
315 // Get the more accessible accessor
316 if ((g
.Attributes
& MethodAttributes
.MemberAccessMask
) !=
317 (s
.Attributes
& MethodAttributes
.MemberAccessMask
)) {
319 if (g
.IsPublic
) accessor
= g
;
320 else if (s
.IsPublic
) accessor
= s
;
321 else if (g
.IsFamilyOrAssembly
) accessor
= g
;
322 else if (s
.IsFamilyOrAssembly
) accessor
= s
;
323 else if (g
.IsAssembly
|| g
.IsFamily
) accessor
= g
;
324 else if (s
.IsAssembly
|| s
.IsFamily
) accessor
= s
;
328 o
.Write (GetMethodVisibility (accessor
));
329 o
.Write (GetMethodModifiers (accessor
));
330 o
.Write (FormatType (pi
.PropertyType
));
333 if (idxp
.Length
== 0)
337 OutlineParams (idxp
);
344 if (g
!= null && ShowMember (g
)) {
345 if ((g
.Attributes
& MethodAttributes
.MemberAccessMask
) !=
346 (accessor
.Attributes
& MethodAttributes
.MemberAccessMask
))
347 o
.Write (GetMethodVisibility (g
));
348 o
.WriteLine ("get;");
351 if (s
!= null && ShowMember (s
)) {
352 if ((s
.Attributes
& MethodAttributes
.MemberAccessMask
) !=
353 (accessor
.Attributes
& MethodAttributes
.MemberAccessMask
))
354 o
.Write (GetMethodVisibility (s
));
355 o
.WriteLine ("set;");
362 void OutlineMethod (MethodInfo mi
)
364 if (MethodIsExplicitIfaceImpl (mi
)) {
365 o
.Write (FormatType (mi
.ReturnType
));
367 // MSFT has no way to get the method that we are overriding
368 // from the interface. this would allow us to pretty print
369 // the type name (and be more correct if there compiler
370 // were to do some strange naming thing).
372 o
.Write (GetMethodVisibility (mi
));
373 o
.Write (GetMethodModifiers (mi
));
374 o
.Write (FormatType (mi
.ReturnType
));
380 o
.Write (FormatGenericParams (mi
.GetGenericArguments ()));
383 OutlineParams (mi
.GetParameters ());
386 WriteGenericConstraints (mi
.GetGenericArguments ());
391 void OutlineOperator (MethodInfo mi
)
393 o
.Write (GetMethodVisibility (mi
));
394 o
.Write (GetMethodModifiers (mi
));
395 if (mi
.Name
== "op_Explicit" || mi
.Name
== "op_Implicit") {
396 o
.Write (mi
.Name
.Substring (3).ToLower ());
397 o
.Write (" operator ");
398 o
.Write (FormatType (mi
.ReturnType
));
400 o
.Write (FormatType (mi
.ReturnType
));
401 o
.Write (" operator ");
402 o
.Write (OperatorFromName (mi
.Name
));
405 OutlineParams (mi
.GetParameters ());
409 void OutlineParams (ParameterInfo
[] pi
)
412 foreach (ParameterInfo p
in pi
) {
413 if (p
.ParameterType
.IsByRef
) {
414 o
.Write (p
.IsOut
? "out " : "ref ");
415 o
.Write (FormatType (p
.ParameterType
.GetElementType ()));
416 } else if (p
.IsDefined (typeof (ParamArrayAttribute
), false)) {
418 o
.Write (FormatType (p
.ParameterType
));
420 o
.Write (FormatType (p
.ParameterType
));
425 if (i
+ 1 < pi
.Length
)
431 void OutlineField (FieldInfo fi
)
433 if (fi
.IsPublic
) o
.Write ("public ");
434 if (fi
.IsFamily
) o
.Write ("protected ");
435 if (fi
.IsPrivate
) o
.Write ("private ");
436 if (fi
.IsAssembly
) o
.Write ("internal ");
437 if (fi
.IsLiteral
) o
.Write ("const ");
438 else if (fi
.IsStatic
) o
.Write ("static ");
439 if (fi
.IsInitOnly
) o
.Write ("readonly ");
441 o
.Write (FormatType (fi
.FieldType
));
445 object v
= fi
.GetValue (this);
447 // TODO: Escape values here
450 o
.Write ("'{0}'", v
);
451 else if (v
is string)
452 o
.Write ("\"{0}\"", v
);
454 o
.Write (fi
.GetValue (this));
459 static string GetMethodVisibility (MethodBase m
)
461 // itnerfaces have no modifiers here
462 if (m
.DeclaringType
.IsInterface
)
465 if (m
.IsPublic
) return "public ";
466 if (m
.IsFamily
) return "protected ";
467 if (m
.IsPrivate
) return "private ";
468 if (m
.IsAssembly
) return "internal ";
473 static string GetMethodModifiers (MethodBase method
)
478 if (method
.IsFinal
) {
479 // This will happen if you have
481 // public void A () {}
482 // static void Main () {}
488 // A needs to be virtual (the CLR requires
489 // methods implementing an iface be virtual),
490 // but can not be inherited. It also can not
491 // be inherited. In C# this is represented
492 // with no special modifiers
494 if (method
.IsVirtual
)
499 // all interface methods are "virtual" but we don't say that in c#
500 if (method
.IsVirtual
&& !method
.DeclaringType
.IsInterface
) {
501 if (method
.IsAbstract
)
504 return ((method
.Attributes
& MethodAttributes
.NewSlot
) != 0) ?
512 static string GetTypeKind (Type t
)
517 if (t
.IsSubclassOf (typeof (System
.MulticastDelegate
)))
529 static string GetTypeVisibility (Type t
)
531 switch (t
.Attributes
& TypeAttributes
.VisibilityMask
){
532 case TypeAttributes
.Public
:
533 case TypeAttributes
.NestedPublic
:
536 case TypeAttributes
.NestedFamily
:
537 case TypeAttributes
.NestedFamANDAssem
:
538 case TypeAttributes
.NestedFamORAssem
:
546 string FormatGenericParams (Type
[] args
)
548 StringBuilder sb
= new StringBuilder ();
549 if (args
.Length
== 0)
553 for (int i
= 0; i
< args
.Length
; i
++) {
556 sb
.Append (FormatType (args
[i
]));
559 return sb
.ToString ();
562 // TODO: fine tune this so that our output is less verbose. We need to figure
563 // out a way to do this while not making things confusing.
564 string FormatType (Type t
)
566 string type
= GetFullName (t
);
568 if (!type
.StartsWith ("System.")) {
569 if (t
.Namespace
== this.t
.Namespace
)
574 if (t
.HasElementType
) {
575 Type et
= t
.GetElementType ();
577 return FormatType (et
) + " []";
579 return FormatType (et
) + " *";
581 return "ref " + FormatType (et
);
585 case "System.Byte": return "byte";
586 case "System.SByte": return "sbyte";
587 case "System.Int16": return "short";
588 case "System.Int32": return "int";
589 case "System.Int64": return "long";
591 case "System.UInt16": return "ushort";
592 case "System.UInt32": return "uint";
593 case "System.UInt64": return "ulong";
595 case "System.Single": return "float";
596 case "System.Double": return "double";
597 case "System.Decimal": return "decimal";
598 case "System.Boolean": return "bool";
599 case "System.Char": return "char";
600 case "System.String": return "string";
602 case "System.Object": return "object";
603 case "System.Void": return "void";
606 if (type
.LastIndexOf(".") == 6)
607 return type
.Substring(7);
610 // If the namespace of the type is the namespace of what
611 // we are printing (or is a member of one if its children
612 // don't print it. This basically means that in C# we would
613 // automatically get the namespace imported by virtue of the
614 // namespace {} block.
616 if (this.t
.Namespace
.StartsWith (t
.Namespace
+ ".") || t
.Namespace
== this.t
.Namespace
)
617 return type
.Substring (t
.Namespace
.Length
+ 1);
622 public static string RemoveGenericArity (string name
)
625 StringBuilder sb
= new StringBuilder ();
626 while (start
< name
.Length
) {
627 int pos
= name
.IndexOf ('`', start
);
629 sb
.Append (name
.Substring (start
));
632 sb
.Append (name
.Substring (start
, pos
-start
));
636 while ((pos
< name
.Length
) && Char
.IsNumber (name
[pos
]))
642 return sb
.ToString ();
645 string GetTypeName (Type t
)
647 StringBuilder sb
= new StringBuilder ();
649 return sb
.ToString ();
652 void GetTypeName (StringBuilder sb
, Type t
)
654 sb
.Append (RemoveGenericArity (t
.Name
));
656 sb
.Append (FormatGenericParams (t
.GetGenericArguments ()));
660 string GetFullName (Type t
)
662 StringBuilder sb
= new StringBuilder ();
663 GetFullName_recursed (sb
, t
, false);
664 return sb
.ToString ();
667 void GetFullName_recursed (StringBuilder sb
, Type t
, bool recursed
)
670 if (t
.IsGenericParameter
) {
676 if (t
.DeclaringType
!= null) {
677 GetFullName_recursed (sb
, t
.DeclaringType
, true);
682 string ns
= t
.Namespace
;
683 if ((ns
!= null) && (ns
!= "")) {
693 void WriteGenericConstraints (Type
[] args
)
696 foreach (Type t
in args
) {
698 Type
[] ifaces
= TypeGetInterfaces (t
, true);
700 GenericParameterAttributes attrs
= t
.GenericParameterAttributes
& GenericParameterAttributes
.SpecialConstraintMask
;
701 GenericParameterAttributes
[] interesting
= {
702 GenericParameterAttributes
.ReferenceTypeConstraint
,
703 GenericParameterAttributes
.NotNullableValueTypeConstraint
,
704 GenericParameterAttributes
.DefaultConstructorConstraint
707 if (t
.BaseType
!= typeof (object) || ifaces
.Length
!= 0 || attrs
!= 0) {
709 o
.Write (FormatType (t
));
713 if (t
.BaseType
!= typeof (object)) {
714 o
.Write (FormatType (t
.BaseType
));
718 foreach (Type iface
in ifaces
) {
723 o
.Write (FormatType (iface
));
726 foreach (GenericParameterAttributes a
in interesting
) {
727 if ((attrs
& a
) == 0)
735 case GenericParameterAttributes
.ReferenceTypeConstraint
:
738 case GenericParameterAttributes
.NotNullableValueTypeConstraint
:
741 case GenericParameterAttributes
.DefaultConstructorConstraint
:
750 string OperatorFromName (string name
)
753 case "op_UnaryPlus": return "+";
754 case "op_UnaryNegation": return "-";
755 case "op_LogicalNot": return "!";
756 case "op_OnesComplement": return "~";
757 case "op_Increment": return "++";
758 case "op_Decrement": return "--";
759 case "op_True": return "true";
760 case "op_False": return "false";
761 case "op_Addition": return "+";
762 case "op_Subtraction": return "-";
763 case "op_Multiply": return "*";
764 case "op_Division": return "/";
765 case "op_Modulus": return "%";
766 case "op_BitwiseAnd": return "&";
767 case "op_BitwiseOr": return "|";
768 case "op_ExclusiveOr": return "^";
769 case "op_LeftShift": return "<<";
770 case "op_RightShift": return ">>";
771 case "op_Equality": return "==";
772 case "op_Inequality": return "!=";
773 case "op_GreaterThan": return ">";
774 case "op_LessThan": return "<";
775 case "op_GreaterThanOrEqual": return ">=";
776 case "op_LessThanOrEqual": return "<=";
777 default: return name
;
781 bool MethodIsExplicitIfaceImpl (MethodBase mb
)
783 if (!(mb
.IsFinal
&& mb
.IsVirtual
&& mb
.IsPrivate
))
786 // UGH msft has no way to get the info about what method is
787 // getting overriden. Another reason to use cecil :-)
789 //MethodInfo mi = mb as MethodInfo;
793 //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType);
794 //return mi.GetBaseDefinition ().DeclaringType.IsInterface;
796 // So, we guess that virtual final private methods only come
801 bool ShowMember (MemberInfo mi
)
803 if (mi
.MemberType
== MemberTypes
.Constructor
&& ((MethodBase
) mi
).IsStatic
)
806 if (options
.ShowPrivate
)
809 if (options
.FilterObsolete
&& mi
.IsDefined (typeof (ObsoleteAttribute
), false))
812 switch (mi
.MemberType
) {
813 case MemberTypes
.Constructor
:
814 case MemberTypes
.Method
:
815 MethodBase mb
= mi
as MethodBase
;
817 if (mb
.IsFamily
|| mb
.IsPublic
|| mb
.IsFamilyOrAssembly
)
820 if (MethodIsExplicitIfaceImpl (mb
))
826 case MemberTypes
.Field
:
827 FieldInfo fi
= mi
as FieldInfo
;
829 if (fi
.IsFamily
|| fi
.IsPublic
|| fi
.IsFamilyOrAssembly
)
835 case MemberTypes
.NestedType
:
836 case MemberTypes
.TypeInfo
:
839 switch (t
.Attributes
& TypeAttributes
.VisibilityMask
){
840 case TypeAttributes
.Public
:
841 case TypeAttributes
.NestedPublic
:
842 case TypeAttributes
.NestedFamily
:
843 case TypeAttributes
.NestedFamORAssem
:
854 static Type
[] TypeGetInterfaces (Type t
, bool declonly
)
856 Type
[] ifaces
= t
.GetInterfaces ();
860 // Handle Object. Also, optimize for no interfaces
861 if (t
.BaseType
== null || ifaces
.Length
== 0)
864 ArrayList ar
= new ArrayList ();
866 foreach (Type i
in ifaces
)
867 if (! i
.IsAssignableFrom (t
.BaseType
))
870 return (Type
[]) ar
.ToArray (typeof (Type
));
874 public class Comparer
: IComparer
{
875 delegate int ComparerFunc (object a
, object b
);
879 Comparer (ComparerFunc f
)
884 public int Compare (object a
, object b
)
889 static int CompareType (object a
, object b
)
891 Type type1
= (Type
) a
;
892 Type type2
= (Type
) b
;
894 if (type1
.IsSubclassOf (typeof (System
.MulticastDelegate
)) != type2
.IsSubclassOf (typeof (System
.MulticastDelegate
)))
895 return (type1
.IsSubclassOf (typeof (System
.MulticastDelegate
)))? -1:1;
896 return string.Compare (type1
.Name
, type2
.Name
);
900 static Comparer TypeComparer
= new Comparer (new ComparerFunc (CompareType
));
902 static Type
[] Sort (Type
[] types
)
904 Array
.Sort (types
, TypeComparer
);
908 static int CompareMemberInfo (object a
, object b
)
910 return string.Compare (((MemberInfo
) a
).Name
, ((MemberInfo
) b
).Name
);
913 static Comparer MemberInfoComparer
= new Comparer (new ComparerFunc (CompareMemberInfo
));
915 public static MemberInfo
[] Sort (MemberInfo
[] inf
)
917 Array
.Sort (inf
, MemberInfoComparer
);
921 static int CompareMethodBase (object a
, object b
)
923 MethodBase aa
= (MethodBase
) a
, bb
= (MethodBase
) b
;
925 if (aa
.IsStatic
== bb
.IsStatic
) {
926 int c
= CompareMemberInfo (a
, b
);
929 ParameterInfo
[] ap
, bp
;
932 // Sort overloads by the names of their types
933 // put methods with fewer params first.
936 ap
= aa
.GetParameters ();
937 bp
= bb
.GetParameters ();
938 int n
= Math
.Min (ap
.Length
, bp
.Length
);
940 for (int i
= 0; i
< n
; i
++)
941 if ((c
= CompareType (ap
[i
].ParameterType
, bp
[i
].ParameterType
)) != 0)
944 return ap
.Length
.CompareTo (bp
.Length
);
952 static Comparer MethodBaseComparer
= new Comparer (new ComparerFunc (CompareMethodBase
));
954 public static MethodBase
[] Sort (MethodBase
[] inf
)
956 Array
.Sort (inf
, MethodBaseComparer
);
960 static int ComparePropertyInfo (object a
, object b
)
962 PropertyInfo aa
= (PropertyInfo
) a
, bb
= (PropertyInfo
) b
;
964 bool astatic
= (aa
.CanRead
? aa
.GetGetMethod (true) : aa
.GetSetMethod (true)).IsStatic
;
965 bool bstatic
= (bb
.CanRead
? bb
.GetGetMethod (true) : bb
.GetSetMethod (true)).IsStatic
;
967 if (astatic
== bstatic
)
968 return CompareMemberInfo (a
, b
);
976 static Comparer PropertyInfoComparer
= new Comparer (new ComparerFunc (ComparePropertyInfo
));
978 public static PropertyInfo
[] Sort (PropertyInfo
[] inf
)
980 Array
.Sort (inf
, PropertyInfoComparer
);
984 static int CompareEventInfo (object a
, object b
)
986 EventInfo aa
= (EventInfo
) a
, bb
= (EventInfo
) b
;
988 bool astatic
= aa
.GetAddMethod (true).IsStatic
;
989 bool bstatic
= bb
.GetAddMethod (true).IsStatic
;
991 if (astatic
== bstatic
)
992 return CompareMemberInfo (a
, b
);
1000 static Comparer EventInfoComparer
= new Comparer (new ComparerFunc (CompareEventInfo
));
1002 public static EventInfo
[] Sort (EventInfo
[] inf
)
1004 Array
.Sort (inf
, EventInfoComparer
);