// // outline -- support for rendering in monop // Some code stolen from updater.cs in monodoc. // // Authors: // Ben Maurer (bmaurer@users.sourceforge.net) // // (C) 2004 Ben Maurer // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // #if !NET_2_1 using System; using System.Reflection; using System.Collections; using System.CodeDom.Compiler; using System.IO; using System.Text; namespace Mono.CSharp { public class Outline { bool declared_only; bool show_private; bool filter_obsolete; IndentedTextWriter o; Type t; public Outline (Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete) { this.t = t; this.o = new IndentedTextWriter (output, "\t"); this.declared_only = declared_only; this.show_private = show_private; this.filter_obsolete = filter_obsolete; } public void OutlineType () { bool first; OutlineAttributes (); o.Write (GetTypeVisibility (t)); if (t.IsClass && !t.IsSubclassOf (typeof (System.MulticastDelegate))) { if (t.IsSealed) o.Write (t.IsAbstract ? " static" : " sealed"); else if (t.IsAbstract) o.Write (" abstract"); } o.Write (" "); o.Write (GetTypeKind (t)); o.Write (" "); Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, declared_only)); Type parent = t.BaseType; if (t.IsSubclassOf (typeof (System.MulticastDelegate))) { MethodInfo method; method = t.GetMethod ("Invoke"); o.Write (FormatType (method.ReturnType)); o.Write (" "); o.Write (GetTypeName (t)); o.Write (" ("); OutlineParams (method.GetParameters ()); o.Write (")"); WriteGenericConstraints (t.GetGenericArguments ()); o.WriteLine (";"); return; } o.Write (GetTypeName (t)); if (((parent != null && parent != typeof (object) && parent != typeof (ValueType)) || interfaces.Length != 0) && ! t.IsEnum) { first = true; o.Write (" : "); if (parent != null && parent != typeof (object) && parent != typeof (ValueType)) { o.Write (FormatType (parent)); first = false; } foreach (Type intf in interfaces) { if (!first) o.Write (", "); first = false; o.Write (FormatType (intf)); } } if (t.IsEnum) { Type underlyingType = System.Enum.GetUnderlyingType (t); if (underlyingType != typeof (int)) o.Write (" : {0}", FormatType (underlyingType)); } WriteGenericConstraints (t.GetGenericArguments ()); o.WriteLine (" {"); o.Indent++; if (t.IsEnum) { bool is_first = true; foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Static)) { if (! is_first) o.WriteLine (","); is_first = false; o.Write (fi.Name); } o.WriteLine (); o.Indent--; o.WriteLine ("}"); return; } first = true; foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) { if (! ShowMember (ci)) continue; if (first) o.WriteLine (); first = false; OutlineMemberAttribute (ci); OutlineConstructor (ci); o.WriteLine (); } first = true; foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) { if (! ShowMember (m)) continue; if ((m.Attributes & MethodAttributes.SpecialName) != 0) continue; if (first) o.WriteLine (); first = false; OutlineMemberAttribute (m); OutlineMethod (m); o.WriteLine (); } first = true; foreach (MethodInfo m in t.GetMethods (DefaultFlags)) { if (! ShowMember (m)) continue; if ((m.Attributes & MethodAttributes.SpecialName) == 0) continue; if (!(m.Name.StartsWith ("op_"))) continue; if (first) o.WriteLine (); first = false; OutlineMemberAttribute (m); OutlineOperator (m); o.WriteLine (); } first = true; foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) { if (! ((pi.CanRead && ShowMember (pi.GetGetMethod (true))) || (pi.CanWrite && ShowMember (pi.GetSetMethod (true))))) continue; if (first) o.WriteLine (); first = false; OutlineMemberAttribute (pi); OutlineProperty (pi); o.WriteLine (); } first = true; foreach (FieldInfo fi in t.GetFields (DefaultFlags)) { if (! ShowMember (fi)) continue; if (first) o.WriteLine (); first = false; OutlineMemberAttribute (fi); OutlineField (fi); o.WriteLine (); } first = true; foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) { if (! ShowMember (ei.GetAddMethod (true))) continue; if (first) o.WriteLine (); first = false; OutlineMemberAttribute (ei); OutlineEvent (ei); o.WriteLine (); } first = true; foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) { if (! ShowMember (ntype)) continue; if (first) o.WriteLine (); first = false; new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType (); } o.Indent--; o.WriteLine ("}"); } BindingFlags DefaultFlags { get { BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; if (declared_only) f |= BindingFlags.DeclaredOnly; return f; } } // FIXME: add other interesting attributes? void OutlineAttributes () { if (t.IsSerializable) o.WriteLine ("[Serializable]"); if (t.IsDefined (typeof (System.FlagsAttribute), true)) o.WriteLine ("[Flags]"); if (t.IsDefined (typeof (System.ObsoleteAttribute), true)) o.WriteLine ("[Obsolete]"); } void OutlineMemberAttribute (MemberInfo mi) { if (!mi.IsDefined (typeof (System.ObsoleteAttribute), false)) return; var oa = mi.GetCustomAttributes (typeof (System.ObsoleteAttribute), false) [0] as ObsoleteAttribute; var msg = oa.Message; o.WriteLine ("[Obsolete{0}]", msg == null || msg == "" ? "" : string.Format ("(\"{0}\")", msg)); } void OutlineEvent (EventInfo ei) { MethodBase accessor = ei.GetAddMethod (true); o.Write (GetMethodVisibility (accessor)); o.Write ("event "); o.Write (FormatType (ei.EventHandlerType)); o.Write (" "); o.Write (ei.Name); o.Write (";"); } void OutlineConstructor (ConstructorInfo ci) { o.Write (GetMethodVisibility (ci)); o.Write (RemoveGenericArity (t.Name)); o.Write (" ("); OutlineParams (ci.GetParameters ()); o.Write (");"); } void OutlineProperty (PropertyInfo pi) { ParameterInfo [] idxp = pi.GetIndexParameters (); MethodBase g = pi.GetGetMethod (true); MethodBase s = pi.GetSetMethod (true); MethodBase accessor = g != null ? g : s; if (pi.CanRead && pi.CanWrite) { // Get the more accessible accessor if ((g.Attributes & MethodAttributes.MemberAccessMask) != (s.Attributes & MethodAttributes.MemberAccessMask)) { if (g.IsPublic) accessor = g; else if (s.IsPublic) accessor = s; else if (g.IsFamilyOrAssembly) accessor = g; else if (s.IsFamilyOrAssembly) accessor = s; else if (g.IsAssembly || g.IsFamily) accessor = g; else if (s.IsAssembly || s.IsFamily) accessor = s; } } o.Write (GetMethodVisibility (accessor)); o.Write (GetMethodModifiers (accessor)); o.Write (FormatType (pi.PropertyType)); o.Write (" "); if (idxp.Length == 0) o.Write (pi.Name); else { o.Write ("this ["); OutlineParams (idxp); o.Write ("]"); } o.WriteLine (" {"); o.Indent ++; if (g != null && ShowMember (g)) { if ((g.Attributes & MethodAttributes.MemberAccessMask) != (accessor.Attributes & MethodAttributes.MemberAccessMask)) o.Write (GetMethodVisibility (g)); o.WriteLine ("get;"); } if (s != null && ShowMember (s)) { if ((s.Attributes & MethodAttributes.MemberAccessMask) != (accessor.Attributes & MethodAttributes.MemberAccessMask)) o.Write (GetMethodVisibility (s)); o.WriteLine ("set;"); } o.Indent --; o.Write ("}"); } void OutlineMethod (MethodInfo mi) { if (MethodIsExplicitIfaceImpl (mi)) { o.Write (FormatType (mi.ReturnType)); o.Write (" "); // MSFT has no way to get the method that we are overriding // from the interface. this would allow us to pretty print // the type name (and be more correct if there compiler // were to do some strange naming thing). } else { o.Write (GetMethodVisibility (mi)); o.Write (GetMethodModifiers (mi)); o.Write (FormatType (mi.ReturnType)); o.Write (" "); } o.Write (mi.Name); o.Write (FormatGenericParams (mi.GetGenericArguments ())); o.Write (" ("); OutlineParams (mi.GetParameters ()); o.Write (")"); WriteGenericConstraints (mi.GetGenericArguments ()); o.Write (";"); } void OutlineOperator (MethodInfo mi) { o.Write (GetMethodVisibility (mi)); o.Write (GetMethodModifiers (mi)); if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") { o.Write (mi.Name.Substring (3).ToLower ()); o.Write (" operator "); o.Write (FormatType (mi.ReturnType)); } else { o.Write (FormatType (mi.ReturnType)); o.Write (" operator "); o.Write (OperatorFromName (mi.Name)); } o.Write (" ("); OutlineParams (mi.GetParameters ()); o.Write (");"); } void OutlineParams (ParameterInfo [] pi) { int i = 0; foreach (ParameterInfo p in pi) { if (p.ParameterType.IsByRef) { o.Write (p.IsOut ? "out " : "ref "); o.Write (FormatType (p.ParameterType.GetElementType ())); } else if (p.IsDefined (typeof (ParamArrayAttribute), false)) { o.Write ("params "); o.Write (FormatType (p.ParameterType)); } else { o.Write (FormatType (p.ParameterType)); } o.Write (" "); o.Write (p.Name); if (i + 1 < pi.Length) o.Write (", "); i++; } } void OutlineField (FieldInfo fi) { if (fi.IsPublic) o.Write ("public "); if (fi.IsFamily) o.Write ("protected "); if (fi.IsPrivate) o.Write ("private "); if (fi.IsAssembly) o.Write ("internal "); if (fi.IsLiteral) o.Write ("const "); else if (fi.IsStatic) o.Write ("static "); if (fi.IsInitOnly) o.Write ("readonly "); o.Write (FormatType (fi.FieldType)); o.Write (" "); o.Write (fi.Name); if (fi.IsLiteral) { object v = fi.GetValue (this); // TODO: Escape values here o.Write (" = "); if (v is char) o.Write ("'{0}'", v); else if (v is string) o.Write ("\"{0}\"", v); else o.Write (fi.GetValue (this)); } o.Write (";"); } static string GetMethodVisibility (MethodBase m) { // itnerfaces have no modifiers here if (m.DeclaringType.IsInterface) return ""; if (m.IsPublic) return "public "; if (m.IsFamily) return "protected "; if (m.IsPrivate) return "private "; if (m.IsAssembly) return "internal "; return null; } static string GetMethodModifiers (MethodBase method) { if (method.IsStatic) return "static "; if (method.IsFinal) { // This will happen if you have // class X : IA { // public void A () {} // static void Main () {} // } // interface IA { // void A (); // } // // A needs to be virtual (the CLR requires // methods implementing an iface be virtual), // but can not be inherited. It also can not // be inherited. In C# this is represented // with no special modifiers if (method.IsVirtual) return null; return "sealed "; } // all interface methods are "virtual" but we don't say that in c# if (method.IsVirtual && !method.DeclaringType.IsInterface) { if (method.IsAbstract) return "abstract "; return ((method.Attributes & MethodAttributes.NewSlot) != 0) ? "virtual " : "override "; } return null; } static string GetTypeKind (Type t) { if (t.IsEnum) return "enum"; if (t.IsClass) { if (t.IsSubclassOf (typeof (System.MulticastDelegate))) return "delegate"; else return "class"; } if (t.IsInterface) return "interface"; if (t.IsValueType) return "struct"; return "class"; } static string GetTypeVisibility (Type t) { switch (t.Attributes & TypeAttributes.VisibilityMask){ case TypeAttributes.Public: case TypeAttributes.NestedPublic: return "public"; case TypeAttributes.NestedFamily: case TypeAttributes.NestedFamANDAssem: case TypeAttributes.NestedFamORAssem: return "protected"; default: return "internal"; } } string FormatGenericParams (Type [] args) { StringBuilder sb = new StringBuilder (); if (args.Length == 0) return ""; sb.Append ("<"); for (int i = 0; i < args.Length; i++) { if (i > 0) sb.Append (","); sb.Append (FormatType (args [i])); } sb.Append (">"); return sb.ToString (); } // TODO: fine tune this so that our output is less verbose. We need to figure // out a way to do this while not making things confusing. string FormatType (Type t) { if (t == null) return ""; string type = GetFullName (t); if (type == null) return t.ToString (); if (!type.StartsWith ("System.")) { if (t.Namespace == this.t.Namespace) return t.Name; return type; } if (t.HasElementType) { Type et = t.GetElementType (); if (t.IsArray) return FormatType (et) + " []"; if (t.IsPointer) return FormatType (et) + " *"; if (t.IsByRef) return "ref " + FormatType (et); } switch (type) { case "System.Byte": return "byte"; case "System.SByte": return "sbyte"; case "System.Int16": return "short"; case "System.Int32": return "int"; case "System.Int64": return "long"; case "System.UInt16": return "ushort"; case "System.UInt32": return "uint"; case "System.UInt64": return "ulong"; case "System.Single": return "float"; case "System.Double": return "double"; case "System.Decimal": return "decimal"; case "System.Boolean": return "bool"; case "System.Char": return "char"; case "System.String": return "string"; case "System.Object": return "object"; case "System.Void": return "void"; } if (type.LastIndexOf(".") == 6) return type.Substring(7); // // If the namespace of the type is the namespace of what // we are printing (or is a member of one if its children // don't print it. This basically means that in C# we would // automatically get the namespace imported by virtue of the // namespace {} block. // if (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace) return type.Substring (t.Namespace.Length + 1); return type; } public static string RemoveGenericArity (string name) { int start = 0; StringBuilder sb = new StringBuilder (); while (start < name.Length) { int pos = name.IndexOf ('`', start); if (pos < 0) { sb.Append (name.Substring (start)); break; } sb.Append (name.Substring (start, pos-start)); pos++; while ((pos < name.Length) && Char.IsNumber (name [pos])) pos++; start = pos; } return sb.ToString (); } string GetTypeName (Type t) { StringBuilder sb = new StringBuilder (); GetTypeName (sb, t); return sb.ToString (); } void GetTypeName (StringBuilder sb, Type t) { sb.Append (RemoveGenericArity (t.Name)); sb.Append (FormatGenericParams (t.GetGenericArguments ())); } string GetFullName (Type t) { StringBuilder sb = new StringBuilder (); GetFullName_recursed (sb, t, false); return sb.ToString (); } void GetFullName_recursed (StringBuilder sb, Type t, bool recursed) { if (t.IsGenericParameter) { sb.Append (t.Name); return; } if (t.DeclaringType != null) { GetFullName_recursed (sb, t.DeclaringType, true); sb.Append ("."); } if (!recursed) { string ns = t.Namespace; if ((ns != null) && (ns != "")) { sb.Append (ns); sb.Append ("."); } } GetTypeName (sb, t); } void WriteGenericConstraints (Type [] args) { foreach (Type t in args) { bool first = true; Type[] ifaces = TypeGetInterfaces (t, true); GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask; GenericParameterAttributes [] interesting = { GenericParameterAttributes.ReferenceTypeConstraint, GenericParameterAttributes.NotNullableValueTypeConstraint, GenericParameterAttributes.DefaultConstructorConstraint }; if (t.BaseType != typeof (object) || ifaces.Length != 0 || attrs != 0) { o.Write (" where "); o.Write (FormatType (t)); o.Write (" : "); } if (t.BaseType != typeof (object)) { o.Write (FormatType (t.BaseType)); first = false; } foreach (Type iface in ifaces) { if (!first) o.Write (", "); first = false; o.Write (FormatType (iface)); } foreach (GenericParameterAttributes a in interesting) { if ((attrs & a) == 0) continue; if (!first) o.Write (", "); first = false; switch (a) { case GenericParameterAttributes.ReferenceTypeConstraint: o.Write ("class"); break; case GenericParameterAttributes.NotNullableValueTypeConstraint: o.Write ("struct"); break; case GenericParameterAttributes.DefaultConstructorConstraint: o.Write ("new ()"); break; } } } } string OperatorFromName (string name) { switch (name) { case "op_UnaryPlus": return "+"; case "op_UnaryNegation": return "-"; case "op_LogicalNot": return "!"; case "op_OnesComplement": return "~"; case "op_Increment": return "++"; case "op_Decrement": return "--"; case "op_True": return "true"; case "op_False": return "false"; case "op_Addition": return "+"; case "op_Subtraction": return "-"; case "op_Multiply": return "*"; case "op_Division": return "/"; case "op_Modulus": return "%"; case "op_BitwiseAnd": return "&"; case "op_BitwiseOr": return "|"; case "op_ExclusiveOr": return "^"; case "op_LeftShift": return "<<"; case "op_RightShift": return ">>"; case "op_Equality": return "=="; case "op_Inequality": return "!="; case "op_GreaterThan": return ">"; case "op_LessThan": return "<"; case "op_GreaterThanOrEqual": return ">="; case "op_LessThanOrEqual": return "<="; default: return name; } } bool MethodIsExplicitIfaceImpl (MethodBase mb) { if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate)) return false; // UGH msft has no way to get the info about what method is // getting overriden. Another reason to use cecil :-) // //MethodInfo mi = mb as MethodInfo; //if (mi == null) // return false; // //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType); //return mi.GetBaseDefinition ().DeclaringType.IsInterface; // So, we guess that virtual final private methods only come // from ifaces :-) return true; } bool ShowMember (MemberInfo mi) { if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic) return false; if (show_private) return true; if (filter_obsolete && mi.IsDefined (typeof (ObsoleteAttribute), false)) return false; switch (mi.MemberType) { case MemberTypes.Constructor: case MemberTypes.Method: MethodBase mb = mi as MethodBase; if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly) return true; if (MethodIsExplicitIfaceImpl (mb)) return true; return false; case MemberTypes.Field: FieldInfo fi = mi as FieldInfo; if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly) return true; return false; case MemberTypes.NestedType: case MemberTypes.TypeInfo: Type t = mi as Type; switch (t.Attributes & TypeAttributes.VisibilityMask){ case TypeAttributes.Public: case TypeAttributes.NestedPublic: case TypeAttributes.NestedFamily: case TypeAttributes.NestedFamORAssem: return true; } return false; } // What am I !!! return true; } static Type [] TypeGetInterfaces (Type t, bool declonly) { if (t.IsGenericParameter) return new Type [0]; Type [] ifaces = t.GetInterfaces (); if (! declonly) return ifaces; // Handle Object. Also, optimize for no interfaces if (t.BaseType == null || ifaces.Length == 0) return ifaces; ArrayList ar = new ArrayList (); foreach (Type i in ifaces) if (! i.IsAssignableFrom (t.BaseType)) ar.Add (i); return (Type []) ar.ToArray (typeof (Type)); } } public class Comparer : IComparer { delegate int ComparerFunc (object a, object b); ComparerFunc cmp; Comparer (ComparerFunc f) { this.cmp = f; } public int Compare (object a, object b) { return cmp (a, b); } static int CompareType (object a, object b) { Type type1 = (Type) a; Type type2 = (Type) b; if (type1.IsSubclassOf (typeof (System.MulticastDelegate)) != type2.IsSubclassOf (typeof (System.MulticastDelegate))) return (type1.IsSubclassOf (typeof (System.MulticastDelegate)))? -1:1; return string.Compare (type1.Name, type2.Name); } // static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType)); // static Type [] Sort (Type [] types) // { // Array.Sort (types, TypeComparer); // return types; // } static int CompareMemberInfo (object a, object b) { return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name); } static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo)); public static MemberInfo [] Sort (MemberInfo [] inf) { Array.Sort (inf, MemberInfoComparer); return inf; } static int CompareMethodBase (object a, object b) { MethodBase aa = (MethodBase) a, bb = (MethodBase) b; if (aa.IsStatic == bb.IsStatic) { int c = CompareMemberInfo (a, b); if (c != 0) return c; ParameterInfo [] ap, bp; // // Sort overloads by the names of their types // put methods with fewer params first. // ap = aa.GetParameters (); bp = bb.GetParameters (); int n = System.Math.Min (ap.Length, bp.Length); for (int i = 0; i < n; i ++) if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0) return c; return ap.Length.CompareTo (bp.Length); } if (aa.IsStatic) return -1; return 1; } static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase)); public static MethodBase [] Sort (MethodBase [] inf) { Array.Sort (inf, MethodBaseComparer); return inf; } static int ComparePropertyInfo (object a, object b) { PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b; bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic; bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic; if (astatic == bstatic) return CompareMemberInfo (a, b); if (astatic) return -1; return 1; } static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo)); public static PropertyInfo [] Sort (PropertyInfo [] inf) { Array.Sort (inf, PropertyInfoComparer); return inf; } static int CompareEventInfo (object a, object b) { EventInfo aa = (EventInfo) a, bb = (EventInfo) b; bool astatic = aa.GetAddMethod (true).IsStatic; bool bstatic = bb.GetAddMethod (true).IsStatic; if (astatic == bstatic) return CompareMemberInfo (a, b); if (astatic) return -1; return 1; } static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo)); public static EventInfo [] Sort (EventInfo [] inf) { Array.Sort (inf, EventInfoComparer); return inf; } } } #endif