X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Ftools%2Fcorcompare%2Fmono-api-info.cs;h=8b1ad945095c64d208ea0ce2d8626209b6c68709;hb=c1d81649cc1d16ee47bd6fb951e220d8aba6a1d0;hp=a689210b12bbcd6a1a791cef41794be3e1a4378c;hpb=0b4bc83e79ca3057693089dc7f926004bb9d9592;p=mono.git diff --git a/mcs/tools/corcompare/mono-api-info.cs b/mcs/tools/corcompare/mono-api-info.cs index a689210b12b..8b1ad945095 100644 --- a/mcs/tools/corcompare/mono-api-info.cs +++ b/mcs/tools/corcompare/mono-api-info.cs @@ -28,56 +28,98 @@ namespace CorCompare { public static int Main (string [] args) { - if (args.Length == 0) - return 1; - + bool showHelp = false; AbiMode = false; - - AssemblyCollection acoll = new AssemblyCollection (); + FollowForwarders = false; + string output = null; + + var acoll = new AssemblyCollection (); + + var options = new Mono.Options.OptionSet { + "usage: mono-api-info [OPTIONS+] ASSEMBLY+", + "", + "Expose IL structure of CLR assemblies as XML.", + "", + "Available Options:", + { "abi", + "Generate ABI, not API; contains only classes with instance fields which are not [NonSerialized].", + v => AbiMode = v != null }, + { "f|follow-forwarders", + "Follow type forwarders.", + v => FollowForwarders = v != null }, + { "d|L|lib|search-directory=", + "Check for assembly references in {DIRECTORY}.", + v => TypeHelper.Resolver.AddSearchDirectory (v) }, + { "r=", + "Read and register the file {ASSEMBLY}, and add the directory containing ASSEMBLY to the search path.", + v => TypeHelper.Resolver.ResolveFile (v) }, + { "o=", + "The output file. If not specified the output will be written to stdout.", + v => output = v }, + { "h|?|help", + "Show this message and exit.", + v => showHelp = v != null }, + }; + + var asms = options.Parse (args); + + if (showHelp || asms.Count == 0) { + options.WriteOptionDescriptions (Console.Out); + Console.WriteLine (); + return showHelp? 0 :1; + } string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"assembly\GAC\MSDATASRC\7.0.3300.0__b03f5f7f11d50a3a")); - foreach (string arg in args) { - if (arg == "--abi") { - AbiMode = true; - } else { - acoll.Add (arg); - - if (arg.Contains ("v3.0")) { - TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727")); - } else if (arg.Contains ("v3.5")) { - TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727")); - TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v3.0\Windows Communication Foundation")); - } else if (arg.Contains ("v4.0")) { - if (arg.Contains ("Silverlight")) { - TypeHelper.Resolver.AddSearchDirectory (Path.Combine (pf, @"Microsoft Silverlight\4.0.51204.0")); - } else { - TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319")); - TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319\WPF")); - } + foreach (string arg in asms) { + acoll.Add (arg); + + if (arg.Contains ("v3.0")) { + TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727")); + } else if (arg.Contains ("v3.5")) { + TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727")); + TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v3.0\Windows Communication Foundation")); + } else if (arg.Contains ("v4.0")) { + if (arg.Contains ("Silverlight")) { + TypeHelper.Resolver.AddSearchDirectory (Path.Combine (pf, @"Microsoft Silverlight\4.0.51204.0")); } else { - TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg)); + TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319")); + TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319\WPF")); } + } else { + TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg)); } } - XmlDocument doc = new XmlDocument (); - acoll.Document = doc; - acoll.DoOutput (); - - var writer = new WellFormedXmlWriter (new XmlTextWriter (Console.Out) { Formatting = Formatting.Indented }); - XmlNode decl = doc.CreateXmlDeclaration ("1.0", "utf-8", null); - doc.InsertBefore (decl, doc.DocumentElement); - doc.WriteTo (writer); + StreamWriter outputStream = null; + if (!string.IsNullOrEmpty (output)) + outputStream = new StreamWriter (output); + try { + TextWriter outStream = outputStream ?? Console.Out; + var settings = new XmlWriterSettings (); + settings.Indent = true; + var textWriter = XmlWriter.Create (outStream, settings); + var writer = new WellFormedXmlWriter (textWriter); + writer.WriteStartDocument (); + acoll.Writer = writer; + acoll.DoOutput (); + writer.WriteEndDocument (); + writer.Flush (); + } finally { + if (outputStream != null) + outputStream.Dispose (); + } return 0; } internal static bool AbiMode { get; private set; } + internal static bool FollowForwarders { get; private set; } } public class Utils { + static char[] CharsToCleanup = new char[] { '<', '>', '/' }; public static string CleanupTypeName (TypeReference type) { @@ -86,13 +128,33 @@ 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 (); } } class AssemblyCollection { - XmlDocument document; + XmlWriter writer; List assemblies = new List (); public AssemblyCollection () @@ -113,19 +175,19 @@ namespace CorCompare public void DoOutput () { - if (document == null) + if (writer == null) throw new InvalidOperationException ("Document not set"); - XmlNode nassemblies = document.CreateElement ("assemblies", null); - document.AppendChild (nassemblies); + writer.WriteStartElement ("assemblies"); foreach (AssemblyDefinition a in assemblies) { - AssemblyData data = new AssemblyData (document, nassemblies, a); + AssemblyData data = new AssemblyData (writer, a); data.DoOutput (); } + writer.WriteEndElement (); } - public XmlDocument Document { - set { document = value; } + public XmlWriter Writer { + set { writer = value; } } AssemblyDefinition LoadAssembly (string assembly) @@ -144,22 +206,18 @@ namespace CorCompare abstract class BaseData { - protected XmlDocument document; - protected XmlNode parent; + protected XmlWriter writer; - protected BaseData (XmlDocument doc, XmlNode parent) + protected BaseData (XmlWriter writer) { - this.document = doc; - this.parent = parent; + this.writer = writer; } public abstract void DoOutput (); - protected void AddAttribute (XmlNode node, string name, string value) + protected void AddAttribute (string name, string value) { - XmlAttribute attr = document.CreateAttribute (name); - attr.Value = value; - node.Attributes.Append (attr); + writer.WriteAttributeString (name, value); } } @@ -167,38 +225,34 @@ namespace CorCompare { AssemblyDefinition ass; - public TypeForwardedToData (XmlDocument document, XmlNode parent, AssemblyDefinition ass) - : base (document, parent) + public TypeForwardedToData (XmlWriter writer, AssemblyDefinition ass) + : base (writer) { this.ass = ass; } public override void DoOutput () { - XmlNode natts = parent.SelectSingleNode("attributes"); - if (natts == null) { - natts = document.CreateElement ("attributes", null); - parent.AppendChild (natts); - } - foreach (ExportedType type in ass.MainModule.ExportedTypes) { if (((uint)type.Attributes & 0x200000u) == 0) continue; - XmlNode node = document.CreateElement ("attribute"); - AddAttribute (node, "name", typeof (TypeForwardedToAttribute).FullName); - XmlNode properties = node.AppendChild (document.CreateElement ("properties")); - XmlNode property = properties.AppendChild (document.CreateElement ("property")); - AddAttribute (property, "name", "Destination"); - AddAttribute (property, "value", Utils.CleanupTypeName (type.FullName)); - natts.AppendChild (node); + writer.WriteStartElement ("attribute"); + AddAttribute ("name", typeof (TypeForwardedToAttribute).FullName); + writer.WriteStartElement ("properties"); + writer.WriteStartElement ("property"); + AddAttribute ("name", "Destination"); + AddAttribute ("value", Utils.CleanupTypeName (type.FullName)); + writer.WriteEndElement (); // properties + writer.WriteEndElement (); // properties + writer.WriteEndElement (); // attribute } } - public static void OutputForwarders (XmlDocument document, XmlNode parent, AssemblyDefinition ass) + public static void OutputForwarders (XmlWriter writer, AssemblyDefinition ass) { - TypeForwardedToData tftd = new TypeForwardedToData (document, parent, ass); + TypeForwardedToData tftd = new TypeForwardedToData (writer, ass); tftd.DoOutput (); } } @@ -207,40 +261,51 @@ namespace CorCompare { AssemblyDefinition ass; - public AssemblyData (XmlDocument document, XmlNode parent, AssemblyDefinition ass) - : base (document, parent) + public AssemblyData (XmlWriter writer, AssemblyDefinition ass) + : base (writer) { this.ass = ass; } public override void DoOutput () { - if (document == null) + if (writer == null) throw new InvalidOperationException ("Document not set"); - XmlNode nassembly = document.CreateElement ("assembly", null); + writer.WriteStartElement ("assembly"); AssemblyNameDefinition aname = ass.Name; - AddAttribute (nassembly, "name", aname.Name); - AddAttribute (nassembly, "version", aname.Version.ToString ()); - parent.AppendChild (nassembly); - TypeForwardedToData.OutputForwarders (document, nassembly, ass); - AttributeData.OutputAttributes (document, nassembly, ass.CustomAttributes); - var typesCollection = ass.MainModule.Types; - if (typesCollection == null || typesCollection.Count == 0) + AddAttribute ("name", aname.Name); + AddAttribute ("version", aname.Version.ToString ()); + + AttributeData.OutputAttributes (writer, ass); + + var types = new List (); + if (ass.MainModule.Types != null) { + types.AddRange (ass.MainModule.Types); + } + + if (Driver.FollowForwarders && ass.MainModule.ExportedTypes != null) { + foreach (var t in ass.MainModule.ExportedTypes) { + var forwarded = t.Resolve (); + if (forwarded == null) { + throw new Exception ("Could not resolve forwarded type " + t.FullName + " in " + ass.Name); + } + types.Add (forwarded); + } + } + + if (types.Count == 0) { + writer.WriteEndElement (); // assembly return; - object [] typesArray = new object [typesCollection.Count]; - for (int i = 0; i < typesCollection.Count; i++) { - typesArray [i] = typesCollection [i]; } - Array.Sort (typesArray, TypeReferenceComparer.Default); - XmlNode nss = document.CreateElement ("namespaces", null); - nassembly.AppendChild (nss); + types.Sort (TypeReferenceComparer.Default); + + writer.WriteStartElement ("namespaces"); string current_namespace = "$%&$&"; - XmlNode ns = null; - XmlNode classes = null; - foreach (TypeDefinition t in typesArray) { + bool in_namespace = false; + foreach (TypeDefinition t in types) { if (string.IsNullOrEmpty (t.Namespace)) continue; @@ -252,16 +317,30 @@ namespace CorCompare if (t.Namespace != current_namespace) { current_namespace = t.Namespace; - ns = document.CreateElement ("namespace", null); - AddAttribute (ns, "name", current_namespace); - nss.AppendChild (ns); - classes = document.CreateElement ("classes", null); - ns.AppendChild (classes); + if (in_namespace) { + writer.WriteEndElement (); // classes + writer.WriteEndElement (); // namespace + } else { + in_namespace = true; + } + writer.WriteStartElement ("namespace"); + AddAttribute ("name", current_namespace); + writer.WriteStartElement ("classes"); } - TypeData bd = new TypeData (document, classes, t); + TypeData bd = new TypeData (writer, t); bd.DoOutput (); + } + + if (in_namespace) { + writer.WriteEndElement (); // classes + writer.WriteEndElement (); // namespace + } + + writer.WriteEndElement (); // namespaces + + writer.WriteEndElement (); // assembly } } @@ -269,34 +348,42 @@ namespace CorCompare { MemberReference [] members; - public MemberData (XmlDocument document, XmlNode parent, MemberReference [] members) - : base (document, parent) + public MemberData (XmlWriter writer, MemberReference [] members) + : base (writer) { this.members = members; } + protected virtual ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member) + { + return null; + } + public override void DoOutput () { - XmlNode mclass = document.CreateElement (ParentTag, null); - parent.AppendChild (mclass); + writer.WriteStartElement (ParentTag); foreach (MemberReference member in members) { - XmlNode mnode = document.CreateElement (Tag, null); - mclass.AppendChild (mnode); - AddAttribute (mnode, "name", GetName (member)); + writer.WriteStartElement (Tag); + AddAttribute ("name", GetName (member)); if (!NoMemberAttributes) - AddAttribute (mnode, "attrib", GetMemberAttributes (member)); + AddAttribute ("attrib", GetMemberAttributes (member)); + AddExtraAttributes (member); - AttributeData.OutputAttributes (document, mnode, GetCustomAttributes (member)); + AttributeData.OutputAttributes (writer, (ICustomAttributeProvider) member, GetAdditionalCustomAttributeProvider (member)); - AddExtraData (mnode, member); + AddExtraData (member); + writer.WriteEndElement (); // Tag } - } + writer.WriteEndElement (); // ParentTag + } - protected abstract IList GetCustomAttributes (MemberReference member); + protected virtual void AddExtraData (MemberReference memberDefenition) + { + } - protected virtual void AddExtraData (XmlNode p, MemberReference memberDefenition) + protected virtual void AddExtraAttributes (MemberReference memberDefinition) { } @@ -323,39 +410,42 @@ namespace CorCompare get { return "NoTAG"; } } - public static void OutputGenericParameters (XmlDocument document, XmlNode nclass, IGenericParameterProvider provider) + public static void OutputGenericParameters (XmlWriter writer, IGenericParameterProvider provider) { if (provider.GenericParameters.Count == 0) return; var gparameters = provider.GenericParameters; - XmlElement ngeneric = document.CreateElement (string.Format ("generic-parameters")); - nclass.AppendChild (ngeneric); + writer.WriteStartElement ("generic-parameters"); foreach (GenericParameter gp in gparameters) { - XmlElement nparam = document.CreateElement (string.Format ("generic-parameter")); - nparam.SetAttribute ("name", gp.Name); - nparam.SetAttribute ("attributes", ((int) gp.Attributes).ToString ()); - - AttributeData.OutputAttributes (document, nparam, gp.CustomAttributes); + writer.WriteStartElement ("generic-parameter"); + writer.WriteAttributeString ("name", gp.Name); + writer.WriteAttributeString ("attributes", ((int) gp.Attributes).ToString ()); - ngeneric.AppendChild (nparam); + AttributeData.OutputAttributes (writer, gp); var constraints = gp.Constraints; - if (constraints.Count == 0) + if (constraints.Count == 0) { + writer.WriteEndElement (); // generic-parameter continue; + } - XmlElement nconstraint = document.CreateElement ("generic-parameter-constraints"); + writer.WriteStartElement ("generic-parameter-constraints"); foreach (TypeReference constraint in constraints) { - XmlElement ncons = document.CreateElement ("generic-parameter-constraint"); - ncons.SetAttribute ("name", Utils.CleanupTypeName (constraint)); - nconstraint.AppendChild (ncons); + writer.WriteStartElement ("generic-parameter-constraint"); + writer.WriteAttributeString ("name", Utils.CleanupTypeName (constraint)); + writer.WriteEndElement (); // generic-parameter-constraint } - nparam.AppendChild (nconstraint); + writer.WriteEndElement (); // generic-parameter-constraints + + writer.WriteEndElement (); // generic-parameter } + + writer.WriteEndElement (); // generic-parameters } } @@ -363,117 +453,107 @@ namespace CorCompare { TypeDefinition type; - public TypeData (XmlDocument document, XmlNode parent, TypeDefinition type) - : base (document, parent, null) + public TypeData (XmlWriter writer, TypeDefinition type) + : base (writer, null) { this.type = type; } - - protected override IList GetCustomAttributes (MemberReference member) { - return ((TypeDefinition) member).CustomAttributes; - } - public override void DoOutput () { - if (document == null) + if (writer == null) throw new InvalidOperationException ("Document not set"); - XmlNode nclass = document.CreateElement ("class", null); - AddAttribute (nclass, "name", type.Name); + writer.WriteStartElement ("class"); + AddAttribute ("name", type.Name); string classType = GetClassType (type); - AddAttribute (nclass, "type", classType); + AddAttribute ("type", classType); if (type.BaseType != null) - AddAttribute (nclass, "base", Utils.CleanupTypeName (type.BaseType)); + AddAttribute ("base", Utils.CleanupTypeName (type.BaseType)); if (type.IsSealed) - AddAttribute (nclass, "sealed", "true"); + AddAttribute ("sealed", "true"); if (type.IsAbstract) - AddAttribute (nclass, "abstract", "true"); + AddAttribute ("abstract", "true"); if ( (type.Attributes & TypeAttributes.Serializable) != 0 || type.IsEnum) - AddAttribute (nclass, "serializable", "true"); + AddAttribute ("serializable", "true"); string charSet = GetCharSet (type); - AddAttribute (nclass, "charset", charSet); + AddAttribute ("charset", charSet); string layout = GetLayout (type); if (layout != null) - AddAttribute (nclass, "layout", layout); + AddAttribute ("layout", layout); if (type.PackingSize >= 0) { - AddAttribute (nclass, "pack", type.PackingSize.ToString ()); + AddAttribute ("pack", type.PackingSize.ToString ()); } if (type.ClassSize >= 0) { - AddAttribute (nclass, "size", type.ClassSize.ToString ()); + AddAttribute ("size", type.ClassSize.ToString ()); } - parent.AppendChild (nclass); + if (type.IsEnum) { + var value_type = GetEnumValueField (type); + if (value_type == null) + throw new NotSupportedException (); - AttributeData.OutputAttributes (document, nclass, GetCustomAttributes(type)); + AddAttribute ("enumtype", Utils.CleanupTypeName (value_type.FieldType)); + } - XmlNode ifaces = null; + AttributeData.OutputAttributes (writer, type); - foreach (TypeReference iface in TypeHelper.GetInterfaces (type)) { - if (!TypeHelper.IsPublic (iface)) - // we're only interested in public interfaces - continue; + var ifaces = TypeHelper.GetInterfaces (type). + Where ((iface) => TypeHelper.IsPublic (iface)). // we're only interested in public interfaces + OrderBy (s => s.FullName, StringComparer.Ordinal); - if (ifaces == null) { - ifaces = document.CreateElement ("interfaces", null); - nclass.AppendChild (ifaces); + if (ifaces.Any ()) { + writer.WriteStartElement ("interfaces"); + foreach (TypeReference iface in ifaces) { + writer.WriteStartElement ("interface"); + AddAttribute ("name", Utils.CleanupTypeName (iface)); + writer.WriteEndElement (); // interface } - - XmlNode iface_node = document.CreateElement ("interface", null); - AddAttribute (iface_node, "name", Utils.CleanupTypeName (iface)); - ifaces.AppendChild (iface_node); + writer.WriteEndElement (); // interfaces } - MemberData.OutputGenericParameters (document, nclass, type); + MemberData.OutputGenericParameters (writer, type); ArrayList members = new ArrayList (); FieldDefinition [] fields = GetFields (type); if (fields.Length > 0) { Array.Sort (fields, MemberReferenceComparer.Default); - FieldData fd = new FieldData (document, nclass, fields); + FieldData fd = new FieldData (writer, fields); members.Add (fd); } - if (type.IsEnum) { - var value_type = GetEnumValueField (type); - if (value_type == null) - throw new NotSupportedException (); - - AddAttribute (nclass, "enumtype", Utils.CleanupTypeName (value_type.FieldType)); - } - if (!Driver.AbiMode) { MethodDefinition [] ctors = GetConstructors (type); if (ctors.Length > 0) { - Array.Sort (ctors, MemberReferenceComparer.Default); - members.Add (new ConstructorData (document, nclass, ctors)); + Array.Sort (ctors, MethodDefinitionComparer.Default); + members.Add (new ConstructorData (writer, ctors)); } PropertyDefinition[] properties = GetProperties (type); if (properties.Length > 0) { - Array.Sort (properties, MemberReferenceComparer.Default); - members.Add (new PropertyData (document, nclass, properties)); + Array.Sort (properties, PropertyDefinitionComparer.Default); + members.Add (new PropertyData (writer, properties)); } EventDefinition [] events = GetEvents (type); if (events.Length > 0) { Array.Sort (events, MemberReferenceComparer.Default); - members.Add (new EventData (document, nclass, events)); + members.Add (new EventData (writer, events)); } MethodDefinition [] methods = GetMethods (type); if (methods.Length > 0) { - Array.Sort (methods, MemberReferenceComparer.Default); - members.Add (new MethodData (document, nclass, methods)); + Array.Sort (methods, MethodDefinitionComparer.Default); + members.Add (new MethodData (writer, methods)); } } @@ -495,15 +575,19 @@ namespace CorCompare nested.RemoveAt (i); } - if (nested.Count > 0) { - XmlNode classes = document.CreateElement ("classes", null); - nclass.AppendChild (classes); - foreach (TypeDefinition t in nested) { - TypeData td = new TypeData (document, classes, t); + var nestedArray = nested.ToArray (); + Array.Sort (nestedArray, TypeReferenceComparer.Default); + + writer.WriteStartElement ("classes"); + foreach (TypeDefinition t in nestedArray) { + TypeData td = new TypeData (writer, t); td.DoOutput (); } + writer.WriteEndElement (); // classes } + + writer.WriteEndElement (); // class } static FieldReference GetEnumValueField (TypeDefinition type) @@ -636,7 +720,7 @@ namespace CorCompare var methods = type.Methods;//type.GetMethods (flags); foreach (MethodDefinition method in methods) { - if (method.IsSpecialName && !method.Name.StartsWith ("op_")) + if (method.IsSpecialName && !method.Name.StartsWith ("op_", StringComparison.Ordinal)) continue; // we're only interested in public or protected members @@ -708,15 +792,11 @@ namespace CorCompare class FieldData : MemberData { - public FieldData (XmlDocument document, XmlNode parent, FieldDefinition [] members) - : base (document, parent, members) + public FieldData (XmlWriter writer, FieldDefinition [] members) + : base (writer, members) { } - protected override IList GetCustomAttributes (MemberReference member) { - return ((FieldDefinition) member).CustomAttributes; - } - protected override string GetName (MemberReference memberDefenition) { FieldDefinition field = (FieldDefinition) memberDefenition; @@ -729,11 +809,12 @@ namespace CorCompare return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture); } - protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) + protected override void AddExtraAttributes (MemberReference memberDefinition) { - base.AddExtraData (p, memberDefenition); - FieldDefinition field = (FieldDefinition) memberDefenition; - AddAttribute (p, "fieldtype", Utils.CleanupTypeName (field.FieldType)); + base.AddExtraAttributes (memberDefinition); + + FieldDefinition field = (FieldDefinition) memberDefinition; + AddAttribute ("fieldtype", Utils.CleanupTypeName (field.FieldType)); if (field.IsLiteral) { object value = field.Constant;//object value = field.GetValue (null); @@ -745,11 +826,11 @@ namespace CorCompare // stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture); //} //else { - stringValue = Convert.ToString (value, CultureInfo.InvariantCulture); + stringValue = Convert.ToString (value, CultureInfo.InvariantCulture); //} if (stringValue != null) - AddAttribute (p, "value", stringValue); + AddAttribute ("value", stringValue); } } @@ -764,30 +845,24 @@ namespace CorCompare class PropertyData : MemberData { - public PropertyData (XmlDocument document, XmlNode parent, PropertyDefinition [] members) - : base (document, parent, members) + public PropertyData (XmlWriter writer, PropertyDefinition [] members) + : base (writer, members) { } - protected override IList GetCustomAttributes (MemberReference member) { - return ((PropertyDefinition) member).CustomAttributes; - } - protected override string GetName (MemberReference memberDefenition) { PropertyDefinition prop = (PropertyDefinition) memberDefenition; return prop.Name; } - protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) + MethodDefinition [] GetMethods (PropertyDefinition prop, out bool haveParameters) { - base.AddExtraData (p, memberDefenition); - PropertyDefinition prop = (PropertyDefinition) memberDefenition; - AddAttribute (p, "ptype", Utils.CleanupTypeName (prop.PropertyType)); MethodDefinition _get = prop.GetMethod; MethodDefinition _set = prop.SetMethod; bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get)); bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set)); + haveParameters = haveGet || (haveSet && _set.Parameters.Count > 1); MethodDefinition [] methods; if (haveGet && haveSet) { @@ -798,16 +873,41 @@ namespace CorCompare methods = new MethodDefinition [] { _set }; } else { //odd - return; + return null; } - if (haveGet || _set.Parameters.Count > 1) { + return methods; + } + + protected override void AddExtraAttributes (MemberReference memberDefinition) + { + base.AddExtraAttributes (memberDefinition); + + PropertyDefinition prop = (PropertyDefinition) memberDefinition; + AddAttribute ("ptype", Utils.CleanupTypeName (prop.PropertyType)); + + bool haveParameters; + MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefinition, out haveParameters); + + if (methods != null && haveParameters) { string parms = Parameters.GetSignature (methods [0].Parameters); if (!string.IsNullOrEmpty (parms)) - AddAttribute (p, "params", parms); + AddAttribute ("params", parms); } - MethodData data = new MethodData (document, p, methods); + } + + protected override void AddExtraData (MemberReference memberDefenition) + { + base.AddExtraData (memberDefenition); + + bool haveParameters; + MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefenition, out haveParameters); + + if (methods == null) + return; + + MethodData data = new MethodData (writer, methods); //data.NoMemberAttributes = true; data.DoOutput (); } @@ -829,15 +929,11 @@ namespace CorCompare class EventData : MemberData { - public EventData (XmlDocument document, XmlNode parent, EventDefinition [] members) - : base (document, parent, members) + public EventData (XmlWriter writer, EventDefinition [] members) + : base (writer, members) { } - protected override IList GetCustomAttributes (MemberReference member) { - return ((EventDefinition) member).CustomAttributes; - } - protected override string GetName (MemberReference memberDefenition) { EventDefinition evt = (EventDefinition) memberDefenition; @@ -850,11 +946,12 @@ namespace CorCompare return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture); } - protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) + protected override void AddExtraAttributes (MemberReference memberDefinition) { - base.AddExtraData (p, memberDefenition); - EventDefinition evt = (EventDefinition) memberDefenition; - AddAttribute (p, "eventtype", Utils.CleanupTypeName (evt.EventType)); + base.AddExtraAttributes (memberDefinition); + + EventDefinition evt = (EventDefinition) memberDefinition; + AddAttribute ("eventtype", Utils.CleanupTypeName (evt.EventType)); } public override string ParentTag { @@ -870,15 +967,11 @@ namespace CorCompare { bool noAtts; - public MethodData (XmlDocument document, XmlNode parent, MethodDefinition [] members) - : base (document, parent, members) + public MethodData (XmlWriter writer, MethodDefinition [] members) + : base (writer, members) { } - protected override IList GetCustomAttributes (MemberReference member) { - return ((MethodDefinition) member).CustomAttributes; - } - protected override string GetName (MemberReference memberDefenition) { MethodDefinition method = (MethodDefinition) memberDefenition; @@ -894,34 +987,59 @@ namespace CorCompare return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture); } - protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) + protected override ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member) { - base.AddExtraData (p, memberDefenition); + var mbase = (MethodDefinition) member; + return mbase.MethodReturnType; + } - if (!(memberDefenition is MethodDefinition)) - return; + protected override void AddExtraAttributes (MemberReference memberDefinition) + { + base.AddExtraAttributes (memberDefinition); - MethodDefinition mbase = (MethodDefinition) memberDefenition; + if (!(memberDefinition is MethodDefinition)) + return; - ParameterData parms = new ParameterData (document, p, mbase.Parameters); - parms.DoOutput (); + MethodDefinition mbase = (MethodDefinition) memberDefinition; if (mbase.IsAbstract) - AddAttribute (p, "abstract", "true"); + AddAttribute ("abstract", "true"); if (mbase.IsVirtual) - AddAttribute (p, "virtual", "true"); + AddAttribute ("virtual", "true"); if (mbase.IsFinal && mbase.IsVirtual && mbase.IsReuseSlot) - AddAttribute (p, "sealed", "true"); + AddAttribute ("sealed", "true"); if (mbase.IsStatic) - AddAttribute (p, "static", "true"); - + 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 (p, "returntype", (rettype)); + AddAttribute ("returntype", (rettype)); +// +// if (mbase.MethodReturnType.HasCustomAttributes) +// AttributeData.OutputAttributes (writer, mbase.MethodReturnType); + } + + protected override void AddExtraData (MemberReference memberDefenition) + { + base.AddExtraData (memberDefenition); - AttributeData.OutputAttributes (document, p, mbase.MethodReturnType.CustomAttributes); + if (!(memberDefenition is MethodDefinition)) + return; + + MethodDefinition mbase = (MethodDefinition) memberDefenition; + + ParameterData parms = new ParameterData (writer, mbase.Parameters); + parms.DoOutput (); - MemberData.OutputGenericParameters (document, p, mbase); + MemberData.OutputGenericParameters (writer, mbase); } public override bool NoMemberAttributes { @@ -940,8 +1058,8 @@ namespace CorCompare class ConstructorData : MethodData { - public ConstructorData (XmlDocument document, XmlNode parent, MethodDefinition [] members) - : base (document, parent, members) + public ConstructorData (XmlWriter writer, MethodDefinition [] members) + : base (writer, members) { } @@ -958,23 +1076,20 @@ namespace CorCompare { private IList parameters; - public ParameterData (XmlDocument document, XmlNode parent, IList parameters) - : base (document, parent) + public ParameterData (XmlWriter writer, IList parameters) + : base (writer) { this.parameters = parameters; } public override void DoOutput () { - XmlNode parametersNode = document.CreateElement ("parameters"); - parent.AppendChild (parametersNode); - + writer.WriteStartElement ("parameters"); foreach (ParameterDefinition parameter in parameters) { - XmlNode paramNode = document.CreateElement ("parameter"); - parametersNode.AppendChild (paramNode); - AddAttribute (paramNode, "name", parameter.Name); - AddAttribute (paramNode, "position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture)); - AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString()); + writer.WriteStartElement ("parameter"); + AddAttribute ("name", parameter.Name); + AddAttribute ("position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture)); + AddAttribute ("attrib", ((int) parameter.Attributes).ToString()); string direction = "in"; @@ -982,105 +1097,115 @@ namespace CorCompare direction = parameter.IsOut ? "out" : "ref"; TypeReference t = parameter.ParameterType; - AddAttribute (paramNode, "type", Utils.CleanupTypeName (t)); + AddAttribute ("type", Utils.CleanupTypeName (t)); if (parameter.IsOptional) { - AddAttribute (paramNode, "optional", "true"); + AddAttribute ("optional", "true"); if (parameter.HasConstant) - AddAttribute (paramNode, "defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ()); + AddAttribute ("defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ()); } if (direction != "in") - AddAttribute (paramNode, "direction", direction); + AddAttribute ("direction", direction); - AttributeData.OutputAttributes (document, paramNode, parameter.CustomAttributes); + AttributeData.OutputAttributes (writer, parameter); + writer.WriteEndElement (); // parameter } + writer.WriteEndElement (); // parameters } } - class AttributeData : BaseData + class AttributeData { - IList atts; - - AttributeData (XmlDocument doc, XmlNode parent, IList attributes) - : base (doc, parent) + public static void DoOutput (XmlWriter writer, IList providers) { - atts = attributes; - } - - public override void DoOutput () - { - if (document == null) + if (writer == null) throw new InvalidOperationException ("Document not set"); - if (atts == null || atts.Count == 0) + if (providers == null || providers.Count == 0) + return; + + if (!providers.Any ((provider) => provider != null && provider.HasCustomAttributes)) return; - XmlNode natts = parent.SelectSingleNode("attributes"); - if (natts == null) { - natts = document.CreateElement ("attributes", null); - parent.AppendChild (natts); - } - - for (int i = 0; i < atts.Count; ++i) { - CustomAttribute att = atts [i]; + writer.WriteStartElement ("attributes"); - string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType); - if (SkipAttribute (att)) + foreach (var provider in providers) { + if (provider == null) + continue; + + if (!provider.HasCustomAttributes) continue; - XmlNode node = document.CreateElement ("attribute"); - AddAttribute (node, "name", attName); - - XmlNode properties = null; - - Dictionary attribute_mapping = CreateAttributeMapping (att); - - foreach (string name in attribute_mapping.Keys) { - if (name == "TypeId") - continue; - - if (properties == null) { - properties = node.AppendChild (document.CreateElement ("properties")); - } - - object o = attribute_mapping [name]; - - XmlNode n = properties.AppendChild (document.CreateElement ("property")); - AddAttribute (n, "name", name); - if (o == null) { - AddAttribute (n, "value", "null"); - continue; + var ass = provider as AssemblyDefinition; + if (ass != null && !Driver.FollowForwarders) + TypeForwardedToData.OutputForwarders (writer, ass); + + var attributes = provider.CustomAttributes. + Where ((att) => !SkipAttribute (att)). + OrderBy ((a) => a.Constructor.DeclaringType.FullName, StringComparer.Ordinal); + + foreach (var att in attributes) { + string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType); + + writer.WriteStartElement ("attribute"); + 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 (); // properties + } } - - string value = o.ToString (); - if (attName.EndsWith ("GuidAttribute")) - value = value.ToUpper (); - AddAttribute (n, "value", value); + writer.WriteEndElement (); // attribute } - - natts.AppendChild (node); } + + writer.WriteEndElement (); // attributes } static Dictionary CreateAttributeMapping (CustomAttribute attribute) { - var mapping = new Dictionary (); + Dictionary mapping = null; - PopulateMapping (mapping, attribute); + PopulateMapping (ref mapping, attribute); var constructor = attribute.Constructor.Resolve (); - if (constructor == null || constructor.Parameters.Count == 0) + if (constructor == null || !constructor.HasParameters) return mapping; - PopulateMapping (mapping, constructor, attribute); + PopulateMapping (ref mapping, constructor, attribute); return mapping; } - static void PopulateMapping (Dictionary mapping, CustomAttribute attribute) + static void PopulateMapping (ref Dictionary mapping, CustomAttribute attribute) { + if (!attribute.HasProperties) + return; + foreach (var named_argument in attribute.Properties) { var name = named_argument.Name; var arg = named_argument.Argument; @@ -1088,13 +1213,15 @@ namespace CorCompare if (arg.Value is CustomAttributeArgument) arg = (CustomAttributeArgument) arg.Value; + if (mapping == null) + mapping = new Dictionary (StringComparer.Ordinal); mapping.Add (name, GetArgumentValue (arg.Type, arg.Value)); } } static Dictionary CreateArgumentFieldMapping (MethodDefinition constructor) { - Dictionary field_mapping = new Dictionary (); + Dictionary field_mapping = null; int? argument = null; @@ -1122,6 +1249,9 @@ namespace CorCompare if (!argument.HasValue) break; + if (field_mapping == null) + field_mapping = new Dictionary (); + if (!field_mapping.ContainsKey (field)) field_mapping.Add (field, (int) argument - 1); @@ -1135,7 +1265,7 @@ namespace CorCompare static Dictionary CreatePropertyFieldMapping (TypeDefinition type) { - Dictionary property_mapping = new Dictionary (); + Dictionary property_mapping = null; foreach (PropertyDefinition property in type.Properties) { if (property.GetMethod == null) @@ -1151,6 +1281,8 @@ namespace CorCompare if (field.DeclaringType.FullName != type.FullName) continue; + if (property_mapping == null) + property_mapping = new Dictionary (); property_mapping.Add (property, field); break; } @@ -1159,7 +1291,7 @@ namespace CorCompare return property_mapping; } - static void PopulateMapping (Dictionary mapping, MethodDefinition constructor, CustomAttribute attribute) + static void PopulateMapping (ref Dictionary mapping, MethodDefinition constructor, CustomAttribute attribute) { if (!constructor.HasBody) return; @@ -1172,6 +1304,8 @@ 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 (StringComparer.Ordinal); mapping.Add ("Value", dca.Value); return; case "System.ComponentModel.BindableAttribute": @@ -1179,6 +1313,8 @@ namespace CorCompare break; if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) { + if (mapping == null) + mapping = new Dictionary (StringComparer.Ordinal); mapping.Add ("Bindable", ca[0].Value); } else { throw new NotImplementedException (); @@ -1188,18 +1324,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 (StringComparer.Ordinal); + mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value)); + } + } } } @@ -1224,7 +1366,7 @@ namespace CorCompare if (!type.IsEnum) return false; - if (type.CustomAttributes.Count == 0) + if (!type.HasCustomAttributes) return false; foreach (CustomAttribute attribute in type.CustomAttributes) @@ -1307,16 +1449,15 @@ namespace CorCompare static bool SkipAttribute (CustomAttribute attribute) { - var type_name = Utils.CleanupTypeName (attribute.Constructor.DeclaringType); - - return !TypeHelper.IsPublic (attribute) - || type_name.EndsWith ("TODOAttribute"); + if (!TypeHelper.IsPublic (attribute)) + return true; + + return attribute.Constructor.DeclaringType.Name.EndsWith ("TODOAttribute", StringComparison.Ordinal); } - public static void OutputAttributes (XmlDocument doc, XmlNode parent, IList attributes) + public static void OutputAttributes (XmlWriter writer, params ICustomAttributeProvider[] providers) { - AttributeData ad = new AttributeData (doc, parent, attributes); - ad.DoOutput (); + AttributeData.DoOutput (writer, providers); } } @@ -1325,7 +1466,7 @@ namespace CorCompare public static string GetSignature (IList infos) { if (infos == null || infos.Count == 0) - return ""; + return string.Empty; var signature = new StringBuilder (); for (int i = 0; i < infos.Count; i++) { @@ -1343,8 +1484,10 @@ namespace CorCompare else modifier = string.Empty; - if (modifier.Length > 0) - signature.AppendFormat ("{0} ", modifier); + if (modifier.Length > 0) { + signature.Append (modifier); + signature.Append (" "); + } signature.Append (Utils.CleanupTypeName (info.ParameterType)); } @@ -1354,19 +1497,17 @@ namespace CorCompare } - class TypeReferenceComparer : IComparer + class TypeReferenceComparer : IComparer { public static TypeReferenceComparer Default = new TypeReferenceComparer (); - public int Compare (object a, object b) + public int Compare (TypeReference a, TypeReference b) { - TypeReference ta = (TypeReference) a; - TypeReference tb = (TypeReference) b; - int result = String.Compare (ta.Namespace, tb.Namespace); + int result = String.Compare (a.Namespace, b.Namespace, StringComparison.Ordinal); if (result != 0) return result; - return String.Compare (ta.Name, tb.Name); + return String.Compare (a.Name, b.Name, StringComparison.Ordinal); } } @@ -1378,7 +1519,30 @@ namespace CorCompare { MemberReference ma = (MemberReference) a; MemberReference mb = (MemberReference) b; - return String.Compare (ma.Name, mb.Name); + return String.Compare (ma.Name, mb.Name, StringComparison.Ordinal); + } + } + + class PropertyDefinitionComparer : IComparer + { + public static PropertyDefinitionComparer Default = new PropertyDefinitionComparer (); + + public int Compare (PropertyDefinition ma, PropertyDefinition mb) + { + int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal); + if (res != 0) + return res; + + if (!ma.HasParameters && !mb.HasParameters) + return 0; + + if (!ma.HasParameters) + return -1; + + if (!mb.HasParameters) + return 1; + + return MethodDefinitionComparer.Compare (ma.Parameters, mb.Parameters); } } @@ -1390,19 +1554,36 @@ namespace CorCompare { MethodDefinition ma = (MethodDefinition) a; MethodDefinition mb = (MethodDefinition) b; - int res = String.Compare (ma.Name, mb.Name); + int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal); if (res != 0) return res; - IList pia = ma.Parameters ; - IList pib = mb.Parameters; - res = pia.Count - pib.Count; + if (!ma.HasParameters && !mb.HasParameters) + return 0; + + if (!ma.HasParameters) + return -1; + + if (!mb.HasParameters) + return 1; + + res = Compare (ma.Parameters, mb.Parameters); + if (res != 0) + return res; + + // operators can differ by only return type + return string.CompareOrdinal (ma.ReturnType.FullName, mb.ReturnType.FullName); + } + + public static int Compare (IList pia, IList pib) + { + var res = pia.Count - pib.Count; if (res != 0) return res; string siga = Parameters.GetSignature (pia); string sigb = Parameters.GetSignature (pib); - return String.Compare (siga, sigb); + return String.Compare (siga, sigb, StringComparison.Ordinal); } } }