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);