// (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.
+//
+
using System;
using System.Reflection;
using System.Collections;
using System.CodeDom.Compiler;
using System.IO;
+using System.Text;
public class Outline {
+ Options options;
IndentedTextWriter o;
Type t;
- public Outline (Type t, TextWriter output)
+ public Outline (Type t, TextWriter output, Options options)
{
this.t = t;
- this.o = new IndentedTextWriter (output, " ");
+ this.o = new IndentedTextWriter (output, "\t");
+ this.options = options;
}
- public void OutlineType (BindingFlags flags)
+ public void OutlineType ()
{
bool first;
o.Write (GetTypeKind (t));
o.Write (" ");
- Type [] interfaces = (Type []) Comparer.Sort (t.GetInterfaces ());
+ Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, options.DeclaredOnly));
Type parent = t.BaseType;
if (t.IsSubclassOf (typeof (System.MulticastDelegate))) {
o.Write (FormatType (method.ReturnType));
o.Write (" ");
- o.Write (t.Name);
+ o.Write (GetTypeName (t));
o.Write (" (");
OutlineParams (method.GetParameters ());
- o.WriteLine (");");
+ o.Write (")");
+#if NET_2_0
+ WriteGenericConstraints (t.GetGenericArguments ());
+#endif
+
+ o.WriteLine (";");
return;
}
- o.Write (t.Name);
+ o.Write (GetTypeName (t));
if (((parent != null && parent != typeof (object) && parent != typeof (ValueType)) || interfaces.Length != 0) && ! t.IsEnum) {
first = true;
o.Write (" : ");
o.Write (FormatType (intf));
}
}
-
+
+ if (t.IsEnum) {
+ Type underlyingType = Enum.GetUnderlyingType (t);
+ if (underlyingType != typeof (int))
+ o.Write (" : {0}", FormatType (underlyingType));
+ }
+#if NET_2_0
+ WriteGenericConstraints (t.GetGenericArguments ());
+#endif
o.WriteLine (" {");
o.Indent++;
first = true;
- foreach (ConstructorInfo ci in t.GetConstructors (flags)) {
+ foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) {
+
+ if (! ShowMember (ci))
+ continue;
+
if (first)
o.WriteLine ();
first = false;
first = true;
- foreach (MethodInfo m in Comparer.Sort (t.GetMethods (flags))) {
+ foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) {
+
+ if (! ShowMember (m))
+ continue;
+
if ((m.Attributes & MethodAttributes.SpecialName) != 0)
continue;
first = true;
- foreach (MethodInfo m in t.GetMethods (flags)) {
+ foreach (MethodInfo m in t.GetMethods (DefaultFlags)) {
+
+ if (! ShowMember (m))
+ continue;
+
if ((m.Attributes & MethodAttributes.SpecialName) == 0)
continue;
if (!(m.Name.StartsWith ("op_")))
first = true;
- foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (flags))) {
+ 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 = true;
- foreach (FieldInfo fi in t.GetFields (flags)) {
+ foreach (FieldInfo fi in t.GetFields (DefaultFlags)) {
+
+ if (! ShowMember (fi))
+ continue;
if (first)
o.WriteLine ();
first = true;
- foreach (EventInfo ei in Comparer.Sort (t.GetEvents (flags))) {
+ foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) {
+
+ if (! ShowMember (ei.GetAddMethod (true)))
+ continue;
if (first)
o.WriteLine ();
first = true;
- foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (flags))) {
+ foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) {
+
+ if (! ShowMember (ntype))
+ continue;
if (first)
o.WriteLine ();
first = false;
- new Outline (ntype, o).OutlineType (flags);
+ new Outline (ntype, o, options).OutlineType ();
}
o.Indent--; o.WriteLine ("}");
}
+
+ BindingFlags DefaultFlags {
+ get {
+ BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
+
+ if (options.DeclaredOnly)
+ f |= BindingFlags.DeclaredOnly;
+
+ return f;
+ }
+ }
// FIXME: add other interesting attributes?
void OutlineAttributes ()
void OutlineEvent (EventInfo ei)
{
- MethodBase accessor = ei.GetAddMethod ();
+ MethodBase accessor = ei.GetAddMethod (true);
o.Write (GetMethodVisibility (accessor));
o.Write ("event ");
void OutlineConstructor (ConstructorInfo ci)
{
o.Write (GetMethodVisibility (ci));
- o.Write (t.Name);
+ o.Write (RemoveGenericArity (t.Name));
o.Write (" (");
OutlineParams (ci.GetParameters ());
o.Write (");");
void OutlineProperty (PropertyInfo pi)
{
ParameterInfo [] idxp = pi.GetIndexParameters ();
- MethodBase accessor = pi.CanRead ? pi.GetGetMethod (true) : pi.GetSetMethod (true);
+ 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.WriteLine (" {");
o.Indent ++;
- if (pi.CanRead) o.WriteLine ("get;");
- if (pi.CanWrite) o.WriteLine ("set;");
+ 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)
{
- o.Write (GetMethodVisibility (mi));
- o.Write (GetMethodModifiers (mi));
- o.Write (FormatType (mi.ReturnType));
- o.Write (" ");
+ 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);
+#if NET_2_0
+ o.Write (FormatGenericParams (mi.GetGenericArguments ()));
+#endif
o.Write (" (");
OutlineParams (mi.GetParameters ());
- o.Write (");");
+ o.Write (")");
+#if NET_2_0
+ WriteGenericConstraints (mi.GetGenericArguments ());
+#endif
+ o.Write (";");
}
void OutlineOperator (MethodInfo mi)
if (fi.IsFamily) o.Write ("protected ");
if (fi.IsPrivate) o.Write ("private ");
if (fi.IsAssembly) o.Write ("internal ");
- if (fi.IsLiteral) o.Write ("const ");
+ 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)
- {
+ if (fi.IsLiteral) {
+ object v = fi.GetValue (this);
+
+ // TODO: Escape values here
o.Write (" = ");
- o.Write (fi.GetValue (this));
+ 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 (";");
}
{
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.IsVirtual && !method.DeclaringType.IsInterface) {
+ if (method.IsAbstract)
+ return "abstract ";
+
return ((method.Attributes & MethodAttributes.NewSlot) != 0) ?
"virtual " :
- "override ";
-
+ "override ";
+ }
+
return null;
}
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)
{
- string type = t.FullName;
+ string type = GetFullName (t);
if (!type.StartsWith ("System.")) {
if (t.Namespace == this.t.Namespace)
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));
+#if NET_2_0
+ sb.Append (FormatGenericParams (t.GetGenericArguments ()));
+#endif
+ }
+
+ 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 NET_2_0
+ if (t.IsGenericParameter) {
+ sb.Append (t.Name);
+ return;
+ }
+#endif
+
+ 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);
+ }
+
+#if NET_2_0
+ 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;
+ }
+ }
+ }
+ }
+#endif
+
string OperatorFromName (string name)
{
switch (name) {
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 (options.ShowPrivate)
+ return true;
+
+ if (options.FilterObsolete && 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)
+ {
+ 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 {
{
MethodBase aa = (MethodBase) a, bb = (MethodBase) b;
- if (aa.IsStatic == bb.IsStatic)
- return CompareMemberInfo (a, 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 = 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;