Merge pull request #4453 from lambdageek/bug-49721
[mono.git] / mcs / tools / corcompare / mono-api-info.cs
index e2bbb36751afd6303549938e30d4f6f541d27fd4..0db2fed2633fa06bc8305983cc6b28624636edf0 100644 (file)
@@ -59,6 +59,9 @@ namespace CorCompare
                                { "h|?|help",
                                        "Show this message and exit.",
                                        v => showHelp = v != null },
+                               { "contract-api",
+                                       "Produces contract API with all members at each level of inheritance hierarchy",
+                                       v => FullAPISet = v != null },
                        };
 
                        var asms = options.Parse (args);
@@ -116,9 +119,11 @@ namespace CorCompare
 
                internal static bool AbiMode { get; private set; }
                internal static bool FollowForwarders { get; private set; }
+               internal static bool FullAPISet { get; set; }
        }
 
        public class Utils {
+               static char[] CharsToCleanup = new char[] { '<', '>', '/' };
 
                public static string CleanupTypeName (TypeReference type)
                {
@@ -127,7 +132,27 @@ namespace CorCompare
 
                public static string CleanupTypeName (string t)
                {
-                       return t.Replace ('<', '[').Replace ('>', ']').Replace ('/', '+');
+                       if (t.IndexOfAny (CharsToCleanup) == -1)
+                               return t;
+                       var sb = new StringBuilder (t.Length);
+                       for (int i = 0; i < t.Length; i++) {
+                               var ch = t [i];
+                               switch (ch) {
+                               case '<':
+                                       sb.Append ('[');
+                                       break;
+                               case '>':
+                                       sb.Append (']');
+                                       break;
+                               case '/':
+                                       sb.Append ('+');
+                                       break;
+                               default:
+                                       sb.Append (ch);
+                                       break;
+                               }
+                       }
+                       return sb.ToString ();
                }
        }
 
@@ -175,7 +200,7 @@ namespace CorCompare
                                if (File.Exists (assembly))
                                        return TypeHelper.Resolver.ResolveFile (assembly);
 
-                               return TypeHelper.Resolver.Resolve (assembly);
+                               return TypeHelper.Resolver.Resolve (AssemblyNameReference.Parse (assembly), new ReaderParameters ());
                        } catch (Exception e) {
                                Console.WriteLine (e);
                                return null;
@@ -517,7 +542,7 @@ namespace CorCompare
                                        members.Add (new ConstructorData (writer, ctors));
                                }
 
-                               PropertyDefinition[] properties = GetProperties (type);
+                               PropertyDefinition[] properties = GetProperties (type, Driver.FullAPISet);
                                if (properties.Length > 0) {
                                        Array.Sort (properties, PropertyDefinitionComparer.Default);
                                        members.Add (new PropertyData (writer, properties));
@@ -529,7 +554,7 @@ namespace CorCompare
                                        members.Add (new EventData (writer, events));
                                }
 
-                               MethodDefinition [] methods = GetMethods (type);
+                               MethodDefinition [] methods = GetMethods (type, Driver.FullAPISet);
                                if (methods.Length > 0) {
                                        Array.Sort (methods, MethodDefinitionComparer.Default);
                                        members.Add (new MethodData (writer, methods));
@@ -672,53 +697,104 @@ namespace CorCompare
                }
 
 
