diff --git a/audit/AssemblyResolver.cs b/audit/AssemblyResolver.cs new file mode 100644 index 000000000..1a694fb08 --- /dev/null +++ b/audit/AssemblyResolver.cs @@ -0,0 +1,53 @@ +// +// AssemblyResolver.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// Copyright (C) 2007 Novell, Inc. +// +// 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.Collections; +using System.IO; + +using Mono.Cecil; + +namespace GuiCompare { + + public class AssemblyResolver : DefaultAssemblyResolver { + + public AssemblyDefinition ResolveFile (string file) + { + return ProcessFile (file); + } + + AssemblyDefinition ProcessFile (string file) + { + AddSearchDirectory (Path.GetDirectoryName (file)); + var assembly = AssemblyDefinition.ReadAssembly (file, new ReaderParameters { AssemblyResolver = this }); + RegisterAssembly (assembly); + + return assembly; + } + } +} diff --git a/audit/Util.cs b/audit/Util.cs new file mode 100644 index 000000000..5ed8d8fa1 --- /dev/null +++ b/audit/Util.cs @@ -0,0 +1,115 @@ +// +// Util.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// Copyright (C) 2008 Novell, Inc. +// +// 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.Collections.Generic; +using System.Text; +using Mono.Cecil; + +using GuiCompare; + +namespace Mono.AssemblyCompare { + + static class TypeHelper { + + public static AssemblyResolver Resolver = new AssemblyResolver (); + + internal static bool IsPublic (TypeReference typeref) + { + if (typeref == null) + throw new ArgumentNullException ("typeref"); + + TypeDefinition td = typeref.Resolve (); + if (td == null) + return false; + + return td.IsPublic; + } + + internal static bool IsDelegate (TypeReference typeref) + { + return IsDerivedFrom (typeref, "System.MulticastDelegate"); + } + + internal static bool IsDerivedFrom (TypeReference type, string derivedFrom) + { + bool first = true; + foreach (var def in WalkHierarchy (type)) { + if (first) { + first = false; + continue; + } + + if (def.FullName == derivedFrom) + return true; + } + + return false; + } + + internal static IEnumerable WalkHierarchy (TypeReference type) + { + for (var def = type.Resolve (); def != null; def = GetBaseType (def)) + yield return def; + } + + internal static IEnumerable GetInterfaces (TypeReference type) + { + var ifaces = new Dictionary (); + + foreach (var def in WalkHierarchy (type)) + foreach (TypeReference iface in def.Interfaces) + ifaces [iface.FullName] = iface; + + return ifaces.Values; + } + + internal static TypeDefinition GetBaseType (TypeDefinition child) + { + if (child.BaseType == null) + return null; + + return child.BaseType.Resolve (); + } + + internal static bool IsPublic (CustomAttribute att) + { + return IsPublic (att.AttributeType); + } + + internal static string GetFullName (CustomAttribute att) + { + return att.AttributeType.FullName; + } + + internal static TypeDefinition GetTypeDefinition (CustomAttribute att) + { + return att.AttributeType.Resolve (); + } + } +} diff --git a/audit/WellFormedXmlWriter.cs b/audit/WellFormedXmlWriter.cs new file mode 100644 index 000000000..e2a9989f3 --- /dev/null +++ b/audit/WellFormedXmlWriter.cs @@ -0,0 +1,294 @@ +// +// WellFormedXmlWriter.cs +// +// Author: +// Atsushi Enomoto +// +// Copyright (C) 2006 Novell, Inc. http://www.novell.com +// +using System; +using System.Globalization; +using System.Collections; +using System.Xml; + +namespace Mono.AssemblyCompare { + + public class WellFormedXmlWriter : DefaultXmlWriter + { + public static bool IsValid (int ch) + { + return !IsInvalid (ch); + } + + public static bool IsInvalid (int ch) + { + switch (ch) { + case 9: + case 10: + case 13: + return false; + } + if (ch < 32) + return true; + if (ch < 0xD800) + return false; + if (ch < 0xE000) + return true; + if (ch < 0xFFFE) + return false; + if (ch < 0x10000) + return true; + if (ch < 0x110000) + return false; + else + return true; + } + + public static int IndexOfInvalid (string s, bool allowSurrogate) + { + for (int i = 0; i < s.Length; i++) + if (IsInvalid (s [i])) { + if (!allowSurrogate || + i + 1 == s.Length || + s [i] < '\uD800' || + s [i] >= '\uDC00' || + s [i + 1] < '\uDC00' || + s [i + 1] >= '\uE000') + return i; + i++; + } + return -1; + } + + public static int IndexOfInvalid (char [] s, int start, int length, bool allowSurrogate) + { + int end = start + length; + if (s.Length < end) + throw new ArgumentOutOfRangeException ("length"); + for (int i = start; i < end; i++) + if (IsInvalid (s [i])) { + if (!allowSurrogate || + i + 1 == end || + s [i] < '\uD800' || + s [i] >= '\uDC00' || + s [i + 1] < '\uDC00' || + s [i + 1] >= '\uE000') + return i; + i++; + } + return -1; + } + + public WellFormedXmlWriter (XmlWriter writer) : base (writer) + { + } + + public override void WriteString (string text) + { + int i = IndexOfInvalid (text, true); + if (i >= 0) { + char [] arr = text.ToCharArray (); + Writer.WriteChars (arr, 0, i); + WriteChars (arr, i, arr.Length - i); + } else { + // no invalid character. + Writer.WriteString (text); + } + } + + public override void WriteChars (char [] text, int idx, int length) + { + int start = idx; + int end = idx + length; + while ((idx = IndexOfInvalid (text, start, length, true)) >= 0) { + if (start < idx) + Writer.WriteChars (text, start, idx - start); + Writer.WriteString (String.Format (CultureInfo.InvariantCulture, + text [idx] < 0x80 ? "\\x{0:X02}" : "\\u{0:X04}", + (int) text [idx])); + length -= idx - start + 1; + start = idx + 1; + } + if (start < end) + Writer.WriteChars (text, start, end - start); + } + + } + + public class DefaultXmlWriter : XmlWriter + { + XmlWriter writer; + + public DefaultXmlWriter (XmlWriter writer) + { + this.writer = writer; + } + + protected XmlWriter Writer { + get { return writer; } + } + + public override void Close () + { + writer.Close (); + } + + public override void Flush () + { + writer.Flush (); + } + + public override string LookupPrefix (string ns) + { + return writer.LookupPrefix (ns); + } + + public override void WriteBase64 (byte [] buffer, int index, int count) + { + writer.WriteBase64 (buffer, index, count); + } + + public override void WriteBinHex (byte [] buffer, int index, int count) + { + writer.WriteBinHex (buffer, index, count); + } + + public override void WriteCData (string text) + { + writer.WriteCData (text); + } + + public override void WriteCharEntity (char ch) + { + writer.WriteCharEntity (ch); + } + + public override void WriteChars (char [] buffer, int index, int count) + { + writer.WriteChars (buffer, index, count); + } + + public override void WriteComment (string text) + { + writer.WriteComment (text); + } + + public override void WriteDocType (string name, string pubid, string sysid, string subset) + { + writer.WriteDocType (name, pubid, sysid, subset); + } + + public override void WriteEndAttribute () + { + writer.WriteEndAttribute (); + } + + public override void WriteEndDocument () + { + writer.WriteEndDocument (); + } + + public override void WriteEndElement () + { + writer.WriteEndElement (); + } + + public override void WriteEntityRef (string name) + { + writer.WriteEntityRef (name); + } + + public override void WriteFullEndElement () + { + writer.WriteFullEndElement (); + } + + public override void WriteName (string name) + { + writer.WriteName (name); + } + + public override void WriteNmToken (string name) + { + writer.WriteNmToken (name); + } + + public override void WriteNode (XmlReader reader, bool defattr) + { + writer.WriteNode (reader, defattr); + } + + public override void WriteProcessingInstruction (string name, string text) + { + writer.WriteProcessingInstruction (name, text); + } + + public override void WriteQualifiedName (string localName, string ns) + { + writer.WriteQualifiedName (localName, ns); + } + + public override void WriteRaw (string data) + { + writer.WriteRaw (data); + } + + public override void WriteRaw (char [] buffer, int index, int count) + { + writer.WriteRaw (buffer, index, count); + } + + public override void WriteStartAttribute (string prefix, string localName, string ns) + { + writer.WriteStartAttribute (prefix, localName, ns); + } + + public override void WriteStartDocument (bool standalone) + { + writer.WriteStartDocument (standalone); + } + + public override void WriteStartDocument () + { + writer.WriteStartDocument (); + } + + public override void WriteStartElement (string prefix, string localName, string ns) + { + writer.WriteStartElement (prefix, localName, ns); + } + + public override void WriteString (string text) + { + writer.WriteString (text); + } + + public override void WriteSurrogateCharEntity (char lowChar, char highChar) + { + writer.WriteSurrogateCharEntity (lowChar, highChar); + } + + public override void WriteWhitespace (string ws) + { + writer.WriteWhitespace (ws); + } + + public override WriteState WriteState { + get { + return writer.WriteState; + } + } + + public override string XmlLang { + get { + return writer.XmlLang; + } + } + + public override XmlSpace XmlSpace { + get { + return writer.XmlSpace; + } + } + } +} \ No newline at end of file diff --git a/audit/get-apiinfo.pl b/audit/get-apiinfo.pl index 7154c48fb..b2549c0a7 100755 --- a/audit/get-apiinfo.pl +++ b/audit/get-apiinfo.pl @@ -25,15 +25,18 @@ die "Usage: get-apiinfo.pl " if (@ARGV != 2); $root = $ARGV[0]; $outdir = $ARGV[1]; +$cecildir = `pkg-config --variable=assemblies_dir mono-cecil`; +chomp ($cecildir); + `mkdir -p $outdir`; `mkdir -p apitmp`; `cp $root/*/*.dll apitmp`; -print "getting api info: "; +print "Getting api info: "; foreach $assm (`ls apitmp/*.dll`) { chomp ($assm); $assm =~ /apitmp\/(.*)\.dll/; print "*"; - `mono mono-api-info.exe $assm > $outdir/$1.apiinfo`; + `MONO_PATH=$cecildir mono mono-api-info.exe $assm > $outdir/$1.apiinfo`; } `rm -rf apitmp`; print " Completed\n\n"; diff --git a/audit/makefile b/audit/makefile index b33d8f916..ffaa3ce44 100644 --- a/audit/makefile +++ b/audit/makefile @@ -1,5 +1,12 @@ MCS=mcs +COMMON_SOURCES = \ + AssemblyResolver.cs \ + Util.cs \ + WellFormedXmlWriter.cs + +APIINFO_SOURCES = mono-api-info.cs $(COMMON_SOURCES) + all: extract-missing.exe mono-api-info.exe mono-api-diff.exe check: all @@ -11,8 +18,8 @@ check: all mono-api-diff.exe: mono-api-diff.cs $(MCS) mono-api-diff.cs -mono-api-info.exe: mono-api-info.cs - $(MCS) mono-api-info.cs +mono-api-info.exe: $(APIINFO_SOURCES) + $(MCS) `pkg-config --libs mono-cecil` -out:$@ $^ extract-missing.exe: extract-missing.cs $(MCS) extract-missing.cs diff --git a/audit/mono-api-diff.cs b/audit/mono-api-diff.cs index b4983c059..2581209c4 100644 --- a/audit/mono-api-diff.cs +++ b/audit/mono-api-diff.cs @@ -4,9 +4,10 @@ // // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Marek Safar (marek.safar@gmail.com) // // (C) 2003 Novell, Inc (http://www.novell.com) -// +// (C) 2009,2010 Collier Technologies (http://www.colliertech.org) using System; using System.Collections; @@ -21,12 +22,14 @@ namespace Mono.AssemblyCompare { static int Main (string [] args) { - if (args.Length != 2) + if (args.Length != 2) { + Console.WriteLine ("Usage: mono mono-api-diff.exe "); return 1; + } - XMLAssembly ms = CreateXMLAssembly (args [0]); - XMLAssembly mono = CreateXMLAssembly (args [1]); - XmlDocument doc = ms.CompareAndGetDocument (mono); + XMLAssembly asm_base = CreateXMLAssembly (args [0]); + XMLAssembly asm_curr = CreateXMLAssembly (args [1]); + XmlDocument doc = asm_base.CompareAndGetDocument (asm_curr); XmlTextWriter writer = new XmlTextWriter (Console.Out); writer.Formatting = Formatting.Indented; @@ -177,6 +180,27 @@ namespace Mono.AssemblyCompare return (object []) list.ToArray (type); } + public static bool IsMeaninglessAttribute (string s) + { + if (s == null) + return false; + if (s == "System.Runtime.CompilerServices.CompilerGeneratedAttribute") + return true; + return false; + } + + public static bool IsTODOAttribute (string s) + { + if (s == null) + return false; + if (s.EndsWith ("MonoDocumentationNoteAttribute") || + s.EndsWith ("MonoExtensionAttribute") || + s.EndsWith ("MonoLimitationAttribute") || + s.EndsWith ("MonoNotSupportedAttribute")) + return true; + return s.EndsWith ("TODOAttribute"); + } + protected void AddAttribute (XmlNode node, string name, string value) { XmlAttribute attr = document.CreateAttribute (name); @@ -266,14 +290,14 @@ namespace Mono.AssemblyCompare public bool HaveWarnings { get { return haveWarnings; } } - + public Counters Counters { get { return counters; } } - + public abstract void CompareTo (XmlDocument doc, XmlNode parent, object other); } - + abstract class XMLNameGroup : XMLData { protected XmlNode group; @@ -448,19 +472,17 @@ namespace Mono.AssemblyCompare newNS.Add (node); AddAttribute (node, "name", xns.Name); - if (oh.ContainsKey (xns.Name)) { - int idx = (int) oh [xns.Name]; - xns.CompareTo (document, node, other [idx]); + int idx = -1; + if (oh.ContainsKey (xns.Name)) + idx = (int) oh [xns.Name]; + XMLNamespace ons = idx >= 0 ? (XMLNamespace) other [idx] : null; + xns.CompareTo (document, node, ons); + if (idx >= 0) other [idx] = null; - xns.AddCountersAttributes (node); - counters.Present++; - counters.PresentTotal++; - counters.AddPartialToTotal (xns.Counters); - } else { - AddAttribute (node, "presence", "missing"); - counters.Missing++; - counters.MissingTotal++; - } + xns.AddCountersAttributes (node); + counters.Present++; + counters.PresentTotal++; + counters.AddPartialToTotal (xns.Counters); } if (other != null) { @@ -503,7 +525,7 @@ namespace Mono.AssemblyCompare this.document = doc; XmlNode parent = doc.CreateElement ("assemblies", null); doc.AppendChild (parent); - + CompareTo (doc, parent, other); XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null); @@ -547,7 +569,7 @@ namespace Mono.AssemblyCompare XmlNode childA = doc.CreateElement ("classes", null); parent.AppendChild (childA); - CompareTypes (childA, nspace.types); + CompareTypes (childA, nspace != null ? nspace.types : new XMLClass [0]); } void CompareTypes (XmlNode parent, XMLClass [] other) @@ -564,23 +586,20 @@ namespace Mono.AssemblyCompare AddAttribute (node, "name", xclass.Name); AddAttribute (node, "type", xclass.Type); - if (oh.ContainsKey (xclass.Name)) { - int idx = (int) oh [xclass.Name]; - xclass.CompareTo (document, node, other [idx]); + int idx = -1; + if (oh.ContainsKey (xclass.Name)) + idx = (int) oh [xclass.Name]; + xclass.CompareTo (document, node, idx >= 0 ? other [idx] : new XMLClass ()); + if (idx >= 0) other [idx] = null; - counters.AddPartialToPartial (xclass.Counters); - } else { - AddAttribute (node, "presence", "missing"); - counters.Missing++; - counters.MissingTotal++; - } + counters.AddPartialToPartial (xclass.Counters); } if (other != null) { count = other.Length; for (int i = 0; i < count; i++) { XMLClass c = other [i]; - if (c == null || c.Name.EndsWith ("TODOAttribute")) + if (c == null || IsTODOAttribute (c.Name)) continue; node = document.CreateElement ("class", null); @@ -636,7 +655,7 @@ namespace Mono.AssemblyCompare XMLEvents events; XMLMethods methods; XMLClass [] nested; - + public override void LoadData (XmlNode node) { if (node == null) @@ -670,7 +689,7 @@ namespace Mono.AssemblyCompare // Console.Error.WriteLine ("Empty class {0} {1}", name, type); return; } - + if (child.Name == "attributes") { attributes = new XMLAttributes (); attributes.LoadData (child); @@ -719,6 +738,12 @@ namespace Mono.AssemblyCompare child = child.NextSibling; } + if (child != null && child.Name == "generic-parameters") { + // HACK: ignore this tag as it doesn't seem to + // add any value when checking for differences + return; + } + if (child == null) return; @@ -752,28 +777,28 @@ namespace Mono.AssemblyCompare } if (type != oclass.type) - AddWarning (parent, "Class type is wrong: {0} != {1}", type, oclass.type); + AddWarning (parent, "Class type is different: {0} != {1}", type, oclass.type); if (baseName != oclass.baseName) - AddWarning (parent, "Base class is wrong: {0} != {1}", baseName, oclass.baseName); + AddWarning (parent, "Base class is different: {0} != {1}", baseName, oclass.baseName); if (isAbstract != oclass.isAbstract || isSealed != oclass.isSealed) { if ((isAbstract && isSealed) || (oclass.isAbstract && oclass.isSealed)) - AddWarning (parent, "Should {0}be static", (isAbstract && isSealed) ? "" : "not "); + AddWarning (parent, "Was {0}static", (isAbstract && isSealed) ? "" : "not "); else if (isAbstract != oclass.isAbstract) - AddWarning (parent, "Should {0}be abstract", isAbstract ? "" : "not "); + AddWarning (parent, "Was {0}abstract", isAbstract ? "" : "not "); else if (isSealed != oclass.isSealed) - AddWarning (parent, "Should {0}be sealed", isSealed ? "" : "not "); + AddWarning (parent, "Was {0}sealed", isSealed ? "" : "not "); } if (isSerializable != oclass.isSerializable) - AddWarning (parent, "Should {0}be serializable", isSerializable ? "" : "not "); + AddWarning (parent, "Was {0}serializable", isSerializable ? "" : "not "); if (charSet != oclass.charSet) - AddWarning (parent, "CharSet is wrong: {0} != {1}", charSet, oclass.charSet); + AddWarning (parent, "CharSet is different: {0} != {1}", charSet, oclass.charSet); if (layout != oclass.layout) - AddWarning (parent, "Layout is wrong: {0} != {1}", layout, oclass.layout); + AddWarning (parent, "Layout is different: {0} != {1}", layout, oclass.layout); if (interfaces != null || oclass.interfaces != null) { if (interfaces == null) @@ -849,7 +874,7 @@ namespace Mono.AssemblyCompare for (int i = 0; i < count; i++) { XMLClass xclass = nested [i]; - node = document.CreateElement ("nestedclass", null); + node = document.CreateElement ("class", null); newNodes.Add (node); AddAttribute (node, "name", xclass.Name); AddAttribute (node, "type", xclass.Type); @@ -871,12 +896,13 @@ namespace Mono.AssemblyCompare count = other.Length; for (int i = 0; i < count; i++) { XMLClass c = other [i]; - if (c == null || c.Name.EndsWith ("TODOAttribute")) + if (c == null || IsTODOAttribute (c.Name)) continue; - node = document.CreateElement ("nestedclass", null); + node = document.CreateElement ("class", null); newNodes.Add (node); AddAttribute (node, "name", c.Name); + AddAttribute (node, "type", c.Type); AddExtra (node); counters.Extra++; counters.ExtraTotal++; @@ -920,6 +946,7 @@ namespace Mono.AssemblyCompare bool isUnsafe; bool isOptional; string defaultValue; + XMLAttributes attributes; public override void LoadData (XmlNode node) { @@ -940,6 +967,16 @@ namespace Mono.AssemblyCompare isOptional = bool.Parse (node.Attributes["optional"].Value); if (node.Attributes["defaultValue"] != null) defaultValue = node.Attributes["defaultValue"].Value; + + XmlNode child = node.FirstChild; + if (child == null) + return; + + if (child.Name == "attributes") { + attributes = new XMLAttributes (); + attributes.LoadData (child); + child = child.NextSibling; + } } public override void CompareTo (XmlDocument doc, XmlNode parent, object other) @@ -948,23 +985,42 @@ namespace Mono.AssemblyCompare XMLParameter oparm = (XMLParameter) other; + if (name != oparm.name) + AddWarning (parent, "Parameter name is different: {0} != {1}", name, oparm.name); + if (type != oparm.type) - AddWarning (parent, "Parameter type is wrong: {0} != {1}", type, oparm.type); - + AddWarning (parent, "Parameter type is different: {0} != {1}", type, oparm.type); + if (attrib != oparm.attrib) - AddWarning (parent, "Parameter attributes wrong: {0} != {1}", attrib, oparm.attrib); + AddWarning (parent, "Parameter attributes different: {0} != {1}", attrib, oparm.attrib); if (direction != oparm.direction) - AddWarning (parent, "Parameter direction wrong: {0} != {1}", direction, oparm.direction); + AddWarning (parent, "Parameter direction different: {0} != {1}", direction, oparm.direction); if (isUnsafe != oparm.isUnsafe) - AddWarning (parent, "Parameter unsafe wrong: {0} != {1}", isUnsafe, oparm.isUnsafe); + AddWarning (parent, "Parameter unsafe different: {0} != {1}", isUnsafe, oparm.isUnsafe); if (isOptional != oparm.isOptional) - AddWarning (parent, "Parameter optional wrong: {0} != {1}", isOptional, oparm.isOptional); + AddWarning (parent, "Parameter optional different: {0} != {1}", isOptional, oparm.isOptional); if (defaultValue != oparm.defaultValue) - AddWarning (parent, "Parameter default value wrong: {0} != {1}", (defaultValue == null) ? "(no default value)" : defaultValue, (oparm.defaultValue == null) ? "(no default value)" : oparm.defaultValue); + AddWarning (parent, "Parameter default value different: {0} != {1}", (defaultValue == null) ? "(no default value)" : defaultValue, (oparm.defaultValue == null) ? "(no default value)" : oparm.defaultValue); + + if (attributes != null || oparm.attributes != null) { + if (attributes == null) + attributes = new XMLAttributes (); + + attributes.CompareTo (doc, parent, oparm.attributes); + counters.AddPartialToPartial (attributes.Counters); + if (oparm.attributes != null && oparm.attributes.IsTodo) { + counters.Todo++; + counters.TodoTotal++; + counters.ErrorTotal++; + AddAttribute (parent, "error", "todo"); + if (oparm.attributes.Comment != null) + AddAttribute (parent, "comment", oparm.attributes.Comment); + } + } } public string Name { @@ -1033,14 +1089,14 @@ namespace Mono.AssemblyCompare if (de.Value == null) { if (other_value != null) - AddWarning (parent, "Property '{0}' is 'null' and should be '{1}'", de.Key, other_value); + AddWarning (parent, "Property '{0}' is 'null' and was '{1}'", de.Key, other_value); continue; } if (de.Value.Equals (other_value)) continue; - AddWarning (parent, "Property '{0}' is '{1}' and should be '{2}'", + AddWarning (parent, "Property '{0}' is '{1}' and was '{2}'", de.Key, de.Value, other_value == null ? "null" : other_value); } } @@ -1067,17 +1123,17 @@ namespace Mono.AssemblyCompare protected override bool CheckIfAdd (string value, XmlNode node) { - if (value.EndsWith ("TODOAttribute")) { + if (IsTODOAttribute (value)) { isTodo = true; XmlNode pNode = node.SelectSingleNode ("properties"); - if (pNode.ChildNodes [0].Attributes ["value"] != null) { + if (pNode != null && pNode.ChildNodes.Count > 0 && pNode.ChildNodes [0].Attributes ["value"] != null) { comment = pNode.ChildNodes [0].Attributes ["value"].Value; } return false; } - return true; + return !IsMeaninglessAttribute (value); } protected override void CompareToInner (string name, XmlNode node, XMLNameGroup other) @@ -1095,7 +1151,7 @@ namespace Mono.AssemblyCompare { string key = null; - // if multiple attributes with the same name (type) exist, then we + // if multiple attributes with the same name (type) exist, then we // cannot be sure which attributes correspond, so we must use the // name of the attribute (type) and the name/value of its properties // as key @@ -1114,7 +1170,7 @@ namespace Mono.AssemblyCompare } } - // sort properties by name, as order of properties in XML is + // sort properties by name, as order of properties in XML is // undefined keyParts.Sort (); @@ -1138,7 +1194,7 @@ namespace Mono.AssemblyCompare { XmlNode pNode = node.SelectSingleNode ("properties"); - if (name.EndsWith ("TODOAttribute")) { + if (IsTODOAttribute (name)) { isTodo = true; if (pNode.ChildNodes [0].Attributes ["value"] != null) { comment = pNode.ChildNodes [0].Attributes ["value"].Value; @@ -1197,7 +1253,7 @@ namespace Mono.AssemblyCompare XMLGenericGroup g = (XMLGenericGroup) other; if (attributes != g.attributes) - AddWarning (parent, "Incorrect generic attributes: '{0}' != '{1}'", attributes, g.attributes); + AddWarning (parent, "Different generic attributes: '{0}' != '{1}'", attributes, g.attributes); } } @@ -1233,7 +1289,7 @@ namespace Mono.AssemblyCompare XmlAttribute xatt = node.Attributes ["attrib"]; if (xatt != null) access [name] = xatt.Value; - + XmlNode orig = node; node = node.FirstChild; @@ -1295,7 +1351,7 @@ namespace Mono.AssemblyCompare oaccName = ConvertToString (Int32.Parse (oacc)); if (accName != oaccName) - AddWarning (parent, "Incorrect attributes: '{0}' != '{1}'", accName, oaccName); + AddWarning (parent, "Different attributes: '{0}' != '{1}'", accName, oaccName); } protected virtual string ConvertToString (int att) @@ -1303,7 +1359,7 @@ namespace Mono.AssemblyCompare return null; } } - + class XMLFields : XMLMember { Hashtable fieldTypes; @@ -1341,7 +1397,7 @@ namespace Mono.AssemblyCompare oftype = fields.fieldTypes [name] as string; if (ftype != oftype) - AddWarning (parent, "Field type is {0} and should be {1}", oftype, ftype); + AddWarning (parent, "Field type is {0} and was {1}", oftype, ftype); } if (fieldValues != null) { string fvalue = fieldValues [name] as string; @@ -1350,7 +1406,7 @@ namespace Mono.AssemblyCompare ofvalue = fields.fieldValues [name] as string; if (fvalue != ofvalue) - AddWarning (parent, "Field value is {0} and should be {1}", ofvalue, fvalue); + AddWarning (parent, "Field value is {0} and was {1}", ofvalue, fvalue); } } @@ -1506,9 +1562,11 @@ namespace Mono.AssemblyCompare public override string GetNodeKey (string name, XmlNode node) { XmlAttributeCollection atts = node.Attributes; - return String.Format ("{0}:{1}:{2}", atts ["name"].Value, - atts ["ptype"].Value, - atts ["params"].Value); + return String.Format ("{0}:{1}:{2}", + (atts["name"] != null ? atts["name"].Value : ""), + (atts["ptype"] != null ? atts["ptype"].Value : ""), + (atts["params"] != null ? atts["params"].Value : "") + ); } public override string GroupName { @@ -1523,6 +1581,7 @@ namespace Mono.AssemblyCompare class XMLEvents : XMLMember { Hashtable eventTypes; + Hashtable nameToMethod = new Hashtable (); protected override void LoadExtraData (string name, XmlNode node) { @@ -1534,6 +1593,19 @@ namespace Mono.AssemblyCompare eventTypes [name] = xatt.Value; } + XmlNode child = node.FirstChild; + while (child != null) { + if (child != null && child.Name == "methods") { + XMLMethods m = new XMLMethods (); + XmlNode parent = child.ParentNode; + string key = GetNodeKey (name, parent); + m.LoadData (child); + nameToMethod [key] = m; + break; + } + child = child.NextSibling; + } + base.LoadExtraData (name, node); } @@ -1555,7 +1627,17 @@ namespace Mono.AssemblyCompare oetype = evt.eventTypes [name] as string; if (etype != oetype) - AddWarning (parent, "Event type is {0} and should be {1}", oetype, etype); + AddWarning (parent, "Event type is {0} and was {1}", oetype, etype); + + XMLMethods m = nameToMethod [name] as XMLMethods; + XMLMethods om = evt.nameToMethod [name] as XMLMethods; + if (m != null || om != null) { + if (m == null) + m = new XMLMethods (); + + m.CompareTo (document, parent, om); + counters.AddPartialToPartial (m.Counters); + } } finally { AddCountersAttributes (parent); copy.AddPartialToPartial (counters); @@ -1591,7 +1673,8 @@ namespace Mono.AssemblyCompare None = 0, Abstract = 1, Virtual = 2, - Static = 4 + Static = 4, + Final = 8, } protected override void LoadExtraData (string name, XmlNode node) @@ -1611,6 +1694,8 @@ namespace Mono.AssemblyCompare flags |= SignatureFlags.Static; if (((XmlElement) node).GetAttribute ("virtual") == "true") flags |= SignatureFlags.Virtual; + if (((XmlElement) node).GetAttribute ("final") == "true") + flags |= SignatureFlags.Final; if (flags != SignatureFlags.None) { if (signatureFlags == null) signatureFlags = new Hashtable (); @@ -1640,6 +1725,23 @@ namespace Mono.AssemblyCompare base.LoadExtraData (name, node); } + public override string GetNodeKey (string name, XmlNode node) + { + // for explicit/implicit operators we need to include the return + // type in the key to allow matching; as a side-effect, differences + // in return types will be reported as extra/missing methods + // + // for regular methods we do not need to take into account the + // return type for matching methods; differences in return types + // will be reported as a warning on the method + if (name.StartsWith ("op_")) { + XmlAttribute xatt = node.Attributes ["returntype"]; + string returnType = xatt != null ? xatt.Value + " " : string.Empty; + return returnType + name; + } + return name; + } + protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other) { // create backup of actual counters @@ -1662,11 +1764,11 @@ namespace Mono.AssemblyCompare if (flags!= oflags) { if (flags == SignatureFlags.None) - AddWarning (parent, String.Format ("should not be {0}", oflags)); + AddWarning (parent, String.Format ("was not {0}", oflags)); else if (oflags == SignatureFlags.None) - AddWarning (parent, String.Format ("should be {0}", flags)); + AddWarning (parent, String.Format ("was {0}", flags)); else - AddWarning (parent, String.Format ("{0} and should be {1}", oflags, flags)); + AddWarning (parent, String.Format ("{0} and was {1}", oflags, flags)); } if (returnTypes != null) { @@ -1676,7 +1778,7 @@ namespace Mono.AssemblyCompare ortype = methods.returnTypes[name] as string; if (rtype != ortype) - AddWarning (parent, "Return type is {0} and should be {1}", ortype, rtype); + AddWarning (parent, "Return type is {0} and was {1}", ortype, rtype); } if (parameters != null) { @@ -1712,7 +1814,7 @@ namespace Mono.AssemblyCompare if ((ma & MethodAttributes.RequireSecObject) != 0) ma = (MethodAttributes) (att - (int) MethodAttributes.RequireSecObject); - // we don't care if the implementation is forwarded through PInvoke + // we don't care if the implementation is forwarded through PInvoke if ((ma & MethodAttributes.PinvokeImpl) != 0) ma = (MethodAttributes) (att - (int) MethodAttributes.PinvokeImpl); diff --git a/audit/mono-api-info.cs b/audit/mono-api-info.cs index 37b3a63da..791c2c57f 100644 --- a/audit/mono-api-info.cs +++ b/audit/mono-api-info.cs @@ -4,61 +4,90 @@ // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) // -// Copyright (C) 2003-2005 Novell, Inc (http://www.novell.com) +// Copyright (C) 2003-2008 Novell, Inc (http://www.novell.com) // using System; using System.Collections; +using System.Collections.Generic; using System.Globalization; -using System.Reflection; +using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Text; using System.Xml; -namespace Mono.AssemblyInfo +using Mono.Cecil; +using Mono.Cecil.Cil; +using System.IO; + +namespace Mono.AssemblyCompare { - class Driver + public class Driver { - static int Main (string [] args) + public static int Main (string [] args) { if (args.Length == 0) return 1; + AbiMode = false; + AssemblyCollection acoll = new AssemblyCollection (); - - foreach (string fullName in args) { - acoll.Add (fullName); + + foreach (string arg in args) { + if (arg == "--abi") { + AbiMode = true; + } else { + acoll.Add (arg); + + TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg)); + } } XmlDocument doc = new XmlDocument (); acoll.Document = doc; acoll.DoOutput (); - XmlTextWriter writer = new XmlTextWriter (Console.Out); - writer.Formatting = Formatting.Indented; - XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null); + 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); return 0; } + + internal static bool AbiMode { get; private set; } + } + + public class Utils { + + public static string CleanupTypeName (TypeReference type) + { + return CleanupTypeName (type.FullName); + } + + public static string CleanupTypeName (string t) + { + return t.Replace ('<', '[').Replace ('>', ']').Replace ('/', '+'); + } } class AssemblyCollection { XmlDocument document; - ArrayList assemblies; + List assemblies = new List (); public AssemblyCollection () { - assemblies = new ArrayList (); } public bool Add (string name) { - Assembly ass = LoadAssembly (name); - if (ass == null) + AssemblyDefinition ass = LoadAssembly (name); + if (ass == null) { + Console.Error.WriteLine ("Cannot load assembly file " + name); return false; + } assemblies.Add (ass); return true; @@ -71,7 +100,7 @@ namespace Mono.AssemblyInfo XmlNode nassemblies = document.CreateElement ("assemblies", null); document.AppendChild (nassemblies); - foreach (Assembly a in assemblies) { + foreach (AssemblyDefinition a in assemblies) { AssemblyData data = new AssemblyData (document, nassemblies, a); data.DoOutput (); } @@ -80,24 +109,18 @@ namespace Mono.AssemblyInfo public XmlDocument Document { set { document = value; } } - - static Assembly LoadAssembly (string aname) + + AssemblyDefinition LoadAssembly (string assembly) { - Assembly ass = null; try { - string name = aname; - if (!name.EndsWith (".dll")) - name += ".dll"; - ass = Assembly.LoadFrom (name); - return ass; - } catch { } + if (File.Exists (assembly)) + return TypeHelper.Resolver.ResolveFile (assembly); - try { - ass = Assembly.LoadWithPartialName (aname); - return ass; - } catch { } - - return null; + return TypeHelper.Resolver.Resolve (assembly); + } catch (Exception e) { + Console.WriteLine (e); + return null; + } } } @@ -122,11 +145,51 @@ namespace Mono.AssemblyInfo } } + class TypeForwardedToData : BaseData + { + AssemblyDefinition ass; + + public TypeForwardedToData (XmlDocument document, XmlNode parent, AssemblyDefinition ass) + : base (document, parent) + { + 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); + } + } + + public static void OutputForwarders (XmlDocument document, XmlNode parent, AssemblyDefinition ass) + { + TypeForwardedToData tftd = new TypeForwardedToData (document, parent, ass); + tftd.DoOutput (); + } + } + class AssemblyData : BaseData { - Assembly ass; - - public AssemblyData (XmlDocument document, XmlNode parent, Assembly ass) + AssemblyDefinition ass; + + public AssemblyData (XmlDocument document, XmlNode parent, AssemblyDefinition ass) : base (document, parent) { this.ass = ass; @@ -138,46 +201,46 @@ namespace Mono.AssemblyInfo throw new InvalidOperationException ("Document not set"); XmlNode nassembly = document.CreateElement ("assembly", null); - AssemblyName aname = ass.GetName (); + AssemblyNameDefinition aname = ass.Name; AddAttribute (nassembly, "name", aname.Name); AddAttribute (nassembly, "version", aname.Version.ToString ()); parent.AppendChild (nassembly); - AttributeData.OutputAttributes (document, nassembly, ass.GetCustomAttributes (false)); - Type [] types = ass.GetExportedTypes (); - if (types == null || types.Length == 0) + TypeForwardedToData.OutputForwarders (document, nassembly, ass); + AttributeData.OutputAttributes (document, nassembly, ass.CustomAttributes); + var typesCollection = ass.MainModule.Types; + if (typesCollection == null || typesCollection.Count == 0) return; - - Array.Sort (types, TypeComparer.Default); + 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); - string currentNS = "$%&$&"; + string current_namespace = "$%&$&"; XmlNode ns = null; XmlNode classes = null; - foreach (Type t in types) { - if (t.Namespace == null || t.Namespace == "") + foreach (TypeDefinition t in typesArray) { + if (string.IsNullOrEmpty (t.Namespace)) continue; - if (t.IsNotPublic) - continue; - - if (t.IsNestedPublic || t.IsNestedAssembly || t.IsNestedFamANDAssem || - t.IsNestedFamORAssem || t.IsNestedPrivate) + if (!Driver.AbiMode && ((t.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public)) continue; if (t.DeclaringType != null) continue; // enforce !nested - - if (t.Namespace != currentNS) { - currentNS = t.Namespace; + + if (t.Namespace != current_namespace) { + current_namespace = t.Namespace; ns = document.CreateElement ("namespace", null); - AddAttribute (ns, "name", currentNS); + AddAttribute (ns, "name", current_namespace); nss.AppendChild (ns); classes = document.CreateElement ("classes", null); ns.AppendChild (classes); } - + TypeData bd = new TypeData (document, classes, t); bd.DoOutput (); } @@ -186,9 +249,9 @@ namespace Mono.AssemblyInfo abstract class MemberData : BaseData { - MemberInfo [] members; + MemberReference [] members; - public MemberData (XmlDocument document, XmlNode parent, MemberInfo [] members) + public MemberData (XmlDocument document, XmlNode parent, MemberReference [] members) : base (document, parent) { this.members = members; @@ -199,30 +262,32 @@ namespace Mono.AssemblyInfo XmlNode mclass = document.CreateElement (ParentTag, null); parent.AppendChild (mclass); - foreach (MemberInfo member in members) { + foreach (MemberReference member in members) { XmlNode mnode = document.CreateElement (Tag, null); mclass.AppendChild (mnode); AddAttribute (mnode, "name", GetName (member)); if (!NoMemberAttributes) AddAttribute (mnode, "attrib", GetMemberAttributes (member)); - AttributeData.OutputAttributes (document, mnode, - member.GetCustomAttributes (false)); + AttributeData.OutputAttributes (document, mnode, GetCustomAttributes (member)); AddExtraData (mnode, member); } } - protected virtual void AddExtraData (XmlNode p, MemberInfo member) + + protected abstract IList GetCustomAttributes (MemberReference member); + + protected virtual void AddExtraData (XmlNode p, MemberReference memberDefenition) { } - protected virtual string GetName (MemberInfo member) + protected virtual string GetName (MemberReference memberDefenition) { return "NoNAME"; } - protected virtual string GetMemberAttributes (MemberInfo member) + protected virtual string GetMemberAttributes (MemberReference memberDefenition) { return null; } @@ -235,25 +300,61 @@ namespace Mono.AssemblyInfo public virtual string ParentTag { get { return "NoPARENTTAG"; } } - + public virtual string Tag { get { return "NoTAG"; } } + + public static void OutputGenericParameters (XmlDocument document, XmlNode nclass, IGenericParameterProvider provider) + { + if (provider.GenericParameters.Count == 0) + return; + + var gparameters = provider.GenericParameters; + + XmlElement ngeneric = document.CreateElement (string.Format ("generic-parameters")); + nclass.AppendChild (ngeneric); + + 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); + + ngeneric.AppendChild (nparam); + + var constraints = gp.Constraints; + if (constraints.Count == 0) + continue; + + XmlElement nconstraint = document.CreateElement ("generic-parameter-constraints"); + + foreach (TypeReference constraint in constraints) { + XmlElement ncons = document.CreateElement ("generic-parameter-constraint"); + ncons.SetAttribute ("name", Utils.CleanupTypeName (constraint)); + nconstraint.AppendChild (ncons); + } + + nparam.AppendChild (nconstraint); + } + } } class TypeData : MemberData { - Type type; - const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | - BindingFlags.Instance | BindingFlags.DeclaredOnly | - BindingFlags.NonPublic; - - public TypeData (XmlDocument document, XmlNode parent, Type type) + TypeDefinition type; + + public TypeData (XmlDocument document, XmlNode parent, TypeDefinition type) : base (document, parent, null) { this.type = type; } + protected override IList GetCustomAttributes (MemberReference member) { + return ((TypeDefinition) member).CustomAttributes; + } + public override void DoOutput () { if (document == null) @@ -265,7 +366,7 @@ namespace Mono.AssemblyInfo AddAttribute (nclass, "type", classType); if (type.BaseType != null) - AddAttribute (nclass, "base", type.BaseType.ToString ()); + AddAttribute (nclass, "base", Utils.CleanupTypeName (type.BaseType)); if (type.IsSealed) AddAttribute (nclass, "sealed", "true"); @@ -283,116 +384,136 @@ namespace Mono.AssemblyInfo if (layout != null) AddAttribute (nclass, "layout", layout); + if (type.PackingSize >= 0) { + AddAttribute (nclass, "pack", type.PackingSize.ToString ()); + } + + if (type.ClassSize >= 0) { + AddAttribute (nclass, "size", type.ClassSize.ToString ()); + } + parent.AppendChild (nclass); - - AttributeData.OutputAttributes (document, nclass, type.GetCustomAttributes (false)); - Type [] interfaces = type.GetInterfaces (); - if (interfaces != null && interfaces.Length > 0) { - XmlNode ifaces = document.CreateElement ("interfaces", null); - nclass.AppendChild (ifaces); - foreach (Type t in interfaces) { - if (!t.IsPublic) { - // we're only interested in public interfaces - continue; - } - XmlNode iface = document.CreateElement ("interface", null); - AddAttribute (iface, "name", t.ToString ()); - ifaces.AppendChild (iface); - } - } + AttributeData.OutputAttributes (document, nclass, GetCustomAttributes(type)); -#if NET_2_0 - // Generic constraints - Type [] gargs = type.GetGenericArguments (); - XmlElement ngeneric = (gargs.Length == 0) ? null : - document.CreateElement ("generic-type-constraints"); - foreach (Type garg in gargs) { - Type [] csts = garg.GetGenericParameterConstraints (); - if (csts.Length == 0 || csts [0] == typeof (object)) + XmlNode ifaces = null; + + foreach (TypeReference iface in TypeHelper.GetInterfaces (type)) { + if (!TypeHelper.IsPublic (iface)) + // we're only interested in public interfaces continue; - XmlElement el = document.CreateElement ("generic-type-constraint"); - el.SetAttribute ("name", garg.ToString ()); - el.SetAttribute ("generic-attribute", - garg.GenericParameterAttributes.ToString ()); - ngeneric.AppendChild (el); - foreach (Type ct in csts) { - XmlElement cel = document.CreateElement ("type"); - cel.AppendChild (document.CreateTextNode (ct.FullName)); - el.AppendChild (cel); + + if (ifaces == null) { + ifaces = document.CreateElement ("interfaces", null); + nclass.AppendChild (ifaces); } + + XmlNode iface_node = document.CreateElement ("interface", null); + AddAttribute (iface_node, "name", Utils.CleanupTypeName (iface)); + ifaces.AppendChild (iface_node); } - if (ngeneric != null && ngeneric.FirstChild != null) - nclass.AppendChild (ngeneric); -#endif + + MemberData.OutputGenericParameters (document, nclass, type); ArrayList members = new ArrayList (); - FieldInfo[] fields = GetFields (type); + FieldDefinition [] fields = GetFields (type); if (fields.Length > 0) { - Array.Sort (fields, MemberInfoComparer.Default); + Array.Sort (fields, MemberReferenceComparer.Default); FieldData fd = new FieldData (document, nclass, fields); - // Special case for enum fields - if (classType == "enum") { - string etype = fields [0].GetType ().ToString (); - AddAttribute (nclass, "enumtype", etype); - } members.Add (fd); } - ConstructorInfo [] ctors = GetConstructors (type); - if (ctors.Length > 0) { - Array.Sort (ctors, MemberInfoComparer.Default); - members.Add (new ConstructorData (document, nclass, ctors)); + if (type.IsEnum) { + var value_type = GetEnumValueField (type); + if (value_type == null) + throw new NotSupportedException (); + + AddAttribute (nclass, "enumtype", Utils.CleanupTypeName (value_type.FieldType)); } - PropertyInfo[] properties = GetProperties (type); - if (properties.Length > 0) { - Array.Sort (properties, MemberInfoComparer.Default); - members.Add (new PropertyData (document, nclass, properties)); - } + if (!Driver.AbiMode) { - EventInfo [] events = GetEvents (type); - if (events.Length > 0) { - Array.Sort (events, MemberInfoComparer.Default); - members.Add (new EventData (document, nclass, events)); - } + MethodDefinition [] ctors = GetConstructors (type); + if (ctors.Length > 0) { + Array.Sort (ctors, MemberReferenceComparer.Default); + members.Add (new ConstructorData (document, nclass, ctors)); + } - MethodInfo [] methods = GetMethods (type); - if (methods.Length > 0) { - Array.Sort (methods, MemberInfoComparer.Default); - members.Add (new MethodData (document, nclass, methods)); + PropertyDefinition[] properties = GetProperties (type); + if (properties.Length > 0) { + Array.Sort (properties, MemberReferenceComparer.Default); + members.Add (new PropertyData (document, nclass, properties)); + } + + EventDefinition [] events = GetEvents (type); + if (events.Length > 0) { + Array.Sort (events, MemberReferenceComparer.Default); + members.Add (new EventData (document, nclass, events)); + } + + MethodDefinition [] methods = GetMethods (type); + if (methods.Length > 0) { + Array.Sort (methods, MemberReferenceComparer.Default); + members.Add (new MethodData (document, nclass, methods)); + } } foreach (MemberData md in members) md.DoOutput (); - Type [] nested = type.GetNestedTypes (); - if (nested != null && nested.Length > 0) { + var nested = type.NestedTypes; + //remove non public(familiy) and nested in second degree + for (int i = nested.Count - 1; i >= 0; i--) { + TypeDefinition t = nested [i]; + if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic || + (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily || + (t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) { + // public + if (t.DeclaringType == type) + continue; // not nested of nested + } + + nested.RemoveAt (i); + } + + + if (nested.Count > 0) { XmlNode classes = document.CreateElement ("classes", null); nclass.AppendChild (classes); - foreach (Type t in nested) { + foreach (TypeDefinition t in nested) { TypeData td = new TypeData (document, classes, t); td.DoOutput (); } } } - protected override string GetMemberAttributes (MemberInfo member) + static FieldReference GetEnumValueField (TypeDefinition type) + { + foreach (FieldDefinition field in type.Fields) + if (field.IsSpecialName && field.Name == "value__") + return field; + + return null; + } + + protected override string GetMemberAttributes (MemberReference member) { if (member != type) throw new InvalidOperationException ("odd"); - + return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture); } - public static bool MustDocumentMethod(MethodBase method) - { + public static bool MustDocumentMethod (MethodDefinition method) { // All other methods - return (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly); + MethodAttributes maskedAccess = method.Attributes & MethodAttributes.MemberAccessMask; + return maskedAccess == MethodAttributes.Public + || maskedAccess == MethodAttributes.Family + || maskedAccess == MethodAttributes.FamORAssem; } - static string GetClassType (Type t) + static string GetClassType (TypeDefinition t) { if (t.IsEnum) return "enum"; @@ -403,181 +524,208 @@ namespace Mono.AssemblyInfo if (t.IsInterface) return "interface"; - if (typeof (Delegate).IsAssignableFrom (t)) + if (TypeHelper.IsDelegate(t)) return "delegate"; return "class"; } - private static string GetCharSet (Type type) + static string GetCharSet (TypeDefinition type) { - if (type.IsAnsiClass) - return CharSet.Ansi.ToString (CultureInfo.InvariantCulture); + TypeAttributes maskedStringFormat = type.Attributes & TypeAttributes.StringFormatMask; + if (maskedStringFormat == TypeAttributes.AnsiClass) + return CharSet.Ansi.ToString (); - if (type.IsAutoClass) - return CharSet.Auto.ToString (CultureInfo.InvariantCulture); + if (maskedStringFormat == TypeAttributes.AutoClass) + return CharSet.Auto.ToString (); - if (type.IsUnicodeClass) - return CharSet.Unicode.ToString (CultureInfo.InvariantCulture); + if (maskedStringFormat == TypeAttributes.UnicodeClass) + return CharSet.Unicode.ToString (); - return CharSet.None.ToString (CultureInfo.InvariantCulture); + return CharSet.None.ToString (); } - private static string GetLayout (Type type) + static string GetLayout (TypeDefinition type) { - if (type.IsAutoLayout) - return LayoutKind.Auto.ToString (CultureInfo.InvariantCulture); + TypeAttributes maskedLayout = type.Attributes & TypeAttributes.LayoutMask; + if (maskedLayout == TypeAttributes.AutoLayout) + return LayoutKind.Auto.ToString (); - if (type.IsExplicitLayout) - return LayoutKind.Explicit.ToString (CultureInfo.InvariantCulture); + if (maskedLayout == TypeAttributes.ExplicitLayout) + return LayoutKind.Explicit.ToString (); - if (type.IsLayoutSequential) - return LayoutKind.Sequential.ToString (CultureInfo.InvariantCulture); + if (maskedLayout == TypeAttributes.SequentialLayout) + return LayoutKind.Sequential.ToString (); return null; } - private FieldInfo[] GetFields (Type type) - { + FieldDefinition [] GetFields (TypeDefinition type) { ArrayList list = new ArrayList (); - FieldInfo[] fields = type.GetFields (flags); - foreach (FieldInfo field in fields) { + var fields = type.Fields; + foreach (FieldDefinition field in fields) { if (field.IsSpecialName) continue; - // we're only interested in public or protected members - if (!field.IsPublic && !field.IsFamily && !field.IsFamilyOrAssembly) + if (Driver.AbiMode && field.IsStatic) continue; - list.Add (field); + // we're only interested in public or protected members + FieldAttributes maskedVisibility = (field.Attributes & FieldAttributes.FieldAccessMask); + if (Driver.AbiMode && !field.IsNotSerialized) { + list.Add (field); + } else { + if (maskedVisibility == FieldAttributes.Public + || maskedVisibility == FieldAttributes.Family + || maskedVisibility == FieldAttributes.FamORAssem) { + list.Add (field); + } + } } - return (FieldInfo[]) list.ToArray (typeof (FieldInfo)); + return (FieldDefinition []) list.ToArray (typeof (FieldDefinition)); } - internal static PropertyInfo[] GetProperties (Type type) - { + + internal static PropertyDefinition [] GetProperties (TypeDefinition type) { ArrayList list = new ArrayList (); - PropertyInfo[] properties = type.GetProperties (flags); - foreach (PropertyInfo property in properties) { - MethodInfo getMethod = null; - MethodInfo setMethod = null; - - if (property.CanRead) { - try { getMethod = property.GetGetMethod (true); } - catch (System.Security.SecurityException) { } - } - if (property.CanWrite) { - try { setMethod = property.GetSetMethod (true); } - catch (System.Security.SecurityException) { } - } + 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); // if neither the getter or setter should be documented, then // skip the property - if (!hasGetter && !hasSetter) { - continue; + if (hasGetter || hasSetter) { + list.Add (property); } - - list.Add (property); } - return (PropertyInfo[]) list.ToArray (typeof (PropertyInfo)); + return (PropertyDefinition []) list.ToArray (typeof (PropertyDefinition)); } - private MethodInfo[] GetMethods (Type type) + private MethodDefinition[] GetMethods (TypeDefinition type) { ArrayList list = new ArrayList (); - MethodInfo[] methods = type.GetMethods (flags); - foreach (MethodInfo method in methods) { - if (method.IsSpecialName) + var methods = type.Methods;//type.GetMethods (flags); + foreach (MethodDefinition method in methods) { + if (method.IsSpecialName && !method.Name.StartsWith ("op_")) 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); + + method.Name = "~" + name; + } + list.Add (method); } - return (MethodInfo[]) list.ToArray (typeof (MethodInfo)); + return (MethodDefinition []) list.ToArray (typeof (MethodDefinition)); } - private ConstructorInfo[] GetConstructors (Type type) + static bool IsFinalizer (MethodDefinition method) + { + if (method.Name != "Finalize") + return false; + + if (!method.IsVirtual) + return false; + + if (method.Parameters.Count != 0) + return false; + + return true; + } + + private MethodDefinition [] GetConstructors (TypeDefinition type) { ArrayList list = new ArrayList (); - ConstructorInfo[] ctors = type.GetConstructors (flags); - foreach (ConstructorInfo constructor in ctors) { + var ctors = type.Methods.Where (m => m.IsConstructor);//type.GetConstructors (flags); + foreach (MethodDefinition constructor in ctors) { // we're only interested in public or protected members - if (!constructor.IsPublic && !constructor.IsFamily && !constructor.IsFamilyOrAssembly) + if (!MustDocumentMethod(constructor)) continue; list.Add (constructor); } - return (ConstructorInfo[]) list.ToArray (typeof (ConstructorInfo)); + return (MethodDefinition []) list.ToArray (typeof (MethodDefinition)); } - private EventInfo[] GetEvents (Type type) + private EventDefinition[] GetEvents (TypeDefinition type) { ArrayList list = new ArrayList (); - EventInfo[] events = type.GetEvents (flags); - foreach (EventInfo eventInfo in events) { - MethodInfo addMethod = eventInfo.GetAddMethod (true); + var events = type.Events;//type.GetEvents (flags); + foreach (EventDefinition eventDef in events) { + MethodDefinition addMethod = eventDef.AddMethod;//eventInfo.GetAddMethod (true); if (addMethod == null || !MustDocumentMethod (addMethod)) continue; - list.Add (eventInfo); + list.Add (eventDef); } - return (EventInfo[]) list.ToArray (typeof (EventInfo)); + return (EventDefinition []) list.ToArray (typeof (EventDefinition)); } } class FieldData : MemberData { - public FieldData (XmlDocument document, XmlNode parent, FieldInfo [] members) + public FieldData (XmlDocument document, XmlNode parent, FieldDefinition [] members) : base (document, parent, members) { } - protected override string GetName (MemberInfo member) + protected override IList GetCustomAttributes (MemberReference member) { + return ((FieldDefinition) member).CustomAttributes; + } + + protected override string GetName (MemberReference memberDefenition) { - FieldInfo field = (FieldInfo) member; + FieldDefinition field = (FieldDefinition) memberDefenition; return field.Name; } - protected override string GetMemberAttributes (MemberInfo member) + protected override string GetMemberAttributes (MemberReference memberDefenition) { - FieldInfo field = (FieldInfo) member; + FieldDefinition field = (FieldDefinition) memberDefenition; return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture); } - protected override void AddExtraData (XmlNode p, MemberInfo member) + protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) { - base.AddExtraData (p, member); - FieldInfo field = (FieldInfo) member; - AddAttribute (p, "fieldtype", field.FieldType.ToString ()); + base.AddExtraData (p, memberDefenition); + FieldDefinition field = (FieldDefinition) memberDefenition; + AddAttribute (p, "fieldtype", Utils.CleanupTypeName (field.FieldType)); if (field.IsLiteral) { - object value = field.GetValue (null); + object value = field.Constant;//object value = field.GetValue (null); string stringValue = null; - if (value is Enum) { - // FIXME: when Mono bug #60090 has been - // fixed, we should just be able to use - // Convert.ToString - stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture); - } else { + //if (value is Enum) { + // // FIXME: when Mono bug #60090 has been + // // fixed, we should just be able to use + // // Convert.ToString + // stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture); + //} + //else { stringValue = Convert.ToString (value, CultureInfo.InvariantCulture); - } + //} if (stringValue != null) AddAttribute (p, "value", stringValue); @@ -595,52 +743,58 @@ namespace Mono.AssemblyInfo class PropertyData : MemberData { - public PropertyData (XmlDocument document, XmlNode parent, PropertyInfo [] members) + public PropertyData (XmlDocument document, XmlNode parent, PropertyDefinition [] members) : base (document, parent, members) { } - protected override string GetName (MemberInfo member) + protected override IList GetCustomAttributes (MemberReference member) { + return ((PropertyDefinition) member).CustomAttributes; + } + + protected override string GetName (MemberReference memberDefenition) { - PropertyInfo prop = (PropertyInfo) member; + PropertyDefinition prop = (PropertyDefinition) memberDefenition; return prop.Name; } - protected override void AddExtraData (XmlNode p, MemberInfo member) + protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) { - base.AddExtraData (p, member); - PropertyInfo prop = (PropertyInfo) member; - Type t = prop.PropertyType; - AddAttribute (p, "ptype", prop.PropertyType.ToString ()); - MethodInfo _get = prop.GetGetMethod (true); - MethodInfo _set = prop.GetSetMethod (true); + 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)); - MethodInfo [] methods; + MethodDefinition [] methods; if (haveGet && haveSet) { - methods = new MethodInfo [] {_get, _set}; + methods = new MethodDefinition [] { _get, _set }; } else if (haveGet) { - methods = new MethodInfo [] {_get}; + methods = new MethodDefinition [] { _get }; } else if (haveSet) { - methods = new MethodInfo [] {_set}; + methods = new MethodDefinition [] { _set }; } else { //odd return; } - string parms = Parameters.GetSignature (methods [0].GetParameters ()); - AddAttribute (p, "params", parms); + if (haveGet || _set.Parameters.Count > 1) { + string parms = Parameters.GetSignature (methods [0].Parameters); + if (!string.IsNullOrEmpty (parms)) + AddAttribute (p, "params", parms); + } MethodData data = new MethodData (document, p, methods); //data.NoMemberAttributes = true; data.DoOutput (); } - protected override string GetMemberAttributes (MemberInfo member) + protected override string GetMemberAttributes (MemberReference memberDefenition) { - PropertyInfo prop = (PropertyInfo) member; - return ((int) prop.Attributes & (0xFFFFFFFF ^ (int) PropertyAttributes.ReservedMask)).ToString (CultureInfo.InvariantCulture); + PropertyDefinition prop = (PropertyDefinition) memberDefenition; + return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture); } public override string ParentTag { @@ -654,28 +808,32 @@ namespace Mono.AssemblyInfo class EventData : MemberData { - public EventData (XmlDocument document, XmlNode parent, EventInfo [] members) + public EventData (XmlDocument document, XmlNode parent, EventDefinition [] members) : base (document, parent, members) { } - protected override string GetName (MemberInfo member) + protected override IList GetCustomAttributes (MemberReference member) { + return ((EventDefinition) member).CustomAttributes; + } + + protected override string GetName (MemberReference memberDefenition) { - EventInfo evt = (EventInfo) member; + EventDefinition evt = (EventDefinition) memberDefenition; return evt.Name; } - protected override string GetMemberAttributes (MemberInfo member) + protected override string GetMemberAttributes (MemberReference memberDefenition) { - EventInfo evt = (EventInfo) member; + EventDefinition evt = (EventDefinition) memberDefenition; return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture); } - protected override void AddExtraData (XmlNode p, MemberInfo member) + protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) { - base.AddExtraData (p, member); - EventInfo evt = (EventInfo) member; - AddAttribute (p, "eventtype", evt.EventHandlerType.ToString ()); + base.AddExtraData (p, memberDefenition); + EventDefinition evt = (EventDefinition) memberDefenition; + AddAttribute (p, "eventtype", Utils.CleanupTypeName (evt.EventType)); } public override string ParentTag { @@ -691,71 +849,41 @@ namespace Mono.AssemblyInfo { bool noAtts; - public MethodData (XmlDocument document, XmlNode parent, MethodBase [] members) + public MethodData (XmlDocument document, XmlNode parent, MethodDefinition [] members) : base (document, parent, members) { } - protected override string GetName (MemberInfo member) + protected override IList GetCustomAttributes (MemberReference member) { + return ((MethodDefinition) member).CustomAttributes; + } + + protected override string GetName (MemberReference memberDefenition) { - MethodBase method = (MethodBase) member; + MethodDefinition method = (MethodDefinition) memberDefenition; string name = method.Name; - string parms = Parameters.GetSignature (method.GetParameters ()); -#if NET_2_0 - MethodInfo mi = method as MethodInfo; - Type [] genArgs = mi == null ? Type.EmptyTypes : - mi.GetGenericArguments (); - if (genArgs.Length > 0) { - string [] genArgNames = new string [genArgs.Length]; - for (int i = 0; i < genArgs.Length; i++) { - genArgNames [i] = genArgs [i].Name; - string genArgCsts = String.Empty; - Type [] gcs = genArgs [i].GetGenericParameterConstraints (); - if (gcs.Length > 0) { - string [] gcNames = new string [gcs.Length]; - for (int g = 0; g < gcs.Length; g++) - gcNames [g] = gcs [g].FullName; - genArgCsts = String.Concat ( - "(", - string.Join (", ", gcNames), - ") ", - genArgNames [i]); - } - else - genArgCsts = genArgNames [i]; - if ((genArgs [i].GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) - genArgCsts = "class " + genArgCsts; - else if ((genArgs [i].GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) - genArgCsts = "struct " + genArgCsts; - genArgNames [i] = genArgCsts; - } - return String.Format ("{0}<{2}>({1})", - name, - parms, - string.Join (",", genArgNames)); - } -#endif - return String.Format ("{0}({1})", name, parms); + string parms = Parameters.GetSignature (method.Parameters); + + return string.Format ("{0}({1})", name, parms); } - protected override string GetMemberAttributes (MemberInfo member) + protected override string GetMemberAttributes (MemberReference memberDefenition) { - MethodBase method = (MethodBase) member; - return ((int)( method.Attributes & ~MethodAttributes.ReservedMask)).ToString (CultureInfo.InvariantCulture); + MethodDefinition method = (MethodDefinition) memberDefenition; + return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture); } - protected override void AddExtraData (XmlNode p, MemberInfo member) + protected override void AddExtraData (XmlNode p, MemberReference memberDefenition) { - base.AddExtraData (p, member); + base.AddExtraData (p, memberDefenition); - ParameterData parms = new ParameterData (document, p, - ((MethodBase) member).GetParameters ()); - parms.DoOutput (); - - if (!(member is MethodBase)) + if (!(memberDefenition is MethodDefinition)) return; - MethodBase mbase = (MethodBase) member; + MethodDefinition mbase = (MethodDefinition) memberDefenition; + + ParameterData parms = new ParameterData (document, p, mbase.Parameters); + parms.DoOutput (); if (mbase.IsAbstract) AddAttribute (p, "abstract", "true"); @@ -764,45 +892,20 @@ namespace Mono.AssemblyInfo if (mbase.IsStatic) AddAttribute (p, "static", "true"); - if (!(member is MethodInfo)) - return; + string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType); + if (rettype != "System.Void" || !mbase.IsConstructor) + AddAttribute (p, "returntype", (rettype)); - MethodInfo method = (MethodInfo) member; - AddAttribute (p, "returntype", method.ReturnType.ToString ()); - - AttributeData.OutputAttributes (document, p, - method.ReturnTypeCustomAttributes.GetCustomAttributes (false)); -#if NET_2_0 - // Generic constraints - Type [] gargs = method.GetGenericArguments (); - XmlElement ngeneric = (gargs.Length == 0) ? null : - document.CreateElement ("generic-method-constraints"); - foreach (Type garg in gargs) { - Type [] csts = garg.GetGenericParameterConstraints (); - if (csts.Length == 0 || csts [0] == typeof (object)) - continue; - XmlElement el = document.CreateElement ("generic-method-constraint"); - el.SetAttribute ("name", garg.ToString ()); - el.SetAttribute ("generic-attribute", - garg.GenericParameterAttributes.ToString ()); - ngeneric.AppendChild (el); - foreach (Type ct in csts) { - XmlElement cel = document.CreateElement ("type"); - cel.AppendChild (document.CreateTextNode (ct.FullName)); - el.AppendChild (cel); - } - } - if (ngeneric != null && ngeneric.FirstChild != null) - p.AppendChild (ngeneric); -#endif + AttributeData.OutputAttributes (document, p, mbase.MethodReturnType.CustomAttributes); + MemberData.OutputGenericParameters (document, p, mbase); } public override bool NoMemberAttributes { get { return noAtts; } set { noAtts = value; } } - + public override string ParentTag { get { return "methods"; } } @@ -814,7 +917,7 @@ namespace Mono.AssemblyInfo class ConstructorData : MethodData { - public ConstructorData (XmlDocument document, XmlNode parent, ConstructorInfo [] members) + public ConstructorData (XmlDocument document, XmlNode parent, MethodDefinition [] members) : base (document, parent, members) { } @@ -830,9 +933,9 @@ namespace Mono.AssemblyInfo class ParameterData : BaseData { - private ParameterInfo[] parameters; + private IList parameters; - public ParameterData (XmlDocument document, XmlNode parent, ParameterInfo[] parameters) + public ParameterData (XmlDocument document, XmlNode parent, IList parameters) : base (document, parent) { this.parameters = parameters; @@ -840,44 +943,43 @@ namespace Mono.AssemblyInfo public override void DoOutput () { - XmlNode parametersNode = document.CreateElement ("parameters", null); + XmlNode parametersNode = document.CreateElement ("parameters"); parent.AppendChild (parametersNode); - foreach (ParameterInfo parameter in parameters) { - XmlNode paramNode = document.CreateElement ("parameter", null); + foreach (ParameterDefinition parameter in parameters) { + XmlNode paramNode = document.CreateElement ("parameter"); parametersNode.AppendChild (paramNode); AddAttribute (paramNode, "name", parameter.Name); - AddAttribute (paramNode, "position", parameter.Position.ToString(CultureInfo.InvariantCulture)); + AddAttribute (paramNode, "position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture)); AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString()); string direction = "in"; - if (parameter.ParameterType.IsByRef) { + if (parameter.ParameterType is ByReferenceType) direction = parameter.IsOut ? "out" : "ref"; - } - Type t = parameter.ParameterType; - AddAttribute (paramNode, "type", t.ToString ()); + TypeReference t = parameter.ParameterType; + AddAttribute (paramNode, "type", Utils.CleanupTypeName (t)); if (parameter.IsOptional) { AddAttribute (paramNode, "optional", "true"); - if (parameter.DefaultValue != System.DBNull.Value) - AddAttribute (paramNode, "defaultValue", (parameter.DefaultValue == null) ? "NULL" : parameter.DefaultValue.ToString ()); + if (parameter.HasConstant) + AddAttribute (paramNode, "defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ()); } if (direction != "in") AddAttribute (paramNode, "direction", direction); - AttributeData.OutputAttributes (document, paramNode, parameter.GetCustomAttributes (false)); + AttributeData.OutputAttributes (document, paramNode, parameter.CustomAttributes); } } } class AttributeData : BaseData { - object [] atts; + IList atts; - AttributeData (XmlDocument doc, XmlNode parent, object[] attributes) + AttributeData (XmlDocument doc, XmlNode parent, IList attributes) : base (doc, parent) { atts = attributes; @@ -888,7 +990,7 @@ namespace Mono.AssemblyInfo if (document == null) throw new InvalidOperationException ("Document not set"); - if (atts == null || atts.Length == 0) + if (atts == null || atts.Count == 0) return; XmlNode natts = parent.SelectSingleNode("attributes"); @@ -897,108 +999,316 @@ namespace Mono.AssemblyInfo parent.AppendChild (natts); } - for (int i = 0; i < atts.Length; ++i) { - Type t = atts [i].GetType (); - if (!t.IsPublic && !t.Name.EndsWith ("TODOAttribute")) - continue; + for (int i = 0; i < atts.Count; ++i) { + CustomAttribute att = atts [i]; - // we ignore attributes that inherit from SecurityAttribute on purpose as they: - // * aren't part of GetCustomAttributes in Fx 1.0/1.1; - // * are encoded differently and in a different metadata table; and - // * won't ever exactly match MS implementation (from a syntax pov) - if (t.IsSubclassOf (typeof (SecurityAttribute))) + string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType); + if (SkipAttribute (att)) continue; XmlNode node = document.CreateElement ("attribute"); - AddAttribute (node, "name", t.ToString ()); + AddAttribute (node, "name", attName); XmlNode properties = null; - foreach (PropertyInfo pi in TypeData.GetProperties (t)) { - if (pi.Name == "TypeId") + + 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")); } - try { - object o = pi.GetValue (atts [i], null); + object o = attribute_mapping [name]; - XmlNode n = properties.AppendChild (document.CreateElement ("property")); - AddAttribute (n, "name", pi.Name); + XmlNode n = properties.AppendChild (document.CreateElement ("property")); + AddAttribute (n, "name", name); - if (o == null) { - AddAttribute (n, "null", "true"); - continue; - } - - string value = o.ToString (); - if (t == typeof (GuidAttribute)) - value = value.ToUpper (); - - AddAttribute (n, "value", value); - } - catch (TargetInvocationException) { + if (o == null) { + AddAttribute (n, "value", "null"); continue; } + + string value = o.ToString (); + if (attName.EndsWith ("GuidAttribute")) + value = value.ToUpper (); + AddAttribute (n, "value", value); } natts.AppendChild (node); } } - public static void OutputAttributes (XmlDocument doc, XmlNode parent, object[] attributes) + static Dictionary CreateAttributeMapping (CustomAttribute attribute) + { + var mapping = new Dictionary (); + + PopulateMapping (mapping, attribute); + + var constructor = attribute.Constructor.Resolve (); + if (constructor == null || constructor.Parameters.Count == 0) + return mapping; + + PopulateMapping (mapping, constructor, attribute); + + return mapping; + } + + static void PopulateMapping (Dictionary mapping, CustomAttribute attribute) + { + foreach (var named_argument in attribute.Properties) { + var name = named_argument.Name; + var arg = named_argument.Argument; + + if (arg.Value is CustomAttributeArgument) + arg = (CustomAttributeArgument) arg.Value; + + mapping.Add (name, GetArgumentValue (arg.Type, arg.Value)); + } + } + + static Dictionary CreateArgumentFieldMapping (MethodDefinition constructor) + { + Dictionary field_mapping = new Dictionary (); + + int? argument = null; + + foreach (Instruction instruction in constructor.Body.Instructions) { + switch (instruction.OpCode.Code) { + case Code.Ldarg_1: + argument = 1; + break; + case Code.Ldarg_2: + argument = 2; + break; + case Code.Ldarg_3: + argument = 3; + break; + case Code.Ldarg: + case Code.Ldarg_S: + argument = ((ParameterDefinition) instruction.Operand).Index + 1; + break; + + case Code.Stfld: + FieldReference field = (FieldReference) instruction.Operand; + if (field.DeclaringType.FullName != constructor.DeclaringType.FullName) + continue; + + if (!argument.HasValue) + break; + + if (!field_mapping.ContainsKey (field)) + field_mapping.Add (field, (int) argument - 1); + + argument = null; + break; + } + } + + return field_mapping; + } + + static Dictionary CreatePropertyFieldMapping (TypeDefinition type) + { + Dictionary property_mapping = new Dictionary (); + + foreach (PropertyDefinition property in type.Properties) { + if (property.GetMethod == null) + continue; + if (!property.GetMethod.HasBody) + continue; + + foreach (Instruction instruction in property.GetMethod.Body.Instructions) { + if (instruction.OpCode.Code != Code.Ldfld) + continue; + + FieldReference field = (FieldReference) instruction.Operand; + if (field.DeclaringType.FullName != type.FullName) + continue; + + property_mapping.Add (property, field); + break; + } + } + + return property_mapping; + } + + static void PopulateMapping (Dictionary mapping, MethodDefinition constructor, CustomAttribute attribute) + { + if (!constructor.HasBody) + return; + + // Custom handling for attributes with arguments which cannot be easily extracted + var ca = attribute.ConstructorArguments; + switch (constructor.DeclaringType.FullName) { + case "System.Runtime.CompilerServices.DecimalConstantAttribute": + var dca = constructor.Parameters[2].ParameterType == constructor.Module.TypeSystem.Int32 ? + 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); + + mapping.Add ("Value", dca.Value); + return; + case "System.ComponentModel.BindableAttribute": + if (ca.Count != 1) + break; + + if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) { + mapping.Add ("Bindable", ca[0].Value); + } else { + throw new NotImplementedException (); + } + + return; + } + + 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)); + } + } + + static object GetArgumentValue (TypeReference reference, object value) + { + var type = reference.Resolve (); + if (type == null) + return value; + + if (type.IsEnum) { + if (IsFlaggedEnum (type)) + return GetFlaggedEnumValue (type, value); + + return GetEnumValue (type, value); + } + + return value; + } + + static bool IsFlaggedEnum (TypeDefinition type) + { + if (!type.IsEnum) + return false; + + if (type.CustomAttributes.Count == 0) + return false; + + foreach (CustomAttribute attribute in type.CustomAttributes) + if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute") + return true; + + return false; + } + + static object GetFlaggedEnumValue (TypeDefinition type, object value) + { + long flags = Convert.ToInt64 (value); + var signature = new StringBuilder (); + + for (int i = type.Fields.Count - 1; i >= 0; i--) { + FieldDefinition field = type.Fields [i]; + + if (!field.HasConstant) + continue; + + long flag = Convert.ToInt64 (field.Constant); + + if (flag == 0) + continue; + + if ((flags & flag) == flag) { + if (signature.Length != 0) + signature.Append (", "); + + signature.Append (field.Name); + flags -= flag; + } + } + + return signature.ToString (); + } + + static object GetEnumValue (TypeDefinition type, object value) + { + foreach (FieldDefinition field in type.Fields) { + if (!field.HasConstant) + continue; + + if (Comparer.Default.Compare (field.Constant, value) == 0) + return field.Name; + } + + return value; + } + + static bool SkipAttribute (CustomAttribute attribute) + { + var type_name = Utils.CleanupTypeName (attribute.Constructor.DeclaringType); + + return !TypeHelper.IsPublic (attribute) + || type_name.EndsWith ("TODOAttribute"); + } + + public static void OutputAttributes (XmlDocument doc, XmlNode parent, IList attributes) { AttributeData ad = new AttributeData (doc, parent, attributes); ad.DoOutput (); } - - private static bool MustDocumentAttribute (Type attributeType) - { - // only document MonoTODOAttribute and public attributes - return attributeType.Name.EndsWith ("TODOAttribute") || attributeType.IsPublic; - } } - class Parameters - { - private Parameters () {} + static class Parameters { - public static string GetSignature (ParameterInfo [] infos) + public static string GetSignature (IList infos) { - if (infos == null || infos.Length == 0) + if (infos == null || infos.Count == 0) return ""; - StringBuilder sb = new StringBuilder (); - foreach (ParameterInfo info in infos) { - string modifier; - if (info.IsIn) - modifier = "in "; - else if (info.IsRetval) - modifier = "ref "; - else if (info.IsOut) - modifier = "out "; - else - modifier = ""; + var signature = new StringBuilder (); + for (int i = 0; i < infos.Count; i++) { - string type_name = info.ParameterType.ToString ().Replace ('<', '[').Replace ('>', ']'); - sb.AppendFormat ("{0}{1}, ", modifier, type_name); + if (i > 0) + signature.Append (", "); + + 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; + + if (modifier.Length > 0) + signature.AppendFormat ("{0} ", modifier); + + signature.Append (Utils.CleanupTypeName (info.ParameterType)); } - sb.Length -= 2; // remove ", " - return sb.ToString (); + return signature.ToString (); } } - - class TypeComparer : IComparer + + class TypeReferenceComparer : IComparer { - public static TypeComparer Default = new TypeComparer (); + public static TypeReferenceComparer Default = new TypeReferenceComparer (); public int Compare (object a, object b) { - Type ta = (Type) a; - Type tb = (Type) b; + TypeReference ta = (TypeReference) a; + TypeReference tb = (TypeReference) b; int result = String.Compare (ta.Namespace, tb.Namespace); if (result != 0) return result; @@ -1007,34 +1317,35 @@ namespace Mono.AssemblyInfo } } - class MemberInfoComparer : IComparer + class MemberReferenceComparer : IComparer { - public static MemberInfoComparer Default = new MemberInfoComparer (); + public static MemberReferenceComparer Default = new MemberReferenceComparer (); public int Compare (object a, object b) { - MemberInfo ma = (MemberInfo) a; - MemberInfo mb = (MemberInfo) b; + MemberReference ma = (MemberReference) a; + MemberReference mb = (MemberReference) b; return String.Compare (ma.Name, mb.Name); } } - class MethodBaseComparer : IComparer + class MethodDefinitionComparer : IComparer { - public static MethodBaseComparer Default = new MethodBaseComparer (); + public static MethodDefinitionComparer Default = new MethodDefinitionComparer (); public int Compare (object a, object b) { - MethodBase ma = (MethodBase) a; - MethodBase mb = (MethodBase) b; + MethodDefinition ma = (MethodDefinition) a; + MethodDefinition mb = (MethodDefinition) b; int res = String.Compare (ma.Name, mb.Name); if (res != 0) return res; - ParameterInfo [] pia = ma.GetParameters (); - ParameterInfo [] pib = mb.GetParameters (); - if (pia.Length != pib.Length) - return pia.Length - pib.Length; + IList pia = ma.Parameters ; + IList pib = mb.Parameters; + res = pia.Count - pib.Count; + if (res != 0) + return res; string siga = Parameters.GetSignature (pia); string sigb = Parameters.GetSignature (pib);