-               internal static PropertyDefinition [] GetProperties (TypeDefinition type) {
-                       ArrayList list = new ArrayList ();
+               internal static PropertyDefinition [] GetProperties (TypeDefinition type, bool fullAPI) {
+                       var list = new List<PropertyDefinition> ();
+
+                       var t = type;
+                       do {
+                               var properties = t.Properties;//type.GetProperties (flags);
+                               foreach (PropertyDefinition property in properties) {
+                                       MethodDefinition getMethod = property.GetMethod;
+                                       MethodDefinition setMethod = property.SetMethod;
 
-                       var properties = type.Properties;//type.GetProperties (flags);
-                       foreach (PropertyDefinition property in properties) {
-                               MethodDefinition getMethod = property.GetMethod;
-                               MethodDefinition setMethod = property.SetMethod;
+                                       bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
+                                       bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
 
-                               bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
-                               bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
+                                       // if neither the getter or setter should be documented, then
+                                       // skip the property
+                                       if (hasGetter || hasSetter) {
 
-                               // if neither the getter or setter should be documented, then
-                               // skip the property
-                               if (hasGetter || hasSetter) {
-                                       list.Add (property);
+                                               if (t != type && list.Any (l => l.Name == property.Name))
+                                                       continue;
+
+                                               list.Add (property);
+                                       }
                                }
-                       }
 
-                       return (PropertyDefinition []) list.ToArray (typeof (PropertyDefinition));
+                               if (!fullAPI)
+                                       break;
+
+                               if (t.IsInterface || t.IsEnum)
+                                       break;
+
+                               if (t.BaseType == null || t.BaseType.FullName == "System.Object")
+                                       t = null;
+                               else
+                                       t = t.BaseType.Resolve ();
+
+                       } while (t != null);
+
+                       return list.ToArray ();
                }
 
-               private MethodDefinition[] GetMethods (TypeDefinition type)
+               private MethodDefinition[] GetMethods (TypeDefinition type, bool fullAPI)
                {
-                       ArrayList list = new ArrayList ();
+                       var list = new List<MethodDefinition> ();
 
-                       var methods = type.Methods;//type.GetMethods (flags);
-                       foreach (MethodDefinition method in methods) {
-                               if (method.IsSpecialName && !method.Name.StartsWith ("op_", StringComparison.Ordinal))
-                                       continue;
+                       var t = type;
+                       do {
+                               var methods = t.Methods;//type.GetMethods (flags);
+                               foreach (MethodDefinition method in methods) {
+                                       if (method.IsSpecialName && !method.Name.StartsWith ("op_", StringComparison.Ordinal))
+                                               continue;
 
-                               // we're only interested in public or protected members
-                               if (!MustDocumentMethod(method))
-                                       continue;
+                                       // we're only interested in public or protected members
+                                       if (!MustDocumentMethod (method))
+                                               continue;
 
-                               if (IsFinalizer (method)) {
-                                       string name = method.DeclaringType.Name;
-                                       int arity = name.IndexOf ('`');
-                                       if (arity > 0)
-                                               name = name.Substring (0, arity);
+                                       if (t == type && IsFinalizer (method)) {
+                                               string name = method.DeclaringType.Name;
+                                               int arity = name.IndexOf ('`');
+                                               if (arity > 0)
+                                                       name = name.Substring (0, arity);
 
-                                       method.Name = "~" + name;
+                                               method.Name = "~" + name;
+                                       }
+
+                                       if (t != type && list.Any (l => l.DeclaringType != method.DeclaringType && l.Name == method.Name && l.Parameters.Count == method.Parameters.Count &&
+                                                                  l.Parameters.SequenceEqual (method.Parameters, new ParameterComparer ())))
+                                               continue;
+
+                                       list.Add (method);
                                }
 
-                               list.Add (method);
+                               if (!fullAPI)
+                                       break;
+
+                               if (t.IsInterface || t.IsEnum)
+                                       break;
+
+                               if (t.BaseType == null || t.BaseType.FullName == "System.Object")
+                                       t = null;
+                               else
+                                       t = t.BaseType.Resolve ();
+
+                       } while (t != null);
+
+                       return list.ToArray ();
+               }
+
+               sealed class ParameterComparer : IEqualityComparer<ParameterDefinition>
+               {
+                       public bool Equals (ParameterDefinition x, ParameterDefinition y)
+                       {
+                               return x.ParameterType.Name == y.ParameterType.Name;
                        }
 
-                       return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
+                       public int GetHashCode (ParameterDefinition obj)
+                       {
+                               return obj.ParameterType.Name.GetHashCode ();
+                       }
                }
 
                static bool IsFinalizer (MethodDefinition method)
@@ -989,6 +1065,15 @@ namespace CorCompare
                                AddAttribute ("sealed", "true");
                        if (mbase.IsStatic)
                                AddAttribute ("static", "true");
+                       var baseMethod = TypeHelper.GetBaseMethodInTypeHierarchy (mbase);
+                       if (baseMethod != null && baseMethod != mbase) {
+                               // This indicates whether this method is an override of another method.
+                               // This information is not necessarily available in the api info for any
+                               // particular assembly, because a method is only overriding another if
+                               // there is a base virtual function with the same signature, and that
+                               // base method can come from another assembly.
+                               AddAttribute ("is-override", "true");
+                       }
                        string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType);
                        if (rettype != "System.Void" || !mbase.IsConstructor)
                                AddAttribute ("returntype", (rettype));
@@ -1004,9 +1089,12 @@ namespace CorCompare
                        if (!(memberDefenition is MethodDefinition))
                                return;
 
-                       MethodDefinition mbase = (MethodDefinition) memberDefenition;
+                       MethodDefinition mbase = (MethodDefinition)memberDefenition;
+
+                       ParameterData parms = new ParameterData (writer, mbase.Parameters) {
+                               HasExtensionParameter = mbase.CustomAttributes.Any (l => l.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
+                       };
 
-                       ParameterData parms = new ParameterData (writer, mbase.Parameters);
                        parms.DoOutput ();
 
                        MemberData.OutputGenericParameters (writer, mbase);
@@ -1052,8 +1140,11 @@ namespace CorCompare
                        this.parameters = parameters;
                }
 
+               public bool HasExtensionParameter { get; set; }
+
                public override void DoOutput ()
                {
+                       bool first = true;
                        writer.WriteStartElement ("parameters");
                        foreach (ParameterDefinition parameter in parameters) {
                                writer.WriteStartElement ("parameter");
@@ -1061,13 +1152,17 @@ namespace CorCompare
                                AddAttribute ("position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture));
                                AddAttribute ("attrib", ((int) parameter.Attributes).ToString());
 
-                               string direction = "in";
+                               string direction = first && HasExtensionParameter ? "this" : "in";
+                               first = false;
 
-                               if (parameter.ParameterType is ByReferenceType)
+                               var pt = parameter.ParameterType;
+                               var brt = pt as ByReferenceType;
+                               if (brt != null) {
                                        direction = parameter.IsOut ? "out" : "ref";
+                                       pt = brt.ElementType;
+                               }
 
-                               TypeReference t = parameter.ParameterType;
-                               AddAttribute ("type", Utils.CleanupTypeName (t));
+                               AddAttribute ("type", Utils.CleanupTypeName (pt));
 
                                if (parameter.IsOptional) {
                                        AddAttribute ("optional", "true");
@@ -1085,17 +1180,9 @@ namespace CorCompare
                }
        }
 
-       class AttributeData : BaseData
+       class AttributeData
        {
-               IList<ICustomAttributeProvider> providers;
-
-               AttributeData (XmlWriter writer, IList<ICustomAttributeProvider> providers)
-                       : base (writer)
-               {
-                       this.providers = providers;
-               }
-
-               public override void DoOutput ()
+               public static void DoOutput (XmlWriter writer, IList<ICustomAttributeProvider> providers)
                {
                        if (writer == null)
                                throw new InvalidOperationException ("Document not set");
@@ -1128,31 +1215,34 @@ namespace CorCompare
                                        string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType);
 
                                        writer.WriteStartElement ("attribute");
-                                       AddAttribute ("name", attName);
-
-                                       var attribute_mapping = CreateAttributeMapping (att).Where ((kvp) => kvp.Key != "TypeId");
-
-                                       if (attribute_mapping.Any ()) {
-                                               writer.WriteStartElement ("properties");
-                                               foreach (var kvp in attribute_mapping) {
-                                                       string name = kvp.Key;
-                                                       object o = kvp.Value;
-
-                                                       writer.WriteStartElement ("property");
-                                                       AddAttribute ("name", name);
-
-                                                       if (o == null) {
-                                                               AddAttribute ("value", "null");
-                                                       } else {
-                                                               string value = o.ToString ();
-                                                               if (attName.EndsWith ("GuidAttribute", StringComparison.Ordinal))
-                                                                       value = value.ToUpper ();
-                                                               AddAttribute ("value", value);
+                                       writer.WriteAttributeString ("name", attName);
+
+                                       var attribute_mapping = CreateAttributeMapping (att);
+
+                                       if (attribute_mapping != null) {
+                                               var mapping = attribute_mapping.Where ((attr) => attr.Key != "TypeId");
+                                               if (mapping.Any ()) {
+                                                       writer.WriteStartElement ("properties");
+                                                       foreach (var kvp in mapping) {
+                                                               string name = kvp.Key;
+                                                               object o = kvp.Value;
+
+                                                               writer.WriteStartElement ("property");
+                                                               writer.WriteAttributeString ("name", name);
+
+                                                               if (o == null) {
+                                                                       writer.WriteAttributeString ("value", "null");
+                                                               } else {
+                                                                       string value = o.ToString ();
+                                                                       if (attName.EndsWith ("GuidAttribute", StringComparison.Ordinal))
+                                                                               value = value.ToUpper ();
+                                                                       writer.WriteAttributeString ("value", value);
+                                                               }
+
+                                                               writer.WriteEndElement (); // property
                                                        }
-
-                                                       writer.WriteEndElement (); // property
+                                                       writer.WriteEndElement (); // properties
                                                }
-                                               writer.WriteEndElement (); // properties
                                        }
                                        writer.WriteEndElement (); // attribute
                                }
@@ -1163,20 +1253,20 @@ namespace CorCompare
 
                static Dictionary<string, object> CreateAttributeMapping (CustomAttribute attribute)
                {
-                       var mapping = new Dictionary<string, object> ();
+                       Dictionary<string, object> mapping = null;
 
-                       PopulateMapping (mapping, attribute);
+                       PopulateMapping (ref mapping, attribute);
 
                        var constructor = attribute.Constructor.Resolve ();
                        if (constructor == null || !constructor.HasParameters)
                                return mapping;
 
-                       PopulateMapping (mapping, constructor, attribute);
+                       PopulateMapping (ref mapping, constructor, attribute);
 
                        return mapping;
                }
 
-               static void PopulateMapping (Dictionary<string, object> mapping, CustomAttribute attribute)
+               static void PopulateMapping (ref Dictionary<string, object> mapping, CustomAttribute attribute)
                {
                        if (!attribute.HasProperties)
                                return;
@@ -1188,13 +1278,15 @@ namespace CorCompare
                                if (arg.Value is CustomAttributeArgument)
                                        arg = (CustomAttributeArgument) arg.Value;
 
+                               if (mapping == null)
+                                       mapping = new Dictionary<string, object> (StringComparer.Ordinal);
                                mapping.Add (name, GetArgumentValue (arg.Type, arg.Value));
                        }
                }
 
                static Dictionary<FieldReference, int> CreateArgumentFieldMapping (MethodDefinition constructor)
                {
-                       Dictionary<FieldReference, int> field_mapping = new Dictionary<FieldReference, int> ();
+                       Dictionary<FieldReference, int> field_mapping = null;
 
                        int? argument = null;
 
@@ -1222,6 +1314,9 @@ namespace CorCompare
                                        if (!argument.HasValue)
                                                break;
 
+                                       if (field_mapping == null)
+                                               field_mapping = new Dictionary<FieldReference, int> ();
+                                       
                                        if (!field_mapping.ContainsKey (field))
                                                field_mapping.Add (field, (int) argument - 1);
 
@@ -1235,7 +1330,7 @@ namespace CorCompare
 
                static Dictionary<PropertyDefinition, FieldReference> CreatePropertyFieldMapping (TypeDefinition type)
                {
-                       Dictionary<PropertyDefinition, FieldReference> property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
+                       Dictionary<PropertyDefinition, FieldReference> property_mapping = null;
 
                        foreach (PropertyDefinition property in type.Properties) {
                                if (property.GetMethod == null)
@@ -1251,6 +1346,8 @@ namespace CorCompare
                                        if (field.DeclaringType.FullName != type.FullName)
                                                continue;
 
+                                       if (property_mapping == null)
+                                               property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
                                        property_mapping.Add (property, field);
                                        break;
                                }
@@ -1259,7 +1356,7 @@ namespace CorCompare
                        return property_mapping;
                }
 
-               static void PopulateMapping (Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
+               static void PopulateMapping (ref Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
                {
                        if (!constructor.HasBody)
                                return;
@@ -1272,14 +1369,26 @@ namespace CorCompare
                                        new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (int) ca[2].Value, (int) ca[3].Value, (int) ca[4].Value) :
                                        new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (uint) ca[2].Value, (uint) ca[3].Value, (uint) ca[4].Value);
 
+                               if (mapping == null)
+                                       mapping = new Dictionary<string, object> (StringComparer.Ordinal);
                                mapping.Add ("Value", dca.Value);
                                return;
                        case "System.ComponentModel.BindableAttribute":
                                if (ca.Count != 1)
                                        break;
 
+                               if (mapping == null)
+                                       mapping = new Dictionary<string, object> (StringComparer.Ordinal);
+
                                if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) {
                                        mapping.Add ("Bindable", ca[0].Value);
+                               } else if (constructor.Parameters[0].ParameterType.FullName == "System.ComponentModel.BindableSupport") {
+                                       if ((int)ca[0].Value == 0)
+                                               mapping.Add ("Bindable", false);
+                                       else if ((int)ca[0].Value == 1)
+                                               mapping.Add ("Bindable", true);
+                                       else
+                                               throw new NotImplementedException ();
                                } else {
                                        throw new NotImplementedException ();
                                }
@@ -1288,18 +1397,24 @@ namespace CorCompare
                        }
 
                        var field_mapping = CreateArgumentFieldMapping (constructor);
-                       var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
-
-                       foreach (var pair in property_mapping) {
-                               int argument;
-                               if (!field_mapping.TryGetValue (pair.Value, out argument))
-                                       continue;
-
-                               var ca_arg = ca [argument];
-                               if (ca_arg.Value is CustomAttributeArgument)
-                                       ca_arg = (CustomAttributeArgument) ca_arg.Value;
-
-                               mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value));
+                       if (field_mapping != null) { 
+                               var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
+
+                               if (property_mapping != null) {
+                                       foreach (var pair in property_mapping) {
+                                               int argument;
+                                               if (!field_mapping.TryGetValue (pair.Value, out argument))
+                                                       continue;
+
+                                               var ca_arg = ca [argument];
+                                               if (ca_arg.Value is CustomAttributeArgument)
+                                                       ca_arg = (CustomAttributeArgument)ca_arg.Value;
+
+                                               if (mapping == null)
+                                                       mapping = new Dictionary<string, object> (StringComparer.Ordinal);
+                                               mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value));
+                                       }
+                               }
                        }
                }
 
@@ -1415,8 +1530,7 @@ namespace CorCompare
 
                public static void OutputAttributes (XmlWriter writer, params ICustomAttributeProvider[] providers)
                {
-                       AttributeData ad = new AttributeData (writer, providers);
-                       ad.DoOutput ();
+                       AttributeData.DoOutput (writer, providers);
                }
        }
 
@@ -1435,13 +1549,13 @@ namespace CorCompare
 
                                ParameterDefinition info = infos [i];
 
-                               string modifier;
-                               if ((info.Attributes & ParameterAttributes.In) != 0)
-                                       modifier = "in";
-                               else if ((info.Attributes & ParameterAttributes.Out) != 0)
-                                       modifier = "out";
-                               else
-                                       modifier = string.Empty;
+                               string modifier = string.Empty;
+                               if (info.ParameterType.IsByReference) {
+                                       if ((info.Attributes & ParameterAttributes.In) != 0)
+                                               modifier = "in";
+                                       else if ((info.Attributes & ParameterAttributes.Out) != 0)
+                                               modifier = "out";
+                               }
 
                                if (modifier.Length > 0) {
                                        signature.Append (modifier);
@@ -1530,6 +1644,15 @@ namespace CorCompare
                        if (res != 0)
                                return res;
 
+                       if (ma.HasGenericParameters != mb.HasGenericParameters)
+                               return ma.HasGenericParameters ? -1 : 1;
+
+                       if (ma.HasGenericParameters && mb.HasGenericParameters) {
+                               res = ma.GenericParameters.Count - mb.GenericParameters.Count;
+                               if (res != 0)
+                                       return res;
+                       }
+
                        // operators can differ by only return type
                        return string.CompareOrdinal (ma.ReturnType.FullName, mb.ReturnType.FullName);
                }