PageRenderTime 44ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/tools/mdoc/Mono.Documentation/monodocer.cs

https://bitbucket.org/danipen/mono
C# | 5008 lines | 4465 code | 460 blank | 83 comment | 1000 complexity | 30cc9fee6844ac8e85177f7b58d515c2 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. // Updater program for syncing Mono's ECMA-style documentation files
  2. // with an assembly.
  3. // By Joshua Tauberer <tauberer@for.net>
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Collections.ObjectModel;
  8. using System.Diagnostics;
  9. using System.Globalization;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Text;
  13. using System.Xml;
  14. using System.Xml.XPath;
  15. using Mono.Cecil;
  16. using Mono.Options;
  17. using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
  18. using StringList = System.Collections.Generic.List<string>;
  19. using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
  20. using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
  21. namespace Mono.Documentation {
  22. class MDocUpdater : MDocCommand
  23. {
  24. string srcPath;
  25. List<AssemblyDefinition> assemblies;
  26. readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
  27. bool delete;
  28. bool show_exceptions;
  29. bool no_assembly_versions, ignore_missing_types;
  30. ExceptionLocations? exceptions;
  31. internal int additions = 0, deletions = 0;
  32. List<DocumentationImporter> importers = new List<DocumentationImporter> ();
  33. DocumentationEnumerator docEnum;
  34. string since;
  35. static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
  36. static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
  37. static MemberFormatter[] typeFormatters = new MemberFormatter[]{
  38. new CSharpMemberFormatter (),
  39. new ILMemberFormatter (),
  40. };
  41. static MemberFormatter[] memberFormatters = new MemberFormatter[]{
  42. new CSharpFullMemberFormatter (),
  43. new ILFullMemberFormatter (),
  44. };
  45. internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
  46. MyXmlNodeList extensionMethods = new MyXmlNodeList ();
  47. HashSet<string> forwardedTypes = new HashSet<string> ();
  48. public override void Run (IEnumerable<string> args)
  49. {
  50. show_exceptions = DebugOutput;
  51. var types = new List<string> ();
  52. var p = new OptionSet () {
  53. { "delete",
  54. "Delete removed members from the XML files.",
  55. v => delete = v != null },
  56. { "exceptions:",
  57. "Document potential exceptions that members can generate. {SOURCES} " +
  58. "is a comma-separated list of:\n" +
  59. " asm Method calls in same assembly\n" +
  60. " depasm Method calls in dependent assemblies\n" +
  61. " all Record all possible exceptions\n" +
  62. " added Modifier; only create <exception/>s\n" +
  63. " for NEW types/members\n" +
  64. "If nothing is specified, then only exceptions from the member will " +
  65. "be listed.",
  66. v => exceptions = ParseExceptionLocations (v) },
  67. { "f=",
  68. "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
  69. v => {
  70. switch (v) {
  71. case "ignore-missing-types":
  72. ignore_missing_types = true;
  73. break;
  74. case "no-assembly-versions":
  75. no_assembly_versions = true;
  76. break;
  77. default:
  78. throw new Exception ("Unsupported flag `" + v + "'.");
  79. }
  80. } },
  81. { "fignore-missing-types",
  82. "Do not report an error if a --type=TYPE type\nwas not found.",
  83. v => ignore_missing_types = v != null },
  84. { "fno-assembly-versions",
  85. "Do not generate //AssemblyVersion elements.",
  86. v => no_assembly_versions = v != null },
  87. { "i|import=",
  88. "Import documentation from {FILE}.",
  89. v => AddImporter (v) },
  90. { "L|lib=",
  91. "Check for assembly references in {DIRECTORY}.",
  92. v => assemblyResolver.AddSearchDirectory (v) },
  93. { "library=",
  94. "Ignored for compatibility with update-ecma-xml.",
  95. v => {} },
  96. { "o|out=",
  97. "Root {DIRECTORY} to generate/update documentation.",
  98. v => srcPath = v },
  99. { "r=",
  100. "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
  101. "(Equivalent to '-L `dirname ASSEMBLY`'.)",
  102. v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
  103. { "since=",
  104. "Manually specify the assembly {VERSION} that new members were added in.",
  105. v => since = v },
  106. { "type=",
  107. "Only update documentation for {TYPE}.",
  108. v => types.Add (v) },
  109. };
  110. var assemblies = Parse (p, args, "update",
  111. "[OPTIONS]+ ASSEMBLIES",
  112. "Create or update documentation from ASSEMBLIES.");
  113. if (assemblies == null)
  114. return;
  115. if (assemblies.Count == 0)
  116. Error ("No assemblies specified.");
  117. foreach (var dir in assemblies
  118. .Where (a => a.Contains (Path.DirectorySeparatorChar))
  119. .Select (a => Path.GetDirectoryName (a)))
  120. assemblyResolver.AddSearchDirectory (dir);
  121. // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
  122. if (srcPath == null)
  123. throw new InvalidOperationException("The --out option is required.");
  124. this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
  125. // Store types that have been forwarded to avoid duplicate generation
  126. GatherForwardedTypes ();
  127. docEnum = docEnum ?? new DocumentationEnumerator ();
  128. // PERFORM THE UPDATES
  129. if (types.Count > 0) {
  130. types.Sort ();
  131. DoUpdateTypes (srcPath, types, srcPath);
  132. }
  133. #if false
  134. else if (opts.@namespace != null)
  135. DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
  136. Path.Combine (dest_dir, opts.@namespace));
  137. #endif
  138. else
  139. DoUpdateAssemblies (srcPath, srcPath);
  140. Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
  141. }
  142. void AddImporter (string path)
  143. {
  144. try {
  145. XmlReader r = new XmlTextReader (path);
  146. if (r.Read ()) {
  147. while (r.NodeType != XmlNodeType.Element) {
  148. if (!r.Read ())
  149. Error ("Unable to read XML file: {0}.", path);
  150. }
  151. if (r.LocalName == "doc") {
  152. importers.Add (new MsxdocDocumentationImporter (path));
  153. }
  154. else if (r.LocalName == "Libraries") {
  155. var ecmadocs = new XmlTextReader (path);
  156. docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
  157. importers.Add (new EcmaDocumentationImporter (ecmadocs));
  158. }
  159. else
  160. Error ("Unsupported XML format within {0}.", path);
  161. }
  162. r.Close ();
  163. } catch (Exception e) {
  164. Environment.ExitCode = 1;
  165. Error ("Could not load XML file: {0}.", e.Message);
  166. }
  167. }
  168. void GatherForwardedTypes ()
  169. {
  170. foreach (var asm in assemblies)
  171. foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
  172. forwardedTypes.Add (type);
  173. }
  174. static ExceptionLocations ParseExceptionLocations (string s)
  175. {
  176. ExceptionLocations loc = ExceptionLocations.Member;
  177. if (s == null)
  178. return loc;
  179. foreach (var type in s.Split (',')) {
  180. switch (type) {
  181. case "added": loc |= ExceptionLocations.AddedMembers; break;
  182. case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
  183. case "asm": loc |= ExceptionLocations.Assembly; break;
  184. case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
  185. default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
  186. }
  187. }
  188. return loc;
  189. }
  190. internal void Warning (string format, params object[] args)
  191. {
  192. Message (TraceLevel.Warning, "mdoc: " + format, args);
  193. }
  194. private AssemblyDefinition LoadAssembly (string name)
  195. {
  196. AssemblyDefinition assembly = null;
  197. try {
  198. assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
  199. } catch (System.IO.FileNotFoundException) { }
  200. if (assembly == null)
  201. throw new InvalidOperationException("Assembly " + name + " not found.");
  202. return assembly;
  203. }
  204. private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
  205. OrderTypeAttributes (element);
  206. XmlTextWriter writer = new XmlTextWriter(output);
  207. writer.Formatting = Formatting.Indented;
  208. writer.Indentation = 2;
  209. writer.IndentChar = ' ';
  210. element.WriteTo(writer);
  211. output.WriteLine();
  212. }
  213. private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
  214. {
  215. Action<string> creator = file => {
  216. using (var writer = OpenWrite (file, mode))
  217. action (writer);
  218. };
  219. MdocFile.UpdateFile (filename, creator);
  220. }
  221. private static void OrderTypeAttributes (XmlElement e)
  222. {
  223. foreach (XmlElement type in e.SelectNodes ("//Type")) {
  224. OrderTypeAttributes (type.Attributes);
  225. }
  226. }
  227. static readonly string[] TypeAttributeOrder = {
  228. "Name", "FullName", "FullNameSP", "Maintainer"
  229. };
  230. private static void OrderTypeAttributes (XmlAttributeCollection c)
  231. {
  232. XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
  233. for (int i = 0; i < c.Count; ++i) {
  234. XmlAttribute a = c [i];
  235. for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
  236. if (a.Name == TypeAttributeOrder [j]) {
  237. attrs [j] = a;
  238. break;
  239. }
  240. }
  241. }
  242. for (int i = attrs.Length-1; i >= 0; --i) {
  243. XmlAttribute n = attrs [i];
  244. if (n == null)
  245. continue;
  246. XmlAttribute r = null;
  247. for (int j = i+1; j < attrs.Length; ++j) {
  248. if (attrs [j] != null) {
  249. r = attrs [j];
  250. break;
  251. }
  252. }
  253. if (r == null)
  254. continue;
  255. c.Remove (n);
  256. c.InsertBefore (n, r);
  257. }
  258. }
  259. private XmlDocument CreateIndexStub()
  260. {
  261. XmlDocument index = new XmlDocument();
  262. XmlElement index_root = index.CreateElement("Overview");
  263. index.AppendChild(index_root);
  264. if (assemblies.Count == 0)
  265. throw new Exception ("No assembly");
  266. XmlElement index_assemblies = index.CreateElement("Assemblies");
  267. index_root.AppendChild(index_assemblies);
  268. XmlElement index_remarks = index.CreateElement("Remarks");
  269. index_remarks.InnerText = "To be added.";
  270. index_root.AppendChild(index_remarks);
  271. XmlElement index_copyright = index.CreateElement("Copyright");
  272. index_copyright.InnerText = "To be added.";
  273. index_root.AppendChild(index_copyright);
  274. XmlElement index_types = index.CreateElement("Types");
  275. index_root.AppendChild(index_types);
  276. return index;
  277. }
  278. private static void WriteNamespaceStub(string ns, string outdir) {
  279. XmlDocument index = new XmlDocument();
  280. XmlElement index_root = index.CreateElement("Namespace");
  281. index.AppendChild(index_root);
  282. index_root.SetAttribute("Name", ns);
  283. XmlElement index_docs = index.CreateElement("Docs");
  284. index_root.AppendChild(index_docs);
  285. XmlElement index_summary = index.CreateElement("summary");
  286. index_summary.InnerText = "To be added.";
  287. index_docs.AppendChild(index_summary);
  288. XmlElement index_remarks = index.CreateElement("remarks");
  289. index_remarks.InnerText = "To be added.";
  290. index_docs.AppendChild(index_remarks);
  291. WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
  292. writer => WriteXml (index.DocumentElement, writer));
  293. }
  294. public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
  295. {
  296. var index = CreateIndexForTypes (dest);
  297. var found = new HashSet<string> ();
  298. foreach (AssemblyDefinition assembly in assemblies) {
  299. foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
  300. string relpath = DoUpdateType (type, basepath, dest);
  301. if (relpath == null)
  302. continue;
  303. found.Add (type.FullName);
  304. if (index == null)
  305. continue;
  306. index.Add (assembly);
  307. index.Add (type);
  308. }
  309. }
  310. if (index != null)
  311. index.Write ();
  312. if (ignore_missing_types)
  313. return;
  314. var notFound = from n in typenames where !found.Contains (n) select n;
  315. if (notFound.Any ())
  316. throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
  317. }
  318. class IndexForTypes {
  319. MDocUpdater app;
  320. string indexFile;
  321. XmlDocument index;
  322. XmlElement index_types;
  323. XmlElement index_assemblies;
  324. public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
  325. {
  326. this.app = app;
  327. this.indexFile = indexFile;
  328. this.index = index;
  329. index_types = WriteElement (index.DocumentElement, "Types");
  330. index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
  331. }
  332. public void Add (AssemblyDefinition assembly)
  333. {
  334. if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
  335. return;
  336. app.AddIndexAssembly (assembly, index_assemblies);
  337. }
  338. public void Add (TypeDefinition type)
  339. {
  340. app.AddIndexType (type, index_types);
  341. }
  342. public void Write ()
  343. {
  344. SortIndexEntries (index_types);
  345. WriteFile (indexFile, FileMode.Create,
  346. writer => WriteXml (index.DocumentElement, writer));
  347. }
  348. }
  349. IndexForTypes CreateIndexForTypes (string dest)
  350. {
  351. string indexFile = Path.Combine (dest, "index.xml");
  352. if (File.Exists (indexFile))
  353. return null;
  354. return new IndexForTypes (this, indexFile, CreateIndexStub ());
  355. }
  356. public string DoUpdateType (TypeDefinition type, string basepath, string dest)
  357. {
  358. if (type.Namespace == null)
  359. Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
  360. type.FullName);
  361. if (!IsPublic (type))
  362. return null;
  363. // Must get the A+B form of the type name.
  364. string typename = GetTypeFileName(type);
  365. string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
  366. string typefile = Path.Combine (basepath, reltypefile);
  367. System.IO.FileInfo file = new System.IO.FileInfo(typefile);
  368. string output = null;
  369. if (dest == null) {
  370. output = typefile;
  371. } else if (dest == "-") {
  372. output = null;
  373. } else {
  374. output = Path.Combine (dest, reltypefile);
  375. }
  376. if (file.Exists) {
  377. // Update
  378. XmlDocument basefile = new XmlDocument();
  379. try {
  380. basefile.Load(typefile);
  381. } catch (Exception e) {
  382. throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
  383. }
  384. DoUpdateType2("Updating", basefile, type, output, false);
  385. } else {
  386. // Stub
  387. XmlElement td = StubType(type, output);
  388. if (td == null)
  389. return null;
  390. System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
  391. if (!dir.Exists) {
  392. dir.Create();
  393. Console.WriteLine("Namespace Directory Created: " + type.Namespace);
  394. }
  395. }
  396. return reltypefile;
  397. }
  398. public void DoUpdateNS (string ns, string nspath, string outpath)
  399. {
  400. Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
  401. AssemblyDefinition assembly = assemblies [0];
  402. foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
  403. XmlDocument basefile = new XmlDocument();
  404. string typefile = Path.Combine(nspath, file.Name);
  405. try {
  406. basefile.Load(typefile);
  407. } catch (Exception e) {
  408. throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
  409. }
  410. string typename =
  411. GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
  412. TypeDefinition type = assembly.GetType(typename);
  413. if (type == null) {
  414. Warning ("Type no longer in assembly: " + typename);
  415. continue;
  416. }
  417. seenTypes[type] = seenTypes;
  418. DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
  419. }
  420. // Stub types not in the directory
  421. foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
  422. if (type.Namespace != ns || seenTypes.ContainsKey(type))
  423. continue;
  424. XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
  425. if (td == null) continue;
  426. }
  427. }
  428. private static string GetTypeFileName (TypeReference type)
  429. {
  430. return filenameFormatter.GetName (type);
  431. }
  432. public static string GetTypeFileName (string typename)
  433. {
  434. StringBuilder filename = new StringBuilder (typename.Length);
  435. int numArgs = 0;
  436. int numLt = 0;
  437. bool copy = true;
  438. for (int i = 0; i < typename.Length; ++i) {
  439. char c = typename [i];
  440. switch (c) {
  441. case '<':
  442. copy = false;
  443. ++numLt;
  444. break;
  445. case '>':
  446. --numLt;
  447. if (numLt == 0) {
  448. filename.Append ('`').Append ((numArgs+1).ToString());
  449. numArgs = 0;
  450. copy = true;
  451. }
  452. break;
  453. case ',':
  454. if (numLt == 1)
  455. ++numArgs;
  456. break;
  457. default:
  458. if (copy)
  459. filename.Append (c);
  460. break;
  461. }
  462. }
  463. return filename.ToString ();
  464. }
  465. private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
  466. {
  467. XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
  468. index_assembly.SetAttribute ("Name", assembly.Name.Name);
  469. index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
  470. AssemblyNameDefinition name = assembly.Name;
  471. if (name.HasPublicKey) {
  472. XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
  473. var key = new StringBuilder (name.PublicKey.Length*3 + 2);
  474. key.Append ("[");
  475. foreach (byte b in name.PublicKey)
  476. key.AppendFormat ("{0,2:x2} ", b);
  477. key.Append ("]");
  478. pubkey.InnerText = key.ToString ();
  479. index_assembly.AppendChild (pubkey);
  480. }
  481. if (!string.IsNullOrEmpty (name.Culture)) {
  482. XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
  483. culture.InnerText = name.Culture;
  484. index_assembly.AppendChild (culture);
  485. }
  486. MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
  487. parent.AppendChild(index_assembly);
  488. }
  489. private void AddIndexType (TypeDefinition type, XmlElement index_types)
  490. {
  491. string typename = GetTypeFileName(type);
  492. // Add namespace and type nodes into the index file as needed
  493. string ns = DocUtils.GetNamespace (type);
  494. XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
  495. if (nsnode == null) {
  496. nsnode = index_types.OwnerDocument.CreateElement("Namespace");
  497. nsnode.SetAttribute ("Name", ns);
  498. index_types.AppendChild (nsnode);
  499. }
  500. string doc_typename = GetDocTypeName (type);
  501. XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
  502. if (typenode == null) {
  503. typenode = index_types.OwnerDocument.CreateElement ("Type");
  504. typenode.SetAttribute ("Name", typename);
  505. nsnode.AppendChild (typenode);
  506. }
  507. if (typename != doc_typename)
  508. typenode.SetAttribute("DisplayName", doc_typename);
  509. else
  510. typenode.RemoveAttribute("DisplayName");
  511. typenode.SetAttribute ("Kind", GetTypeKind (type));
  512. }
  513. private void DoUpdateAssemblies (string source, string dest)
  514. {
  515. string indexfile = dest + "/index.xml";
  516. XmlDocument index;
  517. if (System.IO.File.Exists(indexfile)) {
  518. index = new XmlDocument();
  519. index.Load(indexfile);
  520. // Format change
  521. ClearElement(index.DocumentElement, "Assembly");
  522. ClearElement(index.DocumentElement, "Attributes");
  523. } else {
  524. index = CreateIndexStub();
  525. }
  526. string defaultTitle = "Untitled";
  527. if (assemblies.Count == 1)
  528. defaultTitle = assemblies[0].Name.Name;
  529. WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
  530. XmlElement index_types = WriteElement(index.DocumentElement, "Types");
  531. XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
  532. index_assemblies.RemoveAll ();
  533. HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
  534. foreach (AssemblyDefinition assm in assemblies) {
  535. AddIndexAssembly (assm, index_assemblies);
  536. DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
  537. }
  538. SortIndexEntries (index_types);
  539. CleanupFiles (dest, goodfiles);
  540. CleanupIndexTypes (index_types, goodfiles);
  541. CleanupExtensions (index_types);
  542. WriteFile (indexfile, FileMode.Create,
  543. writer => WriteXml(index.DocumentElement, writer));
  544. }
  545. private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
  546. private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
  547. {
  548. foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
  549. string typename = GetTypeFileName(type);
  550. if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
  551. continue;
  552. string reltypepath = DoUpdateType (type, source, dest);
  553. if (reltypepath == null)
  554. continue;
  555. // Add namespace and type nodes into the index file as needed
  556. AddIndexType (type, index_types);
  557. // Ensure the namespace index file exists
  558. string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
  559. string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
  560. if (File.Exists (onsdoc)) {
  561. File.Move (onsdoc, nsdoc);
  562. }
  563. if (!File.Exists (nsdoc)) {
  564. Console.WriteLine("New Namespace File: " + type.Namespace);
  565. WriteNamespaceStub(type.Namespace, dest);
  566. }
  567. goodfiles.Add (reltypepath);
  568. }
  569. }
  570. private static void SortIndexEntries (XmlElement indexTypes)
  571. {
  572. XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
  573. XmlNodeComparer c = new AttributeNameComparer ();
  574. SortXmlNodes (indexTypes, namespaces, c);
  575. for (int i = 0; i < namespaces.Count; ++i)
  576. SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
  577. }
  578. private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
  579. {
  580. MyXmlNodeList l = new MyXmlNodeList (children.Count);
  581. for (int i = 0; i < children.Count; ++i)
  582. l.Add (children [i]);
  583. l.Sort (comparer);
  584. for (int i = l.Count - 1; i > 0; --i) {
  585. parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
  586. }
  587. }
  588. abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
  589. {
  590. public abstract int Compare (XmlNode x, XmlNode y);
  591. public int Compare (object x, object y)
  592. {
  593. return Compare ((XmlNode) x, (XmlNode) y);
  594. }
  595. }
  596. class AttributeNameComparer : XmlNodeComparer {
  597. string attribute;
  598. public AttributeNameComparer ()
  599. : this ("Name")
  600. {
  601. }
  602. public AttributeNameComparer (string attribute)
  603. {
  604. this.attribute = attribute;
  605. }
  606. public override int Compare (XmlNode x, XmlNode y)
  607. {
  608. return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
  609. }
  610. }
  611. class VersionComparer : XmlNodeComparer {
  612. public override int Compare (XmlNode x, XmlNode y)
  613. {
  614. // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
  615. string a = GetVersion (x.InnerText);
  616. string b = GetVersion (y.InnerText);
  617. return new Version (a).CompareTo (new Version (b));
  618. }
  619. static string GetVersion (string v)
  620. {
  621. int n = v.IndexOf ("x");
  622. if (n < 0)
  623. return v;
  624. return v.Substring (0, n-1);
  625. }
  626. }
  627. private static string GetTypeKind (TypeDefinition type)
  628. {
  629. if (type.IsEnum)
  630. return "Enumeration";
  631. if (type.IsValueType)
  632. return "Structure";
  633. if (type.IsInterface)
  634. return "Interface";
  635. if (DocUtils.IsDelegate (type))
  636. return "Delegate";
  637. if (type.IsClass || type.FullName == "System.Enum") // FIXME
  638. return "Class";
  639. throw new ArgumentException ("Unknown kind for type: " + type.FullName);
  640. }
  641. private static bool IsPublic (TypeDefinition type)
  642. {
  643. TypeDefinition decl = type;
  644. while (decl != null) {
  645. if (!(decl.IsPublic || decl.IsNestedPublic ||
  646. decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
  647. return false;
  648. }
  649. decl = (TypeDefinition) decl.DeclaringType;
  650. }
  651. return true;
  652. }
  653. private void CleanupFiles (string dest, HashSet<string> goodfiles)
  654. {
  655. // Look for files that no longer correspond to types
  656. foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
  657. foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
  658. string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
  659. if (!goodfiles.Contains (relTypeFile)) {
  660. XmlDocument doc = new XmlDocument ();
  661. doc.Load (typefile.FullName);
  662. XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
  663. if (e != null && !no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
  664. using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
  665. WriteXml(doc.DocumentElement, writer);
  666. goodfiles.Add (relTypeFile);
  667. continue;
  668. }
  669. string newname = typefile.FullName + ".remove";
  670. try { System.IO.File.Delete(newname); } catch (Exception) { }
  671. try { typefile.MoveTo(newname); } catch (Exception) { }
  672. Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
  673. }
  674. }
  675. }
  676. }
  677. private static TextWriter OpenWrite (string path, FileMode mode)
  678. {
  679. var w = new StreamWriter (
  680. new FileStream (path, mode),
  681. new UTF8Encoding (false)
  682. );
  683. w.NewLine = "\n";
  684. return w;
  685. }
  686. private string[] GetAssemblyVersions ()
  687. {
  688. return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
  689. }
  690. private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
  691. {
  692. // Look for type nodes that no longer correspond to types
  693. MyXmlNodeList remove = new MyXmlNodeList ();
  694. foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
  695. string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
  696. if (!goodfiles.Contains (fulltypename)) {
  697. remove.Add (typenode);
  698. }
  699. }
  700. foreach (XmlNode n in remove)
  701. n.ParentNode.RemoveChild (n);
  702. }
  703. private void CleanupExtensions (XmlElement index_types)
  704. {
  705. XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
  706. if (extensionMethods.Count == 0) {
  707. if (e == null)
  708. return;
  709. index_types.SelectSingleNode ("/Overview").RemoveChild (e);
  710. return;
  711. }
  712. if (e == null) {
  713. e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
  714. index_types.SelectSingleNode ("/Overview").AppendChild (e);
  715. }
  716. else
  717. e.RemoveAll ();
  718. extensionMethods.Sort (DefaultExtensionMethodComparer);
  719. foreach (XmlNode m in extensionMethods) {
  720. e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
  721. }
  722. }
  723. class ExtensionMethodComparer : XmlNodeComparer {
  724. public override int Compare (XmlNode x, XmlNode y)
  725. {
  726. XmlNode xLink = x.SelectSingleNode ("Member/Link");
  727. XmlNode yLink = y.SelectSingleNode ("Member/Link");
  728. int n = xLink.Attributes ["Type"].Value.CompareTo (
  729. yLink.Attributes ["Type"].Value);
  730. if (n != 0)
  731. return n;
  732. n = xLink.Attributes ["Member"].Value.CompareTo (
  733. yLink.Attributes ["Member"].Value);
  734. if (n == 0 && !object.ReferenceEquals (x, y))
  735. throw new InvalidOperationException ("Duplicate extension method found!");
  736. return n;
  737. }
  738. }
  739. static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
  740. public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
  741. {
  742. Console.WriteLine(message + ": " + type.FullName);
  743. StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
  744. // Update type metadata
  745. UpdateType(basefile.DocumentElement, type);
  746. // Update existing members. Delete member nodes that no longer should be there,
  747. // and remember what members are already documented so we don't add them again.
  748. if (true) {
  749. MyXmlNodeList todelete = new MyXmlNodeList ();
  750. foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
  751. XmlElement oldmember = info.Node;
  752. MemberReference oldmember2 = info.Member;
  753. string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
  754. // Interface implementations and overrides are deleted from the docs
  755. // unless the overrides option is given.
  756. if (oldmember2 != null && sig == null)
  757. oldmember2 = null;
  758. // Deleted (or signature changed)
  759. if (oldmember2 == null) {
  760. if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
  761. continue;
  762. DeleteMember ("Member Removed", output, oldmember, todelete);
  763. continue;
  764. }
  765. // Duplicated
  766. if (seenmembers.ContainsKey (sig)) {
  767. if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
  768. // ignore, already seen
  769. }
  770. else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
  771. DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
  772. else
  773. Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
  774. continue;
  775. }
  776. // Update signature information
  777. UpdateMember(info);
  778. seenmembers.Add (sig, oldmember);
  779. }
  780. foreach (XmlElement oldmember in todelete)
  781. oldmember.ParentNode.RemoveChild (oldmember);
  782. }
  783. if (!DocUtils.IsDelegate (type)) {
  784. XmlNode members = WriteElement (basefile.DocumentElement, "Members");
  785. foreach (MemberReference m in type.GetMembers()) {
  786. if (m is TypeDefinition) continue;
  787. string sig = memberFormatters [0].GetDeclaration (m);
  788. if (sig == null) continue;
  789. if (seenmembers.ContainsKey(sig)) continue;
  790. XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
  791. if (mm == null) continue;
  792. members.AppendChild( mm );
  793. Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
  794. additions++;
  795. }
  796. }
  797. // Import code snippets from files
  798. foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
  799. if (!(code is XmlElement)) continue;
  800. string file = ((XmlElement)code).GetAttribute("src");
  801. string lang = ((XmlElement)code).GetAttribute("lang");
  802. if (file != "") {
  803. string src = GetCodeSource (lang, Path.Combine (srcPath, file));
  804. if (src != null)
  805. code.InnerText = src;
  806. }
  807. }
  808. if (insertSince && since != null) {
  809. XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
  810. docs.AppendChild (CreateSinceNode (basefile));
  811. }
  812. do {
  813. XmlElement d = basefile.DocumentElement ["Docs"];
  814. XmlElement m = basefile.DocumentElement ["Members"];
  815. if (d != null && m != null)
  816. basefile.DocumentElement.InsertBefore (
  817. basefile.DocumentElement.RemoveChild (d), m);
  818. SortTypeMembers (m);
  819. } while (false);
  820. if (output == null)
  821. WriteXml(basefile.DocumentElement, Console.Out);
  822. else {
  823. FileInfo file = new FileInfo (output);
  824. if (!file.Directory.Exists) {
  825. Console.WriteLine("Namespace Directory Created: " + type.Namespace);
  826. file.Directory.Create ();
  827. }
  828. WriteFile (output, FileMode.Create,
  829. writer => WriteXml(basefile.DocumentElement, writer));
  830. }
  831. }
  832. private string GetCodeSource (string lang, string file)
  833. {
  834. int anchorStart;
  835. if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
  836. // Grab the specified region
  837. string region = "#region " + file.Substring (anchorStart + 4);
  838. file = file.Substring (0, anchorStart + 3);
  839. try {
  840. using (StreamReader reader = new StreamReader (file)) {
  841. string line;
  842. StringBuilder src = new StringBuilder ();
  843. int indent = -1;
  844. while ((line = reader.ReadLine ()) != null) {
  845. if (line.Trim() == region) {
  846. indent = line.IndexOf (region);
  847. continue;
  848. }
  849. if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
  850. break;
  851. }
  852. if (indent >= 0)
  853. src.Append (
  854. (line.Length > 0 ? line.Substring (indent) : string.Empty) +
  855. "\n");
  856. }
  857. return src.ToString ();
  858. }
  859. } catch (Exception e) {
  860. Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
  861. file, region, show_exceptions ? e.ToString () : e.Message);
  862. return null;
  863. }
  864. }
  865. try {
  866. using (StreamReader reader = new StreamReader (file))
  867. return reader.ReadToEnd ();
  868. } catch (Exception e) {
  869. Warning ("Could not load <code/> file '" + file + "': " + e.Message);
  870. }
  871. return null;
  872. }
  873. void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
  874. {
  875. string format = output != null
  876. ? "{0}: File='{1}'; Signature='{4}'"
  877. : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
  878. Warning (format,
  879. reason,
  880. output,
  881. member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
  882. member.Attributes ["MemberName"].Value,
  883. member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
  884. if (!delete && MemberDocsHaveUserContent (member)) {
  885. Warning ("Member deletions must be enabled with the --delete option.");
  886. } else {
  887. todelete.Add (member);
  888. deletions++;
  889. }
  890. }
  891. class MemberComparer : XmlNodeComparer {
  892. public override int Compare (XmlNode x, XmlNode y)
  893. {
  894. int r;
  895. string xMemberName = x.Attributes ["MemberName"].Value;
  896. string yMemberName = y.Attributes ["MemberName"].Value;
  897. // generic methods *end* with '>'
  898. // it's possible for explicitly implemented generic interfaces to
  899. // contain <...> without being a generic method
  900. if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
  901. (r = xMemberName.CompareTo (yMemberName)) != 0)
  902. return r;
  903. int lt;
  904. if ((lt = xMemberName.IndexOf ("<")) >= 0)
  905. xMemberName = xMemberName.Substring (0, lt);
  906. if ((lt = yMemberName.IndexOf ("<")) >= 0)
  907. yMemberName = yMemberName.Substring (0, lt);
  908. if ((r = xMemberName.CompareTo (yMemberName)) != 0)
  909. return r;
  910. // if @MemberName matches, then it's either two different types of
  911. // members sharing the same name, e.g. field & property, or it's an
  912. // overloaded method.
  913. // for different type, sort based on MemberType value.
  914. r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
  915. y.SelectSingleNode ("MemberType").InnerText);
  916. if (r != 0)
  917. return r;
  918. // same type -- must be an overloaded method. Sort based on type
  919. // parameter count, then parameter count, then by the parameter
  920. // type names.
  921. XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
  922. XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
  923. if (xTypeParams.Count != yTypeParams.Count)
  924. return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
  925. for (int i = 0; i < xTypeParams.Count; ++i) {
  926. r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
  927. yTypeParams [i].Attributes ["Name"].Value);
  928. if (r != 0)
  929. return r;
  930. }
  931. XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
  932. XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
  933. if (xParams.Count != yParams.Count)
  934. return xParams.Count <= yParams.Count ? -1 : 1;
  935. for (int i = 0; i < xParams.Count; ++i) {
  936. r = xParams [i].Attributes ["Type"].Value.CompareTo (
  937. yParams [i].Attributes ["Type"].Value);
  938. if (r != 0)
  939. return r;
  940. }
  941. // all parameters match, but return value might not match if it was
  942. // changed between one version and another.
  943. XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
  944. XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
  945. if (xReturn != null && yReturn != null) {
  946. r = xReturn.InnerText.CompareTo (yReturn.InnerText);
  947. if (r != 0)
  948. return r;
  949. }
  950. return 0;
  951. }
  952. }
  953. static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
  954. private static void SortTypeMembers (XmlNode members)
  955. {
  956. if (members == null)
  957. return;
  958. SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
  959. }
  960. private static bool MemberDocsHaveUserContent (XmlNode e)
  961. {
  962. e = (XmlElement)e.SelectSingleNode("Docs");
  963. if (e == null) return false;
  964. foreach (XmlElement d in e.SelectNodes("*"))
  965. if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
  966. return true;
  967. return false;
  968. }
  969. // UPDATE HELPER FUNCTIONS
  970. // CREATE A STUB DOCUMENTATION FILE
  971. public XmlElement StubType (TypeDefinition type, string output)
  972. {
  973. string typesig = typeFormatters [0].GetDeclaration (type);
  974. if (typesig == null) return null; // not publicly visible
  975. XmlDocument doc = new XmlDocument();
  976. XmlElement root = doc.CreateElement("Type");
  977. doc.AppendChild (root);
  978. DoUpdateType2 ("New Type", doc, type, output, true);
  979. return root;
  980. }
  981. private XmlElement CreateSinceNode (XmlDocument doc)
  982. {
  983. XmlElement s = doc.CreateElement ("since");
  984. s.SetAttribute ("version", since);
  985. return s;
  986. }
  987. // STUBBING/UPDATING FUNCTIONS
  988. public void UpdateType (XmlElement root, TypeDefinition type)
  989. {
  990. root.SetAttribute("Name", GetDocTypeName (type));
  991. root.SetAttribute("FullName", GetDocTypeFullName (type));
  992. foreach (MemberFormatter f in typeFormatters) {
  993. string element = "TypeSignature[@Language='" + f.Language + "']";
  994. WriteElementAttribute (root, element, "Language", f.Language);
  995. WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
  996. }
  997. XmlElement ass = WriteElement(root, "AssemblyInfo");
  998. WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
  999. if (!no_assembly_versions) {
  1000. UpdateAssemblyVersions (root, type, true);
  1001. }
  1002. else {
  1003. var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
  1004. foreach (var version in versions)
  1005. ass.RemoveChild (version);
  1006. }
  1007. if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
  1008. WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
  1009. else
  1010. ClearElement(ass, "AssemblyCulture");
  1011. // Why-oh-why do we put assembly attributes in each type file?
  1012. // Neither monodoc nor monodocs2html use them, so I'm deleting them
  1013. // since they're outdated in current docs, and a waste of space.
  1014. //MakeAttributes(ass, type.Assembly, true);
  1015. XmlNode assattrs = ass.SelectSingleNode("Attributes");
  1016. if (assattrs != null)
  1017. ass.RemoveChild(assattrs);
  1018. NormalizeWhitespace(ass);
  1019. if (type.IsGenericType ()) {
  1020. MakeTypeParameters (root, type.GenericParameters);
  1021. } else {
  1022. ClearElement(root, "TypeParameters");
  1023. }
  1024. if (type.BaseType != null) {
  1025. XmlElement basenode = WriteElement(root, "Base");
  1026. string basetypename = GetDocTypeFullName (type.BaseType);
  1027. if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
  1028. WriteElementText(root, "Base/BaseTypeName", basetypename);
  1029. // Document how this type instantiates the generic parameters of its base type
  1030. TypeReference origBase = type.BaseType.GetElementType ();
  1031. if (origBase.IsGenericType ()) {
  1032. ClearElement(basenode, "BaseTypeArguments");
  1033. GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
  1034. IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
  1035. IList<GenericParameter> baseGenParams = origBase.GenericParameters;
  1036. if (baseGenArgs.Count != baseGenParams.Count)
  1037. throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
  1038. for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
  1039. GenericParameter param = baseGenParams [i];
  1040. TypeReference value = baseGenArgs [i];
  1041. XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
  1042. XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
  1043. bta.AppendChild(arg);
  1044. arg.SetAttribute ("TypeParamName", param.Name);
  1045. arg.InnerText = GetDocTypeFullName (value);
  1046. }
  1047. }
  1048. } else {
  1049. ClearElement(root, "Base");
  1050. }
  1051. if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
  1052. IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
  1053. List<string> interface_names = userInterfaces
  1054. .Select (iface => GetDocTypeFullName (iface))
  1055. .OrderBy (s => s)
  1056. .ToList ();
  1057. XmlElement interfaces = WriteElement(root, "Interfaces");
  1058. interfaces.RemoveAll();
  1059. foreach (string iname in interface_names) {
  1060. XmlElement iface = root.OwnerDocument.CreateElement("Interface");
  1061. interfaces.AppendChild(iface);
  1062. WriteElementText(iface, "InterfaceName", iname);
  1063. }
  1064. } else {
  1065. ClearElement(root, "Interfaces");
  1066. }
  1067. MakeAttributes (root, GetCustomAttributes (type));
  1068. if (DocUtils.IsDelegate (type)) {
  1069. MakeTypeParameters (root, type.GenericParameters);
  1070. MakeParameters(root, type.GetMethod("Invoke").Parameters);
  1071. MakeReturnValue(root, type.GetMethod("Invoke"));
  1072. }
  1073. DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
  1074. MakeDocNode (typeInfo);
  1075. if (!DocUtils.IsDelegate (type))
  1076. WriteElement (root, "Members");
  1077. OrderTypeNodes (root, root.ChildNodes);
  1078. NormalizeWhitespace(root);
  1079. }
  1080. static readonly string[] TypeNodeOrder = {
  1081. "TypeSignature",
  1082. "MemberOfLibrary",
  1083. "AssemblyInfo",
  1084. "ThreadingSafetyStatement",
  1085. "ThreadSafetyStatement",
  1086. "TypeParameters",
  1087. "Base",
  1088. "Interfaces",
  1089. "Attributes",
  1090. "Parameters",
  1091. "ReturnValue",
  1092. "Docs",
  1093. "Members",
  1094. "TypeExcluded",
  1095. };
  1096. static void OrderTypeNodes (XmlNode member, XmlNodeList children)
  1097. {
  1098. ReorderNodes (member, children, TypeNodeOrder);
  1099. }
  1100. internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
  1101. {
  1102. List<T> l = new List<T> (list);
  1103. l.Sort ();
  1104. return l;
  1105. }
  1106. private void UpdateMember (DocsNodeInfo info)
  1107. {
  1108. XmlElement me = (XmlElement) info.Node;
  1109. MemberReference mi = info.Member;
  1110. foreach (MemberFormatter f in memberFormatters) {
  1111. string element = "MemberSignature[@Language='" + f.Language + "']";
  1112. WriteElementAttribute (me, element, "Language", f.Language);
  1113. WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
  1114. }
  1115. WriteElementText(me, "MemberType", GetMemberType(mi));
  1116. if (!no_assembly_versions) {
  1117. UpdateAssemblyVersions (me, mi, true);
  1118. }
  1119. else {
  1120. ClearElement (me, "AssemblyInfo");
  1121. }
  1122. MakeAttributes (me, GetCustomAttributes (mi));
  1123. MakeReturnValue(me, mi);
  1124. if (mi is MethodReference) {
  1125. MethodReference mb = (MethodReference) mi;
  1126. if (mb.IsGenericMethod ())
  1127. MakeTypeParameters (me, mb.GenericParameters);
  1128. }
  1129. MakeParameters(me, mi);
  1130. string fieldValue;
  1131. if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
  1132. WriteElementText(me, "MemberValue", fieldValue);
  1133. info.Node = WriteElement (me, "Docs");
  1134. MakeDocNode (info);
  1135. OrderMemberNodes (me, me.ChildNodes);
  1136. UpdateExtensionMethods (me, info);
  1137. }
  1138. static readonly string[] MemberNodeOrder = {
  1139. "MemberSignature",
  1140. "MemberType",
  1141. "AssemblyInfo",
  1142. "Attributes",
  1143. "ReturnValue",
  1144. "TypeParameters",
  1145. "Parameters",
  1146. "MemberValue",
  1147. "Docs",
  1148. "Excluded",
  1149. "ExcludedLibrary",
  1150. "Link",
  1151. };
  1152. static void OrderMemberNodes (XmlNode member, XmlNodeList children)
  1153. {
  1154. ReorderNodes (member, children, MemberNodeOrder);
  1155. }
  1156. static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
  1157. {
  1158. MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
  1159. for (int i = 0; i < ordering.Length; ++i) {
  1160. for (int j = 0; j < children.Count; ++j) {
  1161. XmlNode c = children [j];
  1162. if (c.Name == ordering [i]) {
  1163. newChildren.Add (c);
  1164. }
  1165. }
  1166. }
  1167. if (newChildren.Count >= 0)
  1168. node.PrependChild ((XmlNode) newChildren [0]);
  1169. for (int i = 1; i < newChildren.Count; ++i) {
  1170. XmlNode prev = (XmlNode) newChildren [i-1];
  1171. XmlNode cur = (XmlNode) newChildren [i];
  1172. node.RemoveChild (cur);
  1173. node.InsertAfter (cur, prev);
  1174. }
  1175. }
  1176. IEnumerable<string> GetCustomAttributes (MemberReference mi)
  1177. {
  1178. IEnumerable<string> attrs = Enumerable.Empty<string>();
  1179. ICustomAttributeProvider p = mi as ICustomAttributeProvider;
  1180. if (p != null)
  1181. attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
  1182. PropertyDefinition pd = mi as PropertyDefinition;
  1183. if (pd != null) {
  1184. if (pd.GetMethod != null)
  1185. attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
  1186. if (pd.SetMethod != null)
  1187. attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
  1188. }
  1189. EventDefinition ed = mi as EventDefinition;
  1190. if (ed != null) {
  1191. if (ed.AddMethod != null)
  1192. attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
  1193. if (ed.RemoveMethod != null)
  1194. attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
  1195. }
  1196. return attrs;
  1197. }
  1198. IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
  1199. {
  1200. foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
  1201. TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
  1202. if (attrType != null && !IsPublic (attrType))
  1203. continue;
  1204. if (slashdocFormatter.GetName (attribute.AttributeType) == null)
  1205. continue;
  1206. if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
  1207. continue;
  1208. StringList fields = new StringList ();
  1209. for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
  1210. CustomAttributeArgument argument = attribute.ConstructorArguments [i];
  1211. fields.Add (MakeAttributesValueString (
  1212. argument.Value,
  1213. argument.Type));
  1214. }
  1215. var namedArgs =
  1216. (from namedArg in attribute.Fields
  1217. select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
  1218. .Concat (
  1219. (from namedArg in attribute.Properties
  1220. select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
  1221. .OrderBy (v => v.Name);
  1222. foreach (var d in namedArgs)
  1223. fields.Add (string.Format ("{0}={1}", d.Name,
  1224. MakeAttributesValueString (d.Value, d.Type)));
  1225. string a2 = String.Join(", ", fields.ToArray ());
  1226. if (a2 != "") a2 = "(" + a2 + ")";
  1227. string name = attribute.GetDeclaringType();
  1228. if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
  1229. yield return prefix + name + a2;
  1230. }
  1231. }
  1232. static readonly string[] ValidExtensionMembers = {
  1233. "Docs",
  1234. "MemberSignature",
  1235. "MemberType",
  1236. "Parameters",
  1237. "ReturnValue",
  1238. "TypeParameters",
  1239. };
  1240. static readonly string[] ValidExtensionDocMembers = {
  1241. "param",
  1242. "summary",
  1243. "typeparam",
  1244. };
  1245. private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
  1246. {
  1247. MethodDefinition me = info.Member as MethodDefinition;
  1248. if (me == null)
  1249. return;
  1250. if (info.Parameters.Count < 1)
  1251. return;
  1252. if (!DocUtils.IsExtensionMethod (me))
  1253. return;
  1254. XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
  1255. XmlNode member = e.CloneNode (true);
  1256. em.AppendChild (member);
  1257. RemoveExcept (member, ValidExtensionMembers);
  1258. RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
  1259. WriteElementText (member, "MemberType", "ExtensionMethod");
  1260. XmlElement link = member.OwnerDocument.CreateElement ("Link");
  1261. link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
  1262. link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
  1263. member.AppendChild (link);
  1264. AddTargets (em, info);
  1265. extensionMethods.Add (em);
  1266. }
  1267. private static void RemoveExcept (XmlNode node, string[] except)
  1268. {
  1269. if (node == null)
  1270. return;
  1271. MyXmlNodeList remove = null;
  1272. foreach (XmlNode n in node.ChildNodes) {
  1273. if (Array.BinarySearch (except, n.Name) < 0) {
  1274. if (remove == null)
  1275. remove = new MyXmlNodeList ();
  1276. remove.Add (n);
  1277. }
  1278. }
  1279. if (remove != null)
  1280. foreach (XmlNode n in remove)
  1281. node.RemoveChild (n);
  1282. }
  1283. private static void AddTargets (XmlNode member, DocsNodeInfo info)
  1284. {
  1285. XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
  1286. member.PrependChild (targets);
  1287. if (!(info.Parameters [0].ParameterType is GenericParameter)) {
  1288. AppendElementAttributeText (targets, "Target", "Type",
  1289. slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
  1290. }
  1291. else {
  1292. GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
  1293. IList<TypeReference> constraints = gp.Constraints;
  1294. if (constraints.Count == 0)
  1295. AppendElementAttributeText (targets, "Target", "Type", "System.Object");
  1296. else
  1297. foreach (TypeReference c in constraints)
  1298. AppendElementAttributeText(targets, "Target", "Type",
  1299. slashdocFormatter.GetDeclaration (c));
  1300. }
  1301. }
  1302. private static bool GetFieldConstValue (FieldDefinition field, out string value)
  1303. {
  1304. value = null;
  1305. TypeDefinition type = field.DeclaringType.Resolve ();
  1306. if (type != null && type.IsEnum) return false;
  1307. if (type != null && type.IsGenericType ()) return false;
  1308. if (!field.HasConstant)
  1309. return false;
  1310. if (field.IsLiteral) {
  1311. object val = field.Constant;
  1312. if (val == null) value = "null";
  1313. else if (val is Enum) value = val.ToString();
  1314. else if (val is IFormattable) {
  1315. value = ((IFormattable)val).ToString();
  1316. if (val is string)
  1317. value = "\"" + value + "\"";
  1318. }
  1319. if (value != null && value != "")
  1320. return true;
  1321. }
  1322. return false;
  1323. }
  1324. // XML HELPER FUNCTIONS
  1325. internal static XmlElement WriteElement(XmlNode parent, string element) {
  1326. XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
  1327. if (ret == null) {
  1328. string[] path = element.Split('/');
  1329. foreach (string p in path) {
  1330. ret = (XmlElement)parent.SelectSingleNode(p);
  1331. if (ret == null) {
  1332. string ename = p;
  1333. if (ename.IndexOf('[') >= 0) // strip off XPath predicate
  1334. ename = ename.Substring(0, ename.IndexOf('['));
  1335. ret = parent.OwnerDocument.CreateElement(ename);
  1336. parent.AppendChild(ret);
  1337. parent = ret;
  1338. } else {
  1339. parent = ret;
  1340. }
  1341. }
  1342. }
  1343. return ret;
  1344. }
  1345. private static void WriteElementText(XmlNode parent, string element, string value) {
  1346. XmlElement node = WriteElement(parent, element);
  1347. node.InnerText = value;
  1348. }
  1349. static XmlElement AppendElementText (XmlNode parent, string element, string value)
  1350. {
  1351. XmlElement n = parent.OwnerDocument.CreateElement (element);
  1352. parent.AppendChild (n);
  1353. n.InnerText = value;
  1354. return n;
  1355. }
  1356. static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
  1357. {
  1358. XmlElement n = parent.OwnerDocument.CreateElement (element);
  1359. parent.AppendChild (n);
  1360. n.SetAttribute (attribute, value);
  1361. return n;
  1362. }
  1363. internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
  1364. {
  1365. XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
  1366. dest.AppendChild (copy);
  1367. return copy;
  1368. }
  1369. private static void WriteElementInitialText(XmlElement parent, string element, string value) {
  1370. XmlElement node = (XmlElement)parent.SelectSingleNode(element);
  1371. if (node != null)
  1372. return;
  1373. node = WriteElement(parent, element);
  1374. node.InnerText = value;
  1375. }
  1376. private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
  1377. XmlElement node = WriteElement(parent, element);
  1378. if (node.GetAttribute(attribute) == value) return;
  1379. node.SetAttribute(attribute, value);
  1380. }
  1381. internal static void ClearElement(XmlElement parent, string name) {
  1382. XmlElement node = (XmlElement)parent.SelectSingleNode(name);
  1383. if (node != null)
  1384. parent.RemoveChild(node);
  1385. }
  1386. // DOCUMENTATION HELPER FUNCTIONS
  1387. private void MakeDocNode (DocsNodeInfo info)
  1388. {
  1389. List<GenericParameter> genericParams = info.GenericParameters;
  1390. IList<ParameterDefinition> parameters = info.Parameters;
  1391. TypeReference returntype = info.ReturnType;
  1392. bool returnisreturn = info.ReturnIsReturn;
  1393. XmlElement e = info.Node;
  1394. bool addremarks = info.AddRemarks;
  1395. WriteElementInitialText(e, "summary", "To be added.");
  1396. if (parameters != null) {
  1397. string[] values = new string [parameters.Count];
  1398. for (int i = 0; i < values.Length; ++i)
  1399. values [i] = parameters [i].Name;
  1400. UpdateParameters (e, "param", values);
  1401. }
  1402. if (genericParams != null) {
  1403. string[] values = new string [genericParams.Count];
  1404. for (int i = 0; i < values.Length; ++i)
  1405. values [i] = genericParams [i].Name;
  1406. UpdateParameters (e, "typeparam", values);
  1407. }
  1408. string retnodename = null;
  1409. if (returntype != null && returntype.FullName != "System.Void") { // FIXME
  1410. retnodename = returnisreturn ? "returns" : "value";
  1411. string retnodename_other = !returnisreturn ? "returns" : "value";
  1412. // If it has a returns node instead of a value node, change its name.
  1413. XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
  1414. if (retother != null) {
  1415. XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
  1416. foreach (XmlNode node in retother)
  1417. retnode.AppendChild(node.CloneNode(true));
  1418. e.ReplaceChild(retnode, retother);
  1419. } else {
  1420. WriteElementInitialText(e, retnodename, "To be added.");
  1421. }
  1422. } else {
  1423. ClearElement(e, "returns");
  1424. ClearElement(e, "value");
  1425. }
  1426. if (addremarks)
  1427. WriteElementInitialText(e, "remarks", "To be added.");
  1428. if (exceptions.HasValue && info.Member != null &&
  1429. (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
  1430. UpdateExceptions (e, info.Member);
  1431. }
  1432. foreach (DocumentationImporter importer in importers)
  1433. importer.ImportDocumentation (info);
  1434. OrderDocsNodes (e, e.ChildNodes);
  1435. NormalizeWhitespace(e);
  1436. }
  1437. static readonly string[] DocsNodeOrder = {
  1438. "typeparam", "param", "summary", "returns", "value", "remarks",
  1439. };
  1440. private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
  1441. {
  1442. ReorderNodes (docs, children, DocsNodeOrder);
  1443. }
  1444. private void UpdateParameters (XmlElement e, string element, string[] values)
  1445. {
  1446. if (values != null) {
  1447. XmlNode[] paramnodes = new XmlNode[values.Length];
  1448. // Some documentation had param nodes with leading spaces.
  1449. foreach (XmlElement paramnode in e.SelectNodes(element)){
  1450. paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
  1451. }
  1452. // If a member has only one parameter, we can track changes to
  1453. // the name of the parameter easily.
  1454. if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
  1455. UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
  1456. }
  1457. bool reinsert = false;
  1458. // Pick out existing and still-valid param nodes, and
  1459. // create nodes for parameters not in the file.
  1460. Hashtable seenParams = new Hashtable();
  1461. for (int pi = 0; pi < values.Length; pi++) {
  1462. string p = values [pi];
  1463. seenParams[p] = pi;
  1464. paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
  1465. if (paramnodes[pi] != null) continue;
  1466. XmlElement pe = e.OwnerDocument.CreateElement(element);
  1467. pe.SetAttribute("name", p);
  1468. pe.InnerText = "To be added.";
  1469. paramnodes[pi] = pe;
  1470. reinsert = true;
  1471. }
  1472. // Remove parameters that no longer exist and check all params are in the right order.
  1473. int idx = 0;
  1474. MyXmlNodeList todelete = new MyXmlNodeList ();
  1475. foreach (XmlElement paramnode in e.SelectNodes(element)) {
  1476. string name = paramnode.GetAttribute("name");
  1477. if (!seenParams.ContainsKey(name)) {
  1478. if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
  1479. Warning ("The following param node can only be deleted if the --delete option is given: ");
  1480. if (e.ParentNode == e.OwnerDocument.DocumentElement) {
  1481. // delegate type
  1482. Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
  1483. e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
  1484. name);
  1485. }
  1486. else {
  1487. Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
  1488. e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
  1489. e.ParentNode.Attributes ["MemberName"].Value,
  1490. name);
  1491. }
  1492. Warning ("\tValue={0}", paramnode.OuterXml);
  1493. } else {
  1494. todelete.Add (paramnode);
  1495. }
  1496. continue;
  1497. }
  1498. if ((int)seenParams[name] != idx)
  1499. reinsert = true;
  1500. idx++;
  1501. }
  1502. foreach (XmlNode n in todelete) {
  1503. n.ParentNode.RemoveChild (n);
  1504. }
  1505. // Re-insert the parameter nodes at the top of the doc section.
  1506. if (reinsert)
  1507. for (int pi = values.Length-1; pi >= 0; pi--)
  1508. e.PrependChild(paramnodes[pi]);
  1509. } else {
  1510. // Clear all existing param nodes
  1511. foreach (XmlNode paramnode in e.SelectNodes(element)) {
  1512. if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
  1513. Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
  1514. Console.WriteLine(paramnode.OuterXml);
  1515. } else {
  1516. paramnode.ParentNode.RemoveChild(paramnode);
  1517. }
  1518. }
  1519. }
  1520. }
  1521. private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
  1522. {
  1523. string existingName = pe.GetAttribute ("name");
  1524. pe.SetAttribute ("name", newName);
  1525. if (existingName == newName)
  1526. return;
  1527. foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
  1528. if (paramref.GetAttribute ("name").Trim () == existingName)
  1529. paramref.SetAttribute ("name", newName);
  1530. }
  1531. class CrefComparer : XmlNodeComparer {
  1532. public CrefComparer ()
  1533. {
  1534. }
  1535. public override int Compare (XmlNode x, XmlNode y)
  1536. {
  1537. string xType = x.Attributes ["cref"].Value;
  1538. string yType = y.Attributes ["cref"].Value;
  1539. string xNamespace = GetNamespace (xType);
  1540. string yNamespace = GetNamespace (yType);
  1541. int c = xNamespace.CompareTo (yNamespace);
  1542. if (c != 0)
  1543. return c;
  1544. return xType.CompareTo (yType);
  1545. }
  1546. static string GetNamespace (string type)
  1547. {
  1548. int n = type.LastIndexOf ('.');
  1549. if (n >= 0)
  1550. return type.Substring (0, n);
  1551. return string.Empty;
  1552. }
  1553. }
  1554. private void UpdateExceptions (XmlNode docs, MemberReference member)
  1555. {
  1556. foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
  1557. string cref = slashdocFormatter.GetDeclaration (source.Exception);
  1558. var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
  1559. if (node != null)
  1560. continue;
  1561. XmlElement e = docs.OwnerDocument.CreateElement ("exception");
  1562. e.SetAttribute ("cref", cref);
  1563. e.InnerXml = "To be added; from: <see cref=\"" +
  1564. string.Join ("\" />, <see cref=\"",
  1565. source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
  1566. .ToArray ()) +
  1567. "\" />";
  1568. docs.AppendChild (e);
  1569. }
  1570. SortXmlNodes (docs, docs.SelectNodes ("exception"),
  1571. new CrefComparer ());
  1572. }
  1573. private static void NormalizeWhitespace(XmlElement e) {
  1574. // Remove all text and whitespace nodes from the element so it
  1575. // is outputted with nice indentation and no blank lines.
  1576. ArrayList deleteNodes = new ArrayList();
  1577. foreach (XmlNode n in e)
  1578. if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
  1579. deleteNodes.Add(n);
  1580. foreach (XmlNode n in deleteNodes)
  1581. n.ParentNode.RemoveChild(n);
  1582. }
  1583. private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
  1584. {
  1585. TypeDefinition type = member as TypeDefinition;
  1586. if (type == null)
  1587. type = member.DeclaringType as TypeDefinition;
  1588. return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
  1589. }
  1590. private static string GetAssemblyVersion (AssemblyDefinition assembly)
  1591. {
  1592. return assembly.Name.Version.ToString();
  1593. }
  1594. private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
  1595. {
  1596. XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
  1597. if (e == null) {
  1598. e = root.OwnerDocument.CreateElement("AssemblyInfo");
  1599. root.AppendChild(e);
  1600. }
  1601. List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
  1602. .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
  1603. .ToList ();
  1604. // matches.Count > 0 && add: ignore -- already present
  1605. if (matches.Count > 0 && !add) {
  1606. foreach (XmlNode c in matches)
  1607. e.RemoveChild (c);
  1608. }
  1609. else if (matches.Count == 0 && add) {
  1610. foreach (string sv in assemblyVersions) {
  1611. XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
  1612. c.InnerText = sv;
  1613. e.AppendChild(c);
  1614. }
  1615. }
  1616. // matches.Count == 0 && !add: ignore -- already not present
  1617. XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
  1618. SortXmlNodes (e, avs, new VersionComparer ());
  1619. return avs.Count != 0;
  1620. }
  1621. // FIXME: get TypeReferences instead of string comparison?
  1622. private static string[] IgnorableAttributes = {
  1623. // Security related attributes
  1624. "System.Reflection.AssemblyKeyFileAttribute",
  1625. "System.Reflection.AssemblyDelaySignAttribute",
  1626. // Present in @RefType
  1627. "System.Runtime.InteropServices.OutAttribute",
  1628. // For naming the indexer to use when not using indexers
  1629. "System.Reflection.DefaultMemberAttribute",
  1630. // for decimal constants
  1631. "System.Runtime.CompilerServices.DecimalConstantAttribute",
  1632. // compiler generated code
  1633. "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
  1634. // more compiler generated code, e.g. iterator methods
  1635. "System.Diagnostics.DebuggerHiddenAttribute",
  1636. "System.Runtime.CompilerServices.FixedBufferAttribute",
  1637. "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
  1638. // extension methods
  1639. "System.Runtime.CompilerServices.ExtensionAttribute",
  1640. // Used to differentiate 'object' from C#4 'dynamic'
  1641. "System.Runtime.CompilerServices.DynamicAttribute",
  1642. };
  1643. private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
  1644. {
  1645. if (!attributes.Any ()) {
  1646. ClearElement (root, "Attributes");
  1647. return;
  1648. }
  1649. XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
  1650. if (e != null)
  1651. e.RemoveAll();
  1652. else if (e == null)
  1653. e = root.OwnerDocument.CreateElement("Attributes");
  1654. foreach (string attribute in attributes) {
  1655. XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
  1656. e.AppendChild(ae);
  1657. WriteElementText(ae, "AttributeName", attribute);
  1658. }
  1659. if (e.ParentNode == null)
  1660. root.AppendChild(e);
  1661. NormalizeWhitespace(e);
  1662. }
  1663. public static string MakeAttributesValueString (object v, TypeReference valueType)
  1664. {
  1665. if (v == null)
  1666. return "null";
  1667. if (valueType.FullName == "System.Type")
  1668. return "typeof(" + v.ToString () + ")";
  1669. if (valueType.FullName == "System.String")
  1670. return "\"" + v.ToString () + "\"";
  1671. if (valueType.FullName == "System.Char")
  1672. return "'" + v.ToString () + "'";
  1673. if (v is Boolean)
  1674. return (bool)v ? "true" : "false";
  1675. TypeDefinition valueDef = valueType.Resolve ();
  1676. if (valueDef == null || !valueDef.IsEnum)
  1677. return v.ToString ();
  1678. string typename = GetDocTypeFullName (valueType);
  1679. var values = GetEnumerationValues (valueDef);
  1680. long c = ToInt64 (v);
  1681. if (values.ContainsKey (c))
  1682. return typename + "." + values [c];
  1683. if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
  1684. return string.Join (" | ",
  1685. (from i in values.Keys
  1686. where (c & i) != 0
  1687. select typename + "." + values [i])
  1688. .DefaultIfEmpty (v.ToString ()).ToArray ());
  1689. }
  1690. return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
  1691. }
  1692. private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
  1693. {
  1694. var values = new Dictionary<long, string> ();
  1695. foreach (var f in
  1696. (from f in type.Fields
  1697. where !(f.IsRuntimeSpecialName || f.IsSpecialName)
  1698. select f)) {
  1699. values [ToInt64 (f.Constant)] = f.Name;
  1700. }
  1701. return values;
  1702. }
  1703. static long ToInt64 (object value)
  1704. {
  1705. if (value is ulong)
  1706. return (long) (ulong) value;
  1707. return Convert.ToInt64 (value);
  1708. }
  1709. private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
  1710. {
  1711. XmlElement e = WriteElement(root, "Parameters");
  1712. e.RemoveAll();
  1713. foreach (ParameterDefinition p in parameters) {
  1714. XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
  1715. e.AppendChild(pe);
  1716. pe.SetAttribute("Name", p.Name);
  1717. pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
  1718. if (p.ParameterType is ByReferenceType) {
  1719. if (p.IsOut) pe.SetAttribute("RefType", "out");
  1720. else pe.SetAttribute("RefType", "ref");
  1721. }
  1722. MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
  1723. }
  1724. }
  1725. private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
  1726. {
  1727. if (typeParams == null || typeParams.Count == 0) {
  1728. XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
  1729. if (f != null)
  1730. root.RemoveChild (f);
  1731. return;
  1732. }
  1733. XmlElement e = WriteElement(root, "TypeParameters");
  1734. e.RemoveAll();
  1735. foreach (GenericParameter t in typeParams) {
  1736. XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
  1737. e.AppendChild(pe);
  1738. pe.SetAttribute("Name", t.Name);
  1739. MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
  1740. XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
  1741. IList<TypeReference> constraints = t.Constraints;
  1742. GenericParameterAttributes attrs = t.Attributes;
  1743. if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
  1744. if (ce != null)
  1745. e.RemoveChild (ce);
  1746. continue;
  1747. }
  1748. if (ce != null)
  1749. ce.RemoveAll();
  1750. else {
  1751. ce = root.OwnerDocument.CreateElement ("Constraints");
  1752. }
  1753. pe.AppendChild (ce);
  1754. if ((attrs & GenericParameterAttributes.Contravariant) != 0)
  1755. AppendElementText (ce, "ParameterAttribute", "Contravariant");
  1756. if ((attrs & GenericParameterAttributes.Covariant) != 0)
  1757. AppendElementText (ce, "ParameterAttribute", "Covariant");
  1758. if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
  1759. AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
  1760. if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
  1761. AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
  1762. if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
  1763. AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
  1764. foreach (TypeReference c in constraints) {
  1765. TypeDefinition cd = c.Resolve ();
  1766. AppendElementText (ce,
  1767. (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
  1768. GetDocTypeFullName (c));
  1769. }
  1770. }
  1771. }
  1772. private void MakeParameters (XmlElement root, MemberReference mi)
  1773. {
  1774. if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
  1775. MakeParameters (root, ((MethodDefinition)mi).Parameters);
  1776. else if (mi is MethodDefinition) {
  1777. MethodDefinition mb = (MethodDefinition) mi;
  1778. IList<ParameterDefinition> parameters = mb.Parameters;
  1779. MakeParameters(root, parameters);
  1780. if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
  1781. XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
  1782. p.SetAttribute ("RefType", "this");
  1783. }
  1784. }
  1785. else if (mi is PropertyDefinition) {
  1786. IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
  1787. if (parameters.Count > 0)
  1788. MakeParameters(root, parameters);
  1789. else
  1790. return;
  1791. }
  1792. else if (mi is FieldDefinition) return;
  1793. else if (mi is EventDefinition) return;
  1794. else throw new ArgumentException();
  1795. }
  1796. internal static string GetDocParameterType (TypeReference type)
  1797. {
  1798. return GetDocTypeFullName (type).Replace ("@", "&");
  1799. }
  1800. private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
  1801. {
  1802. XmlElement e = WriteElement(root, "ReturnValue");
  1803. e.RemoveAll();
  1804. WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
  1805. if (attributes != null)
  1806. MakeAttributes(e, GetCustomAttributes (attributes, ""));
  1807. }
  1808. private void MakeReturnValue (XmlElement root, MemberReference mi)
  1809. {
  1810. if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
  1811. return;
  1812. else if (mi is MethodDefinition)
  1813. MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
  1814. else if (mi is PropertyDefinition)
  1815. MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
  1816. else if (mi is FieldDefinition)
  1817. MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
  1818. else if (mi is EventDefinition)
  1819. MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
  1820. else
  1821. throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
  1822. }
  1823. private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
  1824. {
  1825. MemberReference mi = info.Member;
  1826. if (mi is TypeDefinition) return null;
  1827. string sigs = memberFormatters [0].GetDeclaration (mi);
  1828. if (sigs == null) return null; // not publicly visible
  1829. // no documentation for property/event accessors. Is there a better way of doing this?
  1830. if (mi.Name.StartsWith("get_")) return null;
  1831. if (mi.Name.StartsWith("set_")) return null;
  1832. if (mi.Name.StartsWith("add_")) return null;
  1833. if (mi.Name.StartsWith("remove_")) return null;
  1834. if (mi.Name.StartsWith("raise_")) return null;
  1835. XmlElement me = doc.CreateElement("Member");
  1836. me.SetAttribute("MemberName", GetMemberName (mi));
  1837. info.Node = me;
  1838. UpdateMember(info);
  1839. if (exceptions.HasValue &&
  1840. (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
  1841. UpdateExceptions (info.Node, info.Member);
  1842. if (since != null) {
  1843. XmlNode docs = me.SelectSingleNode("Docs");
  1844. docs.AppendChild (CreateSinceNode (doc));
  1845. }
  1846. return me;
  1847. }
  1848. internal static string GetMemberName (MemberReference mi)
  1849. {
  1850. MethodDefinition mb = mi as MethodDefinition;
  1851. if (mb == null) {
  1852. PropertyDefinition pi = mi as PropertyDefinition;
  1853. if (pi == null)
  1854. return mi.Name;
  1855. return DocUtils.GetPropertyName (pi);
  1856. }
  1857. StringBuilder sb = new StringBuilder (mi.Name.Length);
  1858. if (!DocUtils.IsExplicitlyImplemented (mb))
  1859. sb.Append (mi.Name);
  1860. else {
  1861. TypeReference iface;
  1862. MethodReference ifaceMethod;
  1863. DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
  1864. sb.Append (GetDocTypeFullName (iface));
  1865. sb.Append ('.');
  1866. sb.Append (ifaceMethod.Name);
  1867. }
  1868. if (mb.IsGenericMethod ()) {
  1869. IList<GenericParameter> typeParams = mb.GenericParameters;
  1870. if (typeParams.Count > 0) {
  1871. sb.Append ("<");
  1872. sb.Append (typeParams [0].Name);
  1873. for (int i = 1; i < typeParams.Count; ++i)
  1874. sb.Append (",").Append (typeParams [i].Name);
  1875. sb.Append (">");
  1876. }
  1877. }
  1878. return sb.ToString ();
  1879. }
  1880. /// SIGNATURE GENERATION FUNCTIONS
  1881. internal static bool IsPrivate (MemberReference mi)
  1882. {
  1883. return memberFormatters [0].GetDeclaration (mi) == null;
  1884. }
  1885. internal static string GetMemberType (MemberReference mi)
  1886. {
  1887. if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
  1888. return "Constructor";
  1889. if (mi is MethodDefinition)
  1890. return "Method";
  1891. if (mi is PropertyDefinition)
  1892. return "Property";
  1893. if (mi is FieldDefinition)
  1894. return "Field";
  1895. if (mi is EventDefinition)
  1896. return "Event";
  1897. throw new ArgumentException();
  1898. }
  1899. private static string GetDocTypeName (TypeReference type)
  1900. {
  1901. return docTypeFormatter.GetName (type);
  1902. }
  1903. internal static string GetDocTypeFullName (TypeReference type)
  1904. {
  1905. return DocTypeFullMemberFormatter.Default.GetName (type);
  1906. }
  1907. internal static string GetXPathForMember (DocumentationMember member)
  1908. {
  1909. StringBuilder xpath = new StringBuilder ();
  1910. xpath.Append ("//Members/Member[@MemberName=\"")
  1911. .Append (member.MemberName)
  1912. .Append ("\"]");
  1913. if (member.Parameters != null && member.Parameters.Count > 0) {
  1914. xpath.Append ("/Parameters[count(Parameter) = ")
  1915. .Append (member.Parameters.Count);
  1916. for (int i = 0; i < member.Parameters.Count; ++i) {
  1917. xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
  1918. xpath.Append (member.Parameters [i]);
  1919. xpath.Append ("\"");
  1920. }
  1921. xpath.Append ("]/..");
  1922. }
  1923. return xpath.ToString ();
  1924. }
  1925. public static string GetXPathForMember (XPathNavigator member)
  1926. {
  1927. StringBuilder xpath = new StringBuilder ();
  1928. xpath.Append ("//Type[@FullName=\"")
  1929. .Append (member.SelectSingleNode ("../../@FullName").Value)
  1930. .Append ("\"]/");
  1931. xpath.Append ("Members/Member[@MemberName=\"")
  1932. .Append (member.SelectSingleNode ("@MemberName").Value)
  1933. .Append ("\"]");
  1934. XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
  1935. if (parameters.Count > 0) {
  1936. xpath.Append ("/Parameters[count(Parameter) = ")
  1937. .Append (parameters.Count);
  1938. int i = 0;
  1939. while (parameters.MoveNext ()) {
  1940. ++i;
  1941. xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
  1942. xpath.Append (parameters.Current.Value);
  1943. xpath.Append ("\"");
  1944. }
  1945. xpath.Append ("]/..");
  1946. }
  1947. return xpath.ToString ();
  1948. }
  1949. public static string GetXPathForMember (MemberReference member)
  1950. {
  1951. StringBuilder xpath = new StringBuilder ();
  1952. xpath.Append ("//Type[@FullName=\"")
  1953. .Append (member.DeclaringType.FullName)
  1954. .Append ("\"]/");
  1955. xpath.Append ("Members/Member[@MemberName=\"")
  1956. .Append (GetMemberName (member))
  1957. .Append ("\"]");
  1958. IList<ParameterDefinition> parameters = null;
  1959. if (member is MethodDefinition)
  1960. parameters = ((MethodDefinition) member).Parameters;
  1961. else if (member is PropertyDefinition) {
  1962. parameters = ((PropertyDefinition) member).Parameters;
  1963. }
  1964. if (parameters != null && parameters.Count > 0) {
  1965. xpath.Append ("/Parameters[count(Parameter) = ")
  1966. .Append (parameters.Count);
  1967. for (int i = 0; i < parameters.Count; ++i) {
  1968. xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
  1969. xpath.Append (GetDocParameterType (parameters [i].ParameterType));
  1970. xpath.Append ("\"");
  1971. }
  1972. xpath.Append ("]/..");
  1973. }
  1974. return xpath.ToString ();
  1975. }
  1976. }
  1977. static class CecilExtensions {
  1978. public static string GetDeclaringType(this CustomAttribute attribute)
  1979. {
  1980. return attribute.Constructor.DeclaringType.FullName;
  1981. }
  1982. public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
  1983. {
  1984. foreach (var c in type.Methods.Where (m => m.IsConstructor))
  1985. yield return (MemberReference) c;
  1986. foreach (var e in type.Events)
  1987. yield return (MemberReference) e;
  1988. foreach (var f in type.Fields)
  1989. yield return (MemberReference) f;
  1990. foreach (var m in type.Methods.Where (m => !m.IsConstructor))
  1991. yield return (MemberReference) m;
  1992. foreach (var t in type.NestedTypes)
  1993. yield return (MemberReference) t;
  1994. foreach (var p in type.Properties)
  1995. yield return (MemberReference) p;
  1996. }
  1997. public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
  1998. {
  1999. return GetMembers (type).Where (m => m.Name == member);
  2000. }
  2001. public static MemberReference GetMember (this TypeDefinition type, string member)
  2002. {
  2003. return GetMembers (type, member).EnsureZeroOrOne ();
  2004. }
  2005. static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
  2006. {
  2007. if (source.Count () > 1)
  2008. throw new InvalidOperationException ("too many matches");
  2009. return source.FirstOrDefault ();
  2010. }
  2011. public static MethodDefinition GetMethod (this TypeDefinition type, string method)
  2012. {
  2013. return type.Methods
  2014. .Where (m => m.Name == method)
  2015. .EnsureZeroOrOne ();
  2016. }
  2017. public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
  2018. {
  2019. TypeDefinition def = type as TypeDefinition;
  2020. if (def == null)
  2021. return new MemberReference [0];
  2022. CustomAttribute defMemberAttr = def.CustomAttributes
  2023. .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
  2024. if (defMemberAttr == null)
  2025. return new MemberReference [0];
  2026. string name = (string) defMemberAttr.ConstructorArguments [0].Value;
  2027. return def.Properties
  2028. .Where (p => p.Name == name)
  2029. .Select (p => (MemberReference) p);
  2030. }
  2031. public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
  2032. {
  2033. return assembly.Modules.SelectMany (md => md.GetAllTypes ());
  2034. }
  2035. public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
  2036. {
  2037. return GetTypes (assembly)
  2038. .Where (td => td.FullName == type)
  2039. .EnsureZeroOrOne ();
  2040. }
  2041. public static bool IsGenericType (this TypeReference type)
  2042. {
  2043. return type.GenericParameters.Count > 0;
  2044. }
  2045. public static bool IsGenericMethod (this MethodReference method)
  2046. {
  2047. return method.GenericParameters.Count > 0;
  2048. }
  2049. public static MemberReference Resolve (this MemberReference member)
  2050. {
  2051. FieldReference fr = member as FieldReference;
  2052. if (fr != null)
  2053. return fr.Resolve ();
  2054. MethodReference mr = member as MethodReference;
  2055. if (mr != null)
  2056. return mr.Resolve ();
  2057. TypeReference tr = member as TypeReference;
  2058. if (tr != null)
  2059. return tr.Resolve ();
  2060. PropertyReference pr = member as PropertyReference;
  2061. if (pr != null)
  2062. return pr;
  2063. EventReference er = member as EventReference;
  2064. if (er != null)
  2065. return er;
  2066. throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
  2067. }
  2068. public static TypeReference GetUnderlyingType (this TypeDefinition type)
  2069. {
  2070. if (!type.IsEnum)
  2071. return type;
  2072. return type.Fields.First (f => f.Name == "value__").FieldType;
  2073. }
  2074. public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
  2075. {
  2076. return self.Types.SelectMany (t => t.GetAllTypes ());
  2077. }
  2078. static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
  2079. {
  2080. yield return self;
  2081. if (!self.HasNestedTypes)
  2082. yield break;
  2083. foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
  2084. yield return type;
  2085. }
  2086. }
  2087. static class DocUtils {
  2088. public static bool IsExplicitlyImplemented (MethodDefinition method)
  2089. {
  2090. return method.IsPrivate && method.IsFinal && method.IsVirtual;
  2091. }
  2092. public static string GetTypeDotMember (string name)
  2093. {
  2094. int startType, startMethod;
  2095. startType = startMethod = -1;
  2096. for (int i = 0; i < name.Length; ++i) {
  2097. if (name [i] == '.') {
  2098. startType = startMethod;
  2099. startMethod = i;
  2100. }
  2101. }
  2102. return name.Substring (startType+1);
  2103. }
  2104. public static string GetMember (string name)
  2105. {
  2106. int i = name.LastIndexOf ('.');
  2107. if (i == -1)
  2108. return name;
  2109. return name.Substring (i+1);
  2110. }
  2111. public static void GetInfoForExplicitlyImplementedMethod (
  2112. MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
  2113. {
  2114. iface = null;
  2115. ifaceMethod = null;
  2116. if (method.Overrides.Count != 1)
  2117. throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
  2118. iface = method.Overrides [0].DeclaringType;
  2119. ifaceMethod = method.Overrides [0];
  2120. }
  2121. public static string GetPropertyName (PropertyDefinition pi)
  2122. {
  2123. // Issue: (g)mcs-generated assemblies that explicitly implement
  2124. // properties don't specify the full namespace, just the
  2125. // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
  2126. MethodDefinition method = pi.GetMethod;
  2127. if (method == null)
  2128. method = pi.SetMethod;
  2129. if (!IsExplicitlyImplemented (method))
  2130. return pi.Name;
  2131. // Need to determine appropriate namespace for this member.
  2132. TypeReference iface;
  2133. MethodReference ifaceMethod;
  2134. GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
  2135. return string.Join (".", new string[]{
  2136. DocTypeFullMemberFormatter.Default.GetName (iface),
  2137. GetMember (pi.Name)});
  2138. }
  2139. public static string GetNamespace (TypeReference type)
  2140. {
  2141. if (type.GetElementType ().IsNested)
  2142. type = type.GetElementType ();
  2143. while (type != null && type.IsNested)
  2144. type = type.DeclaringType;
  2145. if (type == null)
  2146. return string.Empty;
  2147. return type.Namespace;
  2148. }
  2149. public static string PathCombine (string dir, string path)
  2150. {
  2151. if (dir == null)
  2152. dir = "";
  2153. if (path == null)
  2154. path = "";
  2155. return Path.Combine (dir, path);
  2156. }
  2157. public static bool IsExtensionMethod (MethodDefinition method)
  2158. {
  2159. return
  2160. method.CustomAttributes
  2161. .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
  2162. && method.DeclaringType.CustomAttributes
  2163. .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
  2164. }
  2165. public static bool IsDelegate (TypeDefinition type)
  2166. {
  2167. TypeReference baseRef = type.BaseType;
  2168. if (baseRef == null)
  2169. return false;
  2170. return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
  2171. baseRef.FullName == "System.MulticastDelegate";
  2172. }
  2173. public static List<TypeReference> GetDeclaringTypes (TypeReference type)
  2174. {
  2175. List<TypeReference> decls = new List<TypeReference> ();
  2176. decls.Add (type);
  2177. while (type.DeclaringType != null) {
  2178. decls.Add (type.DeclaringType);
  2179. type = type.DeclaringType;
  2180. }
  2181. decls.Reverse ();
  2182. return decls;
  2183. }
  2184. public static int GetGenericArgumentCount (TypeReference type)
  2185. {
  2186. GenericInstanceType inst = type as GenericInstanceType;
  2187. return inst != null
  2188. ? inst.GenericArguments.Count
  2189. : type.GenericParameters.Count;
  2190. }
  2191. public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
  2192. {
  2193. HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
  2194. List<TypeReference> userInterfaces = new List<TypeReference> ();
  2195. foreach (TypeReference iface in type.Interfaces) {
  2196. TypeReference lookup = iface.Resolve () ?? iface;
  2197. if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
  2198. userInterfaces.Add (iface);
  2199. }
  2200. return userInterfaces;
  2201. }
  2202. private static string GetQualifiedTypeName (TypeReference type)
  2203. {
  2204. return "[" + type.Scope.Name + "]" + type.FullName;
  2205. }
  2206. private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
  2207. {
  2208. HashSet<string> inheritedInterfaces = new HashSet<string> ();
  2209. Action<TypeDefinition> a = null;
  2210. a = t => {
  2211. if (t == null) return;
  2212. foreach (TypeReference r in t.Interfaces) {
  2213. inheritedInterfaces.Add (GetQualifiedTypeName (r));
  2214. a (r.Resolve ());
  2215. }
  2216. };
  2217. TypeReference baseRef = type.BaseType;
  2218. while (baseRef != null) {
  2219. TypeDefinition baseDef = baseRef.Resolve ();
  2220. if (baseDef != null) {
  2221. a (baseDef);
  2222. baseRef = baseDef.BaseType;
  2223. }
  2224. else
  2225. baseRef = null;
  2226. }
  2227. foreach (TypeReference r in type.Interfaces)
  2228. a (r.Resolve ());
  2229. return inheritedInterfaces;
  2230. }
  2231. }
  2232. class DocsNodeInfo {
  2233. public DocsNodeInfo (XmlElement node)
  2234. {
  2235. this.Node = node;
  2236. }
  2237. public DocsNodeInfo (XmlElement node, TypeDefinition type)
  2238. : this (node)
  2239. {
  2240. SetType (type);
  2241. }
  2242. public DocsNodeInfo (XmlElement node, MemberReference member)
  2243. : this (node)
  2244. {
  2245. SetMemberInfo (member);
  2246. }
  2247. void SetType (TypeDefinition type)
  2248. {
  2249. if (type == null)
  2250. throw new ArgumentNullException ("type");
  2251. Type = type;
  2252. GenericParameters = new List<GenericParameter> (type.GenericParameters);
  2253. List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
  2254. int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
  2255. for (int i = 0; i < declTypes.Count - 1; ++i) {
  2256. int remove = System.Math.Min (maxGenArgs,
  2257. DocUtils.GetGenericArgumentCount (declTypes [i]));
  2258. maxGenArgs -= remove;
  2259. while (remove-- > 0)
  2260. GenericParameters.RemoveAt (0);
  2261. }
  2262. if (DocUtils.IsDelegate (type)) {
  2263. Parameters = type.GetMethod("Invoke").Parameters;
  2264. ReturnType = type.GetMethod("Invoke").ReturnType;
  2265. ReturnIsReturn = true;
  2266. }
  2267. }
  2268. void SetMemberInfo (MemberReference member)
  2269. {
  2270. if (member == null)
  2271. throw new ArgumentNullException ("member");
  2272. ReturnIsReturn = true;
  2273. AddRemarks = true;
  2274. Member = member;
  2275. if (member is MethodReference ) {
  2276. MethodReference mr = (MethodReference) member;
  2277. Parameters = mr.Parameters;
  2278. if (mr.IsGenericMethod ()) {
  2279. GenericParameters = new List<GenericParameter> (mr.GenericParameters);
  2280. }
  2281. }
  2282. else if (member is PropertyDefinition) {
  2283. Parameters = ((PropertyDefinition) member).Parameters;
  2284. }
  2285. if (member is MethodDefinition) {
  2286. ReturnType = ((MethodDefinition) member).ReturnType;
  2287. } else if (member is PropertyDefinition) {
  2288. ReturnType = ((PropertyDefinition) member).PropertyType;
  2289. ReturnIsReturn = false;
  2290. }
  2291. // no remarks section for enum members
  2292. if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
  2293. AddRemarks = false;
  2294. }
  2295. public TypeReference ReturnType;
  2296. public List<GenericParameter> GenericParameters;
  2297. public IList<ParameterDefinition> Parameters;
  2298. public bool ReturnIsReturn;
  2299. public XmlElement Node;
  2300. public bool AddRemarks = true;
  2301. public MemberReference Member;
  2302. public TypeDefinition Type;
  2303. }
  2304. class DocumentationEnumerator {
  2305. public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
  2306. {
  2307. return GetDocumentationTypes (assembly, forTypes, null);
  2308. }
  2309. protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
  2310. {
  2311. foreach (TypeDefinition type in assembly.GetTypes()) {
  2312. if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
  2313. continue;
  2314. if (seen != null && seen.Contains (type.FullName))
  2315. continue;
  2316. yield return type;
  2317. foreach (TypeDefinition nested in type.NestedTypes)
  2318. yield return nested;
  2319. }
  2320. }
  2321. public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
  2322. {
  2323. foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
  2324. if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
  2325. oldmember.RemoveAttribute ("__monodocer-seen__");
  2326. continue;
  2327. }
  2328. MemberReference m = GetMember (type, new DocumentationMember (oldmember));
  2329. if (m == null) {
  2330. yield return new DocsNodeInfo (oldmember);
  2331. }
  2332. else {
  2333. yield return new DocsNodeInfo (oldmember, m);
  2334. }
  2335. }
  2336. }
  2337. protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
  2338. {
  2339. string membertype = member.MemberType;
  2340. string returntype = member.ReturnType;
  2341. string docName = member.MemberName;
  2342. string[] docTypeParams = GetTypeParameters (docName);
  2343. // Loop through all members in this type with the same name
  2344. foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
  2345. if (mi is TypeDefinition) continue;
  2346. if (MDocUpdater.GetMemberType(mi) != membertype) continue;
  2347. if (MDocUpdater.IsPrivate (mi))
  2348. continue;
  2349. IList<ParameterDefinition> pis = null;
  2350. string[] typeParams = null;
  2351. if (mi is MethodDefinition) {
  2352. MethodDefinition mb = (MethodDefinition) mi;
  2353. pis = mb.Parameters;
  2354. if (docTypeParams != null && mb.IsGenericMethod ()) {
  2355. IList<GenericParameter> args = mb.GenericParameters;
  2356. if (args.Count == docTypeParams.Length) {
  2357. typeParams = args.Select (p => p.Name).ToArray ();
  2358. }
  2359. }
  2360. }
  2361. else if (mi is PropertyDefinition)
  2362. pis = ((PropertyDefinition)mi).Parameters;
  2363. int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
  2364. int pcount = pis == null ? 0 : pis.Count;
  2365. if (mcount != pcount)
  2366. continue;
  2367. MethodDefinition mDef = mi as MethodDefinition;
  2368. if (mDef != null && !mDef.IsConstructor) {
  2369. // Casting operators can overload based on return type.
  2370. if (returntype != GetReplacedString (
  2371. MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
  2372. typeParams, docTypeParams)) {
  2373. continue;
  2374. }
  2375. }
  2376. if (pcount == 0)
  2377. return mi;
  2378. bool good = true;
  2379. for (int i = 0; i < pis.Count; i++) {
  2380. string paramType = GetReplacedString (
  2381. MDocUpdater.GetDocParameterType (pis [i].ParameterType),
  2382. typeParams, docTypeParams);
  2383. if (paramType != (string) member.Parameters [i]) {
  2384. good = false;
  2385. break;
  2386. }
  2387. }
  2388. if (!good) continue;
  2389. return mi;
  2390. }
  2391. return null;
  2392. }
  2393. static string[] GetTypeParameters (string docName)
  2394. {
  2395. if (docName [docName.Length-1] != '>')
  2396. return null;
  2397. StringList types = new StringList ();
  2398. int endToken = docName.Length-2;
  2399. int i = docName.Length-2;
  2400. do {
  2401. if (docName [i] == ',' || docName [i] == '<') {
  2402. types.Add (docName.Substring (i + 1, endToken - i));
  2403. endToken = i-1;
  2404. }
  2405. if (docName [i] == '<')
  2406. break;
  2407. } while (--i >= 0);
  2408. types.Reverse ();
  2409. return types.ToArray ();
  2410. }
  2411. protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
  2412. {
  2413. // need to worry about 4 forms of //@MemberName values:
  2414. // 1. "Normal" (non-generic) member names: GetEnumerator
  2415. // - Lookup as-is.
  2416. // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
  2417. // - try as-is, and try type.member (due to "kludge" for property
  2418. // support.
  2419. // 3. "Normal" Generic member names: Sort<T> (CSC)
  2420. // - need to remove generic parameters --> "Sort"
  2421. // 4. Explicitly-implemented interface members for generic interfaces:
  2422. // -- System.Collections.Generic.IEnumerable<T>.Current
  2423. // - Try as-is, and try type.member, *keeping* the generic parameters.
  2424. // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
  2425. // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
  2426. // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
  2427. // this as (1) or (2).
  2428. if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
  2429. // Cases 1 & 2
  2430. foreach (MemberReference mi in type.GetMembers (docName))
  2431. yield return mi;
  2432. if (CountChars (docName, '.') > 0)
  2433. // might be a property; try only type.member instead of
  2434. // namespace.type.member.
  2435. foreach (MemberReference mi in
  2436. type.GetMembers (DocUtils.GetTypeDotMember (docName)))
  2437. yield return mi;
  2438. yield break;
  2439. }
  2440. // cases 3 & 4
  2441. int numLt = 0;
  2442. int numDot = 0;
  2443. int startLt, startType, startMethod;
  2444. startLt = startType = startMethod = -1;
  2445. for (int i = 0; i < docName.Length; ++i) {
  2446. switch (docName [i]) {
  2447. case '<':
  2448. if (numLt == 0) {
  2449. startLt = i;
  2450. }
  2451. ++numLt;
  2452. break;
  2453. case '>':
  2454. --numLt;
  2455. if (numLt == 0 && (i + 1) < docName.Length)
  2456. // there's another character in docName, so this <...> sequence is
  2457. // probably part of a generic type -- case 4.
  2458. startLt = -1;
  2459. break;
  2460. case '.':
  2461. startType = startMethod;
  2462. startMethod = i;
  2463. ++numDot;
  2464. break;
  2465. }
  2466. }
  2467. string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
  2468. // case 3
  2469. foreach (MemberReference mi in type.GetMembers (refName))
  2470. yield return mi;
  2471. // case 4
  2472. foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
  2473. yield return mi;
  2474. // If we _still_ haven't found it, we've hit another generic naming issue:
  2475. // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
  2476. // explicitly-implemented METHOD names (not properties), e.g.
  2477. // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
  2478. // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
  2479. // which the XML docs will contain.
  2480. //
  2481. // Alas, we can't derive the Mono name from docName, so we need to iterate
  2482. // over all member names, convert them into CSC format, and compare... :-(
  2483. if (numDot == 0)
  2484. yield break;
  2485. foreach (MemberReference mi in type.GetMembers ()) {
  2486. if (MDocUpdater.GetMemberName (mi) == docName)
  2487. yield return mi;
  2488. }
  2489. }
  2490. static string GetReplacedString (string typeName, string[] from, string[] to)
  2491. {
  2492. if (from == null)
  2493. return typeName;
  2494. for (int i = 0; i < from.Length; ++i)
  2495. typeName = typeName.Replace (from [i], to [i]);
  2496. return typeName;
  2497. }
  2498. private static int CountChars (string s, char c)
  2499. {
  2500. int count = 0;
  2501. for (int i = 0; i < s.Length; ++i) {
  2502. if (s [i] == c)
  2503. ++count;
  2504. }
  2505. return count;
  2506. }
  2507. }
  2508. class EcmaDocumentationEnumerator : DocumentationEnumerator {
  2509. XmlReader ecmadocs;
  2510. MDocUpdater app;
  2511. public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
  2512. {
  2513. this.app = app;
  2514. this.ecmadocs = ecmaDocs;
  2515. }
  2516. public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
  2517. {
  2518. HashSet<string> seen = new HashSet<string> ();
  2519. return GetDocumentationTypes (assembly, forTypes, seen)
  2520. .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
  2521. }
  2522. new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
  2523. {
  2524. int typeDepth = -1;
  2525. while (ecmadocs.Read ()) {
  2526. switch (ecmadocs.Name) {
  2527. case "Type": {
  2528. if (typeDepth == -1)
  2529. typeDepth = ecmadocs.Depth;
  2530. if (ecmadocs.NodeType != XmlNodeType.Element)
  2531. continue;
  2532. if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
  2533. continue;
  2534. string typename = ecmadocs.GetAttribute ("FullName");
  2535. string typename2 = MDocUpdater.GetTypeFileName (typename);
  2536. if (forTypes != null &&
  2537. forTypes.BinarySearch (typename) < 0 &&
  2538. typename != typename2 &&
  2539. forTypes.BinarySearch (typename2) < 0)
  2540. continue;
  2541. TypeDefinition t;
  2542. if ((t = assembly.GetType (typename)) == null &&
  2543. (t = assembly.GetType (typename2)) == null)
  2544. continue;
  2545. seen.Add (typename);
  2546. if (typename != typename2)
  2547. seen.Add (typename2);
  2548. Console.WriteLine (" Import: {0}", t.FullName);
  2549. if (ecmadocs.Name != "Docs") {
  2550. int depth = ecmadocs.Depth;
  2551. while (ecmadocs.Read ()) {
  2552. if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
  2553. break;
  2554. }
  2555. }
  2556. if (!ecmadocs.IsStartElement ("Docs"))
  2557. throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
  2558. yield return t;
  2559. break;
  2560. }
  2561. default:
  2562. break;
  2563. }
  2564. }
  2565. }
  2566. public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
  2567. {
  2568. return GetMembers (basefile, type)
  2569. .Concat (base.GetDocumentationMembers (basefile, type));
  2570. }
  2571. private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
  2572. {
  2573. while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
  2574. // do nothing
  2575. }
  2576. if (ecmadocs.IsEmptyElement)
  2577. yield break;
  2578. int membersDepth = ecmadocs.Depth;
  2579. bool go = true;
  2580. while (go && ecmadocs.Read ()) {
  2581. switch (ecmadocs.Name) {
  2582. case "Member": {
  2583. if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
  2584. continue;
  2585. DocumentationMember dm = new DocumentationMember (ecmadocs);
  2586. string xp = MDocUpdater.GetXPathForMember (dm);
  2587. XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
  2588. MemberReference m;
  2589. if (oldmember == null) {
  2590. m = GetMember (type, dm);
  2591. if (m == null) {
  2592. app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
  2593. type.FullName, dm.MemberSignatures ["C#"]);
  2594. // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
  2595. continue;
  2596. }
  2597. // oldmember lookup may have failed due to type parameter renames.
  2598. // Try again.
  2599. oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
  2600. if (oldmember == null) {
  2601. XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
  2602. oldmember = basefile.CreateElement ("Member");
  2603. oldmember.SetAttribute ("MemberName", dm.MemberName);
  2604. members.AppendChild (oldmember);
  2605. foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
  2606. XmlElement ms = basefile.CreateElement ("MemberSignature");
  2607. ms.SetAttribute ("Language", key);
  2608. ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
  2609. oldmember.AppendChild (ms);
  2610. }
  2611. oldmember.SetAttribute ("__monodocer-seen__", "true");
  2612. Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
  2613. app.additions++;
  2614. }
  2615. }
  2616. else {
  2617. m = GetMember (type, new DocumentationMember (oldmember));
  2618. if (m == null) {
  2619. app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
  2620. type.FullName, dm.MemberSignatures ["C#"]);
  2621. continue;
  2622. }
  2623. oldmember.SetAttribute ("__monodocer-seen__", "true");
  2624. }
  2625. DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
  2626. if (ecmadocs.Name != "Docs")
  2627. throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
  2628. yield return node;
  2629. break;
  2630. }
  2631. case "Members":
  2632. if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
  2633. go = false;
  2634. }
  2635. break;
  2636. }
  2637. }
  2638. }
  2639. }
  2640. abstract class DocumentationImporter {
  2641. public abstract void ImportDocumentation (DocsNodeInfo info);
  2642. }
  2643. class MsxdocDocumentationImporter : DocumentationImporter {
  2644. XmlDocument slashdocs;
  2645. public MsxdocDocumentationImporter (string file)
  2646. {
  2647. var xml = File.ReadAllText (file);
  2648. // Ensure Unix line endings
  2649. xml = xml.Replace ("\r", "");
  2650. slashdocs = new XmlDocument();
  2651. slashdocs.LoadXml (xml);
  2652. }
  2653. public override void ImportDocumentation (DocsNodeInfo info)
  2654. {
  2655. XmlNode elem = GetDocs (info.Member ?? info.Type);
  2656. if (elem == null)
  2657. return;
  2658. XmlElement e = info.Node;
  2659. if (elem.SelectSingleNode("summary") != null)
  2660. MDocUpdater.ClearElement(e, "summary");
  2661. if (elem.SelectSingleNode("remarks") != null)
  2662. MDocUpdater.ClearElement(e, "remarks");
  2663. if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
  2664. MDocUpdater.ClearElement(e, "value");
  2665. MDocUpdater.ClearElement(e, "returns");
  2666. }
  2667. foreach (XmlNode child in elem.ChildNodes) {
  2668. switch (child.Name) {
  2669. case "param":
  2670. case "typeparam": {
  2671. XmlAttribute name = child.Attributes ["name"];
  2672. if (name == null)
  2673. break;
  2674. XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
  2675. if (p2 != null)
  2676. p2.InnerXml = child.InnerXml;
  2677. break;
  2678. }
  2679. // Occasionally XML documentation will use <returns/> on
  2680. // properties, so let's try to normalize things.
  2681. case "value":
  2682. case "returns": {
  2683. XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
  2684. v.InnerXml = child.InnerXml;
  2685. e.AppendChild (v);
  2686. break;
  2687. }
  2688. case "altmember":
  2689. case "exception":
  2690. case "permission": {
  2691. XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
  2692. if (cref == null)
  2693. break;
  2694. XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
  2695. if (a == null) {
  2696. a = e.OwnerDocument.CreateElement (child.Name);
  2697. a.SetAttribute ("cref", child.Attributes ["cref"].Value);
  2698. e.AppendChild (a);
  2699. }
  2700. a.InnerXml = child.InnerXml;
  2701. break;
  2702. }
  2703. case "seealso": {
  2704. XmlAttribute cref = child.Attributes ["cref"];
  2705. if (cref == null)
  2706. break;
  2707. XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
  2708. if (a == null) {
  2709. a = e.OwnerDocument.CreateElement ("altmember");
  2710. a.SetAttribute ("cref", child.Attributes ["cref"].Value);
  2711. e.AppendChild (a);
  2712. }
  2713. break;
  2714. }
  2715. default: {
  2716. bool add = true;
  2717. if (child.NodeType == XmlNodeType.Element &&
  2718. e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
  2719. add = false;
  2720. if (add)
  2721. MDocUpdater.CopyNode (child, e);
  2722. break;
  2723. }
  2724. }
  2725. }
  2726. }
  2727. private XmlNode GetDocs (MemberReference member)
  2728. {
  2729. string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
  2730. if (slashdocsig != null)
  2731. return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
  2732. return null;
  2733. }
  2734. }
  2735. class EcmaDocumentationImporter : DocumentationImporter {
  2736. XmlReader ecmadocs;
  2737. public EcmaDocumentationImporter (XmlReader ecmaDocs)
  2738. {
  2739. this.ecmadocs = ecmaDocs;
  2740. }
  2741. public override void ImportDocumentation (DocsNodeInfo info)
  2742. {
  2743. if (!ecmadocs.IsStartElement ("Docs")) {
  2744. return;
  2745. }
  2746. XmlElement e = info.Node;
  2747. int depth = ecmadocs.Depth;
  2748. ecmadocs.ReadStartElement ("Docs");
  2749. while (ecmadocs.Read ()) {
  2750. if (ecmadocs.Name == "Docs") {
  2751. if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
  2752. break;
  2753. else
  2754. throw new InvalidOperationException ("Skipped past current <Docs/> element!");
  2755. }
  2756. if (!ecmadocs.IsStartElement ())
  2757. continue;
  2758. switch (ecmadocs.Name) {
  2759. case "param":
  2760. case "typeparam": {
  2761. string name = ecmadocs.GetAttribute ("name");
  2762. if (name == null)
  2763. break;
  2764. XmlNode doc = e.SelectSingleNode (
  2765. ecmadocs.Name + "[@name='" + name + "']");
  2766. string value = ecmadocs.ReadInnerXml ();
  2767. if (doc != null)
  2768. doc.InnerXml = value.Replace ("\r", "");
  2769. break;
  2770. }
  2771. case "altmember":
  2772. case "exception":
  2773. case "permission":
  2774. case "seealso": {
  2775. string name = ecmadocs.Name;
  2776. string cref = ecmadocs.GetAttribute ("cref");
  2777. if (cref == null)
  2778. break;
  2779. XmlNode doc = e.SelectSingleNode (
  2780. ecmadocs.Name + "[@cref='" + cref + "']");
  2781. string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
  2782. if (doc != null)
  2783. doc.InnerXml = value;
  2784. else {
  2785. XmlElement n = e.OwnerDocument.CreateElement (name);
  2786. n.SetAttribute ("cref", cref);
  2787. n.InnerXml = value;
  2788. e.AppendChild (n);
  2789. }
  2790. break;
  2791. }
  2792. default: {
  2793. string name = ecmadocs.Name;
  2794. string xpath = ecmadocs.Name;
  2795. StringList attributes = new StringList (ecmadocs.AttributeCount);
  2796. if (ecmadocs.MoveToFirstAttribute ()) {
  2797. do {
  2798. attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
  2799. } while (ecmadocs.MoveToNextAttribute ());
  2800. ecmadocs.MoveToContent ();
  2801. }
  2802. if (attributes.Count > 0) {
  2803. xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
  2804. }
  2805. XmlNode doc = e.SelectSingleNode (xpath);
  2806. string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
  2807. if (doc != null) {
  2808. doc.InnerXml = value;
  2809. }
  2810. else {
  2811. XmlElement n = e.OwnerDocument.CreateElement (name);
  2812. n.InnerXml = value;
  2813. foreach (string a in attributes) {
  2814. int eq = a.IndexOf ('=');
  2815. n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
  2816. }
  2817. e.AppendChild (n);
  2818. }
  2819. break;
  2820. }
  2821. }
  2822. }
  2823. }
  2824. }
  2825. class DocumentationMember {
  2826. public StringToStringMap MemberSignatures = new StringToStringMap ();
  2827. public string ReturnType;
  2828. public StringList Parameters;
  2829. public string MemberName;
  2830. public string MemberType;
  2831. public DocumentationMember (XmlReader reader)
  2832. {
  2833. MemberName = reader.GetAttribute ("MemberName");
  2834. int depth = reader.Depth;
  2835. bool go = true;
  2836. StringList p = new StringList ();
  2837. do {
  2838. if (reader.NodeType != XmlNodeType.Element)
  2839. continue;
  2840. switch (reader.Name) {
  2841. case "MemberSignature":
  2842. MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
  2843. break;
  2844. case "MemberType":
  2845. MemberType = reader.ReadElementString ();
  2846. break;
  2847. case "ReturnType":
  2848. if (reader.Depth == depth + 2)
  2849. ReturnType = reader.ReadElementString ();
  2850. break;
  2851. case "Parameter":
  2852. if (reader.Depth == depth + 2)
  2853. p.Add (reader.GetAttribute ("Type"));
  2854. break;
  2855. case "Docs":
  2856. if (reader.Depth == depth + 1)
  2857. go = false;
  2858. break;
  2859. }
  2860. } while (go && reader.Read () && reader.Depth >= depth);
  2861. if (p.Count > 0) {
  2862. Parameters = p;
  2863. }
  2864. }
  2865. public DocumentationMember (XmlNode node)
  2866. {
  2867. MemberName = node.Attributes ["MemberName"].Value;
  2868. foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
  2869. XmlAttribute l = n.Attributes ["Language"];
  2870. XmlAttribute v = n.Attributes ["Value"];
  2871. if (l != null && v != null)
  2872. MemberSignatures [l.Value] = v.Value;
  2873. }
  2874. MemberType = node.SelectSingleNode ("MemberType").InnerText;
  2875. XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
  2876. if (rt != null)
  2877. ReturnType = rt.InnerText;
  2878. XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
  2879. if (p.Count > 0) {
  2880. Parameters = new StringList (p.Count);
  2881. for (int i = 0; i < p.Count; ++i)
  2882. Parameters.Add (p [i].Attributes ["Type"].Value);
  2883. }
  2884. }
  2885. }
  2886. public class DynamicParserContext {
  2887. public ReadOnlyCollection<bool> TransformFlags;
  2888. public int TransformIndex;
  2889. public DynamicParserContext (ICustomAttributeProvider provider)
  2890. {
  2891. CustomAttribute da;
  2892. if (provider.HasCustomAttributes &&
  2893. (da = (provider.CustomAttributes.Cast<CustomAttribute>()
  2894. .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
  2895. CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
  2896. ? new CustomAttributeArgument [0]
  2897. : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
  2898. TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
  2899. }
  2900. }
  2901. }
  2902. public enum MemberFormatterState {
  2903. None,
  2904. WithinGenericTypeParameters,
  2905. }
  2906. public abstract class MemberFormatter {
  2907. public virtual string Language {
  2908. get {return "";}
  2909. }
  2910. public string GetName (MemberReference member)
  2911. {
  2912. return GetName (member, null);
  2913. }
  2914. public virtual string GetName (MemberReference member, DynamicParserContext context)
  2915. {
  2916. TypeReference type = member as TypeReference;
  2917. if (type != null)
  2918. return GetTypeName (type, context);
  2919. MethodReference method = member as MethodReference;
  2920. if (method != null && method.Name == ".ctor") // method.IsConstructor
  2921. return GetConstructorName (method);
  2922. if (method != null)
  2923. return GetMethodName (method);
  2924. PropertyReference prop = member as PropertyReference;
  2925. if (prop != null)
  2926. return GetPropertyName (prop);
  2927. FieldReference field = member as FieldReference;
  2928. if (field != null)
  2929. return GetFieldName (field);
  2930. EventReference e = member as EventReference;
  2931. if (e != null)
  2932. return GetEventName (e);
  2933. throw new NotSupportedException ("Can't handle: " +
  2934. (member == null ? "null" : member.GetType().ToString()));
  2935. }
  2936. protected virtual string GetTypeName (TypeReference type)
  2937. {
  2938. return GetTypeName (type, null);
  2939. }
  2940. protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
  2941. {
  2942. if (type == null)
  2943. throw new ArgumentNullException ("type");
  2944. return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
  2945. }
  2946. protected virtual char[] ArrayDelimeters {
  2947. get {return new char[]{'[', ']'};}
  2948. }
  2949. protected virtual MemberFormatterState MemberFormatterState { get; set; }
  2950. protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  2951. {
  2952. if (type is ArrayType) {
  2953. TypeSpecification spec = type as TypeSpecification;
  2954. _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
  2955. return AppendArrayModifiers (buf, (ArrayType) type);
  2956. }
  2957. if (type is ByReferenceType) {
  2958. return AppendRefTypeName (buf, type, context);
  2959. }
  2960. if (type is PointerType) {
  2961. return AppendPointerTypeName (buf, type, context);
  2962. }
  2963. AppendNamespace (buf, type);
  2964. if (type is GenericParameter) {
  2965. return AppendTypeName (buf, type, context);
  2966. }
  2967. GenericInstanceType genInst = type as GenericInstanceType;
  2968. if (type.GenericParameters.Count == 0 &&
  2969. (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
  2970. return AppendFullTypeName (buf, type, context);
  2971. }
  2972. return AppendGenericType (buf, type, context);
  2973. }
  2974. protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
  2975. {
  2976. string ns = DocUtils.GetNamespace (type);
  2977. if (ns != null && ns.Length > 0)
  2978. buf.Append (ns).Append ('.');
  2979. return buf;
  2980. }
  2981. protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  2982. {
  2983. if (type.DeclaringType != null)
  2984. AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
  2985. return AppendTypeName (buf, type, context);
  2986. }
  2987. protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  2988. {
  2989. if (context != null)
  2990. context.TransformIndex++;
  2991. return AppendTypeName (buf, type.Name);
  2992. }
  2993. protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
  2994. {
  2995. int n = typename.IndexOf ("`");
  2996. if (n >= 0)
  2997. return buf.Append (typename.Substring (0, n));
  2998. return buf.Append (typename);
  2999. }
  3000. protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
  3001. {
  3002. buf.Append (ArrayDelimeters [0]);
  3003. int rank = array.Rank;
  3004. if (rank > 1)
  3005. buf.Append (new string (',', rank-1));
  3006. return buf.Append (ArrayDelimeters [1]);
  3007. }
  3008. protected virtual string RefTypeModifier {
  3009. get {return "@";}
  3010. }
  3011. protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  3012. {
  3013. TypeSpecification spec = type as TypeSpecification;
  3014. return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
  3015. .Append (RefTypeModifier);
  3016. }
  3017. protected virtual string PointerModifier {
  3018. get {return "*";}
  3019. }
  3020. protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  3021. {
  3022. TypeSpecification spec = type as TypeSpecification;
  3023. return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
  3024. .Append (PointerModifier);
  3025. }
  3026. protected virtual char[] GenericTypeContainer {
  3027. get {return new char[]{'<', '>'};}
  3028. }
  3029. protected virtual char NestedTypeSeparator {
  3030. get {return '.';}
  3031. }
  3032. protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
  3033. {
  3034. List<TypeReference> decls = DocUtils.GetDeclaringTypes (
  3035. type is GenericInstanceType ? type.GetElementType () : type);
  3036. List<TypeReference> genArgs = GetGenericArguments (type);
  3037. int argIdx = 0;
  3038. int prev = 0;
  3039. bool insertNested = false;
  3040. foreach (var decl in decls) {
  3041. TypeReference declDef = decl.Resolve () ?? decl;
  3042. if (insertNested) {
  3043. buf.Append (NestedTypeSeparator);
  3044. }
  3045. insertNested = true;
  3046. AppendTypeName (buf, declDef, context);
  3047. int ac = DocUtils.GetGenericArgumentCount (declDef);
  3048. int c = ac - prev;
  3049. prev = ac;
  3050. if (c > 0) {
  3051. buf.Append (GenericTypeContainer [0]);
  3052. var origState = MemberFormatterState;
  3053. MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
  3054. _AppendTypeName (buf, genArgs [argIdx++], context);
  3055. for (int i = 1; i < c; ++i) {
  3056. _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
  3057. }
  3058. MemberFormatterState = origState;
  3059. buf.Append (GenericTypeContainer [1]);
  3060. }
  3061. }
  3062. return buf;
  3063. }
  3064. protected List<TypeReference> GetGenericArguments (TypeReference type)
  3065. {
  3066. var args = new List<TypeReference> ();
  3067. GenericInstanceType inst = type as GenericInstanceType;
  3068. if (inst != null)
  3069. args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
  3070. else
  3071. args.AddRange (type.GenericParameters.Cast<TypeReference> ());
  3072. return args;
  3073. }
  3074. protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
  3075. {
  3076. return buf;
  3077. }
  3078. protected virtual string GetConstructorName (MethodReference constructor)
  3079. {
  3080. return constructor.Name;
  3081. }
  3082. protected virtual string GetMethodName (MethodReference method)
  3083. {
  3084. return method.Name;
  3085. }
  3086. protected virtual string GetPropertyName (PropertyReference property)
  3087. {
  3088. return property.Name;
  3089. }
  3090. protected virtual string GetFieldName (FieldReference field)
  3091. {
  3092. return field.Name;
  3093. }
  3094. protected virtual string GetEventName (EventReference e)
  3095. {
  3096. return e.Name;
  3097. }
  3098. public virtual string GetDeclaration (MemberReference member)
  3099. {
  3100. if (member == null)
  3101. throw new ArgumentNullException ("member");
  3102. TypeDefinition type = member as TypeDefinition;
  3103. if (type != null)
  3104. return GetTypeDeclaration (type);
  3105. MethodDefinition method = member as MethodDefinition;
  3106. if (method != null && method.IsConstructor)
  3107. return GetConstructorDeclaration (method);
  3108. if (method != null)
  3109. return GetMethodDeclaration (method);
  3110. PropertyDefinition prop = member as PropertyDefinition;
  3111. if (prop != null)
  3112. return GetPropertyDeclaration (prop);
  3113. FieldDefinition field = member as FieldDefinition;
  3114. if (field != null)
  3115. return GetFieldDeclaration (field);
  3116. EventDefinition e = member as EventDefinition;
  3117. if (e != null)
  3118. return GetEventDeclaration (e);
  3119. throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
  3120. }
  3121. protected virtual string GetTypeDeclaration (TypeDefinition type)
  3122. {
  3123. if (type == null)
  3124. throw new ArgumentNullException ("type");
  3125. StringBuilder buf = new StringBuilder (type.Name.Length);
  3126. _AppendTypeName (buf, type, null);
  3127. AppendGenericTypeConstraints (buf, type);
  3128. return buf.ToString ();
  3129. }
  3130. protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
  3131. {
  3132. return GetConstructorName (constructor);
  3133. }
  3134. protected virtual string GetMethodDeclaration (MethodDefinition method)
  3135. {
  3136. if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
  3137. ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
  3138. return null;
  3139. // Special signature for destructors.
  3140. if (method.Name == "Finalize" && method.Parameters.Count == 0)
  3141. return GetFinalizerName (method);
  3142. StringBuilder buf = new StringBuilder ();
  3143. AppendVisibility (buf, method);
  3144. if (buf.Length == 0 &&
  3145. !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
  3146. return null;
  3147. AppendModifiers (buf, method);
  3148. if (buf.Length != 0)
  3149. buf.Append (" ");
  3150. buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
  3151. AppendMethodName (buf, method);
  3152. AppendGenericMethod (buf, method).Append (" ");
  3153. AppendParameters (buf, method, method.Parameters);
  3154. AppendGenericMethodConstraints (buf, method);
  3155. return buf.ToString ();
  3156. }
  3157. protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
  3158. {
  3159. return buf.Append (method.Name);
  3160. }
  3161. protected virtual string GetFinalizerName (MethodDefinition method)
  3162. {
  3163. return "Finalize";
  3164. }
  3165. protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
  3166. {
  3167. return buf;
  3168. }
  3169. protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
  3170. {
  3171. return buf;
  3172. }
  3173. protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
  3174. {
  3175. return buf;
  3176. }
  3177. protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
  3178. {
  3179. return buf;
  3180. }
  3181. protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
  3182. {
  3183. return buf;
  3184. }
  3185. protected virtual string GetPropertyDeclaration (PropertyDefinition property)
  3186. {
  3187. return GetPropertyName (property);
  3188. }
  3189. protected virtual string GetFieldDeclaration (FieldDefinition field)
  3190. {
  3191. return GetFieldName (field);
  3192. }
  3193. protected virtual string GetEventDeclaration (EventDefinition e)
  3194. {
  3195. return GetEventName (e);
  3196. }
  3197. }
  3198. class ILFullMemberFormatter : MemberFormatter {
  3199. public override string Language {
  3200. get {return "ILAsm";}
  3201. }
  3202. protected override char NestedTypeSeparator {
  3203. get {
  3204. return '/';
  3205. }
  3206. }
  3207. protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
  3208. {
  3209. if (GetBuiltinType (type.FullName) != null)
  3210. return buf;
  3211. string ns = DocUtils.GetNamespace (type);
  3212. if (ns != null && ns.Length > 0) {
  3213. if (type.IsValueType)
  3214. buf.Append ("valuetype ");
  3215. else
  3216. buf.Append ("class ");
  3217. buf.Append (ns).Append ('.');
  3218. }
  3219. return buf;
  3220. }
  3221. private static string GetBuiltinType (string t)
  3222. {
  3223. switch (t) {
  3224. case "System.Byte": return "unsigned int8";
  3225. case "System.SByte": return "int8";
  3226. case "System.Int16": return "int16";
  3227. case "System.Int32": return "int32";
  3228. case "System.Int64": return "int64";
  3229. case "System.IntPtr": return "native int";
  3230. case "System.UInt16": return "unsigned int16";
  3231. case "System.UInt32": return "unsigned int32";
  3232. case "System.UInt64": return "unsigned int64";
  3233. case "System.UIntPtr": return "native unsigned int";
  3234. case "System.Single": return "float32";
  3235. case "System.Double": return "float64";
  3236. case "System.Boolean": return "bool";
  3237. case "System.Char": return "char";
  3238. case "System.Void": return "void";
  3239. case "System.String": return "string";
  3240. case "System.Object": return "object";
  3241. }
  3242. return null;
  3243. }
  3244. protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
  3245. {
  3246. return buf.Append (typename);
  3247. }
  3248. protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  3249. {
  3250. if (type is GenericParameter) {
  3251. AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
  3252. return buf;
  3253. }
  3254. string s = GetBuiltinType (type.FullName);
  3255. if (s != null) {
  3256. return buf.Append (s);
  3257. }
  3258. return base.AppendTypeName (buf, type, context);
  3259. }
  3260. private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
  3261. {
  3262. if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
  3263. return buf.Append (type.Owner is TypeReference ? "!" : "!!");
  3264. }
  3265. GenericParameterAttributes attrs = type.Attributes;
  3266. if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
  3267. buf.Append ("class ");
  3268. if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
  3269. buf.Append ("struct ");
  3270. if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
  3271. buf.Append (".ctor ");
  3272. IList<TypeReference> constraints = type.Constraints;
  3273. MemberFormatterState = 0;
  3274. if (constraints.Count > 0) {
  3275. var full = new ILFullMemberFormatter ();
  3276. buf.Append ("(").Append (full.GetName (constraints [0]));
  3277. for (int i = 1; i < constraints.Count; ++i) {
  3278. buf.Append (", ").Append (full.GetName (constraints [i]));
  3279. }
  3280. buf.Append (") ");
  3281. }
  3282. MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
  3283. if ((attrs & GenericParameterAttributes.Covariant) != 0)
  3284. buf.Append ("+ ");
  3285. if ((attrs & GenericParameterAttributes.Contravariant) != 0)
  3286. buf.Append ("- ");
  3287. return buf;
  3288. }
  3289. protected override string GetTypeDeclaration (TypeDefinition type)
  3290. {
  3291. string visibility = GetTypeVisibility (type.Attributes);
  3292. if (visibility == null)
  3293. return null;
  3294. StringBuilder buf = new StringBuilder ();
  3295. buf.Append (".class ");
  3296. if (type.IsNested)
  3297. buf.Append ("nested ");
  3298. buf.Append (visibility).Append (" ");
  3299. if (type.IsInterface)
  3300. buf.Append ("interface ");
  3301. if (type.IsSequentialLayout)
  3302. buf.Append ("sequential ");
  3303. if (type.IsAutoLayout)
  3304. buf.Append ("auto ");
  3305. if (type.IsAnsiClass)
  3306. buf.Append ("ansi ");
  3307. if (type.IsAbstract)
  3308. buf.Append ("abstract ");
  3309. if (type.IsSerializable)
  3310. buf.Append ("serializable ");
  3311. if (type.IsSealed)
  3312. buf.Append ("sealed ");
  3313. if (type.IsBeforeFieldInit)
  3314. buf.Append ("beforefieldinit ");
  3315. var state = MemberFormatterState;
  3316. MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
  3317. buf.Append (GetName (type));
  3318. MemberFormatterState = state;
  3319. var full = new ILFullMemberFormatter ();
  3320. if (type.BaseType != null) {
  3321. buf.Append (" extends ");
  3322. if (type.BaseType.FullName == "System.Object")
  3323. buf.Append ("System.Object");
  3324. else
  3325. buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
  3326. }
  3327. bool first = true;
  3328. foreach (var name in type.Interfaces
  3329. .Select (i => full.GetName (i))
  3330. .OrderBy (n => n)) {
  3331. if (first) {
  3332. buf.Append (" implements ");
  3333. first = false;
  3334. }
  3335. else {
  3336. buf.Append (", ");
  3337. }
  3338. buf.Append (name);
  3339. }
  3340. return buf.ToString ();
  3341. }
  3342. protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
  3343. {
  3344. List<TypeReference> decls = DocUtils.GetDeclaringTypes (
  3345. type is GenericInstanceType ? type.GetElementType () : type);
  3346. bool first = true;
  3347. foreach (var decl in decls) {
  3348. TypeReference declDef = decl.Resolve () ?? decl;
  3349. if (!first) {
  3350. buf.Append (NestedTypeSeparator);
  3351. }
  3352. first = false;
  3353. AppendTypeName (buf, declDef, context);
  3354. }
  3355. buf.Append ('<');
  3356. first = true;
  3357. foreach (TypeReference arg in GetGenericArguments (type)) {
  3358. if (!first)
  3359. buf.Append (", ");
  3360. first = false;
  3361. _AppendTypeName (buf, arg, context);
  3362. }
  3363. buf.Append ('>');
  3364. return buf;
  3365. }
  3366. static string GetTypeVisibility (TypeAttributes ta)
  3367. {
  3368. switch (ta & TypeAttributes.VisibilityMask) {
  3369. case TypeAttributes.Public:
  3370. case TypeAttributes.NestedPublic:
  3371. return "public";
  3372. case TypeAttributes.NestedFamily:
  3373. case TypeAttributes.NestedFamORAssem:
  3374. return "protected";
  3375. default:
  3376. return null;
  3377. }
  3378. }
  3379. protected override string GetConstructorDeclaration (MethodDefinition constructor)
  3380. {
  3381. return GetMethodDeclaration (constructor);
  3382. }
  3383. protected override string GetMethodDeclaration (MethodDefinition method)
  3384. {
  3385. if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
  3386. return null;
  3387. var buf = new StringBuilder ();
  3388. buf.Append (".method ");
  3389. AppendVisibility (buf, method);
  3390. if (method.IsStatic)
  3391. buf.Append ("static ");
  3392. if (method.IsHideBySig)
  3393. buf.Append ("hidebysig ");
  3394. if (method.IsPInvokeImpl) {
  3395. var info = method.PInvokeInfo;
  3396. buf.Append ("pinvokeimpl (\"")
  3397. .Append (info.Module.Name)
  3398. .Append ("\" as \"")
  3399. .Append (info.EntryPoint)
  3400. .Append ("\"");
  3401. if (info.IsCharSetAuto)
  3402. buf.Append (" auto");
  3403. if (info.IsCharSetUnicode)
  3404. buf.Append (" unicode");
  3405. if (info.IsCharSetAnsi)
  3406. buf.Append (" ansi");
  3407. if (info.IsCallConvCdecl)
  3408. buf.Append (" cdecl");
  3409. if (info.IsCallConvStdCall)
  3410. buf.Append (" stdcall");
  3411. if (info.IsCallConvWinapi)
  3412. buf.Append (" winapi");
  3413. if (info.IsCallConvThiscall)
  3414. buf.Append (" thiscall");
  3415. if (info.SupportsLastError)
  3416. buf.Append (" lasterr");
  3417. buf.Append (")");
  3418. }
  3419. if (method.IsSpecialName)
  3420. buf.Append ("specialname ");
  3421. if (method.IsRuntimeSpecialName)
  3422. buf.Append ("rtspecialname ");
  3423. if (method.IsNewSlot)
  3424. buf.Append ("newslot ");
  3425. if (method.IsVirtual)
  3426. buf.Append ("virtual ");
  3427. if (!method.IsStatic)
  3428. buf.Append ("instance ");
  3429. _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
  3430. buf.Append (' ')
  3431. .Append (method.Name);
  3432. if (method.IsGenericMethod ()) {
  3433. var state = MemberFormatterState;
  3434. MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
  3435. IList<GenericParameter> args = method.GenericParameters;
  3436. if (args.Count > 0) {
  3437. buf.Append ("<");
  3438. _AppendTypeName (buf, args [0], null);
  3439. for (int i = 1; i < args.Count; ++i)
  3440. _AppendTypeName (buf.Append (", "), args [i], null);
  3441. buf.Append (">");
  3442. }
  3443. MemberFormatterState = state;
  3444. }
  3445. buf.Append ('(');
  3446. bool first = true;
  3447. for (int i = 0; i < method.Parameters.Count; ++i) {
  3448. if (!first)
  3449. buf.Append (", ");
  3450. first = false;
  3451. _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
  3452. buf.Append (' ');
  3453. buf.Append (method.Parameters [i].Name);
  3454. }
  3455. buf.Append (')');
  3456. if (method.IsIL)
  3457. buf.Append (" cil");
  3458. if (method.IsRuntime)
  3459. buf.Append (" runtime");
  3460. if (method.IsManaged)
  3461. buf.Append (" managed");
  3462. return buf.ToString ();
  3463. }
  3464. protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
  3465. {
  3466. if (DocUtils.IsExplicitlyImplemented (method)) {
  3467. TypeReference iface;
  3468. MethodReference ifaceMethod;
  3469. DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
  3470. return buf.Append (new CSharpMemberFormatter ().GetName (iface))
  3471. .Append ('.')
  3472. .Append (ifaceMethod.Name);
  3473. }
  3474. return base.AppendMethodName (buf, method);
  3475. }
  3476. protected override string RefTypeModifier {
  3477. get {return "";}
  3478. }
  3479. protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
  3480. {
  3481. if (method.IsPublic)
  3482. return buf.Append ("public ");
  3483. if (method.IsFamilyAndAssembly)
  3484. return buf.Append ("familyandassembly");
  3485. if (method.IsFamilyOrAssembly)
  3486. return buf.Append ("familyorassembly");
  3487. if (method.IsFamily)
  3488. return buf.Append ("family");
  3489. return buf;
  3490. }
  3491. protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
  3492. {
  3493. string modifiers = String.Empty;
  3494. if (method.IsStatic) modifiers += " static";
  3495. if (method.IsVirtual && !method.IsAbstract) {
  3496. if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
  3497. else modifiers += " override";
  3498. }
  3499. TypeDefinition declType = (TypeDefinition) method.DeclaringType;
  3500. if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
  3501. if (method.IsFinal) modifiers += " sealed";
  3502. if (modifiers == " virtual sealed") modifiers = "";
  3503. return buf.Append (modifiers);
  3504. }
  3505. protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
  3506. {
  3507. if (method.IsGenericMethod ()) {
  3508. IList<GenericParameter> args = method.GenericParameters;
  3509. if (args.Count > 0) {
  3510. buf.Append ("<");
  3511. buf.Append (args [0].Name);
  3512. for (int i = 1; i < args.Count; ++i)
  3513. buf.Append (",").Append (args [i].Name);
  3514. buf.Append (">");
  3515. }
  3516. }
  3517. return buf;
  3518. }
  3519. protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
  3520. {
  3521. return AppendParameters (buf, method, parameters, '(', ')');
  3522. }
  3523. private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
  3524. {
  3525. buf.Append (begin);
  3526. if (parameters.Count > 0) {
  3527. if (DocUtils.IsExtensionMethod (method))
  3528. buf.Append ("this ");
  3529. AppendParameter (buf, parameters [0]);
  3530. for (int i = 1; i < parameters.Count; ++i) {
  3531. buf.Append (", ");
  3532. AppendParameter (buf, parameters [i]);
  3533. }
  3534. }
  3535. return buf.Append (end);
  3536. }
  3537. private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
  3538. {
  3539. if (parameter.ParameterType is ByReferenceType) {
  3540. if (parameter.IsOut)
  3541. buf.Append ("out ");
  3542. else
  3543. buf.Append ("ref ");
  3544. }
  3545. buf.Append (GetName (parameter.ParameterType)).Append (" ");
  3546. return buf.Append (parameter.Name);
  3547. }
  3548. protected override string GetPropertyDeclaration (PropertyDefinition property)
  3549. {
  3550. MethodDefinition gm = null, sm = null;
  3551. string get_visible = null;
  3552. if ((gm = property.GetMethod) != null &&
  3553. (DocUtils.IsExplicitlyImplemented (gm) ||
  3554. (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
  3555. get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
  3556. string set_visible = null;
  3557. if ((sm = property.SetMethod) != null &&
  3558. (DocUtils.IsExplicitlyImplemented (sm) ||
  3559. (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
  3560. set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
  3561. if ((set_visible == null) && (get_visible == null))
  3562. return null;
  3563. StringBuilder buf = new StringBuilder ()
  3564. .Append (".property ");
  3565. if (!(gm ?? sm).IsStatic)
  3566. buf.Append ("instance ");
  3567. _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
  3568. buf.Append (' ').Append (property.Name);
  3569. if (!property.HasParameters || property.Parameters.Count == 0)
  3570. return buf.ToString ();
  3571. buf.Append ('(');
  3572. bool first = true;
  3573. foreach (ParameterDefinition p in property.Parameters) {
  3574. if (!first)
  3575. buf.Append (", ");
  3576. first = false;
  3577. _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
  3578. }
  3579. buf.Append (')');
  3580. return buf.ToString ();
  3581. }
  3582. protected override string GetFieldDeclaration (FieldDefinition field)
  3583. {
  3584. TypeDefinition declType = (TypeDefinition) field.DeclaringType;
  3585. if (declType.IsEnum && field.Name == "value__")
  3586. return null; // This member of enums aren't documented.
  3587. StringBuilder buf = new StringBuilder ();
  3588. AppendFieldVisibility (buf, field);
  3589. if (buf.Length == 0)
  3590. return null;
  3591. buf.Insert (0, ".field ");
  3592. if (field.IsStatic)
  3593. buf.Append ("static ");
  3594. if (field.IsInitOnly)
  3595. buf.Append ("initonly ");
  3596. if (field.IsLiteral)
  3597. buf.Append ("literal ");
  3598. _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
  3599. buf.Append (' ').Append (field.Name);
  3600. AppendFieldValue (buf, field);
  3601. return buf.ToString ();
  3602. }
  3603. static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
  3604. {
  3605. if (field.IsPublic)
  3606. return buf.Append ("public ");
  3607. if (field.IsFamilyAndAssembly)
  3608. return buf.Append ("familyandassembly ");
  3609. if (field.IsFamilyOrAssembly)
  3610. return buf.Append ("familyorassembly ");
  3611. if (field.IsFamily)
  3612. return buf.Append ("family ");
  3613. return buf;
  3614. }
  3615. static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
  3616. {
  3617. // enums have a value__ field, which we ignore
  3618. if (field.DeclaringType.IsGenericType ())
  3619. return buf;
  3620. if (field.HasConstant && field.IsLiteral) {
  3621. object val = null;
  3622. try {
  3623. val = field.Constant;
  3624. } catch {
  3625. return buf;
  3626. }
  3627. if (val == null)
  3628. buf.Append (" = ").Append ("null");
  3629. else if (val is Enum)
  3630. buf.Append (" = ")
  3631. .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
  3632. .Append ('(')
  3633. .Append (val.ToString ())
  3634. .Append (')');
  3635. else if (val is IFormattable) {
  3636. string value = ((IFormattable)val).ToString();
  3637. buf.Append (" = ");
  3638. if (val is string)
  3639. buf.Append ("\"" + value + "\"");
  3640. else
  3641. buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
  3642. .Append ('(')
  3643. .Append (value)
  3644. .Append (')');
  3645. }
  3646. }
  3647. return buf;
  3648. }
  3649. protected override string GetEventDeclaration (EventDefinition e)
  3650. {
  3651. StringBuilder buf = new StringBuilder ();
  3652. if (AppendVisibility (buf, e.AddMethod).Length == 0) {
  3653. return null;
  3654. }
  3655. buf.Length = 0;
  3656. buf.Append (".event ")
  3657. .Append (GetName (e.EventType))
  3658. .Append (' ')
  3659. .Append (e.Name);
  3660. return buf.ToString ();
  3661. }
  3662. }
  3663. class ILMemberFormatter : ILFullMemberFormatter {
  3664. protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
  3665. {
  3666. return buf;
  3667. }
  3668. }
  3669. class CSharpFullMemberFormatter : MemberFormatter {
  3670. public override string Language {
  3671. get {return "C#";}
  3672. }
  3673. protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
  3674. {
  3675. string ns = DocUtils.GetNamespace (type);
  3676. if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
  3677. buf.Append (ns).Append ('.');
  3678. return buf;
  3679. }
  3680. private string GetCSharpType (string t)
  3681. {
  3682. switch (t) {
  3683. case "System.Byte": return "byte";
  3684. case "System.SByte": return "sbyte";
  3685. case "System.Int16": return "short";
  3686. case "System.Int32": return "int";
  3687. case "System.Int64": return "long";
  3688. case "System.UInt16": return "ushort";
  3689. case "System.UInt32": return "uint";
  3690. case "System.UInt64": return "ulong";
  3691. case "System.Single": return "float";
  3692. case "System.Double": return "double";
  3693. case "System.Decimal": return "decimal";
  3694. case "System.Boolean": return "bool";
  3695. case "System.Char": return "char";
  3696. case "System.Void": return "void";
  3697. case "System.String": return "string";
  3698. case "System.Object": return "object";
  3699. }
  3700. return null;
  3701. }
  3702. protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  3703. {
  3704. if (context != null && context.TransformFlags != null &&
  3705. (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
  3706. context.TransformIndex++;
  3707. return buf.Append ("dynamic");
  3708. }
  3709. if (type is GenericParameter)
  3710. return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
  3711. string t = type.FullName;
  3712. if (!t.StartsWith ("System.")) {
  3713. return base.AppendTypeName (buf, type, context);
  3714. }
  3715. string s = GetCSharpType (t);
  3716. if (s != null) {
  3717. if (context != null)
  3718. context.TransformIndex++;
  3719. return buf.Append (s);
  3720. }
  3721. return base.AppendTypeName (buf, type, context);
  3722. }
  3723. private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
  3724. {
  3725. if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
  3726. return buf;
  3727. GenericParameterAttributes attrs = type.Attributes;
  3728. bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
  3729. bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
  3730. if (isin)
  3731. buf.Append ("in ");
  3732. else if (isout)
  3733. buf.Append ("out ");
  3734. return buf;
  3735. }
  3736. protected override string GetTypeDeclaration (TypeDefinition type)
  3737. {
  3738. string visibility = GetTypeVisibility (type.Attributes);
  3739. if (visibility == null)
  3740. return null;
  3741. StringBuilder buf = new StringBuilder ();
  3742. buf.Append (visibility);
  3743. buf.Append (" ");
  3744. MemberFormatter full = new CSharpFullMemberFormatter ();
  3745. if (DocUtils.IsDelegate (type)) {
  3746. buf.Append("delegate ");
  3747. MethodDefinition invoke = type.GetMethod ("Invoke");
  3748. buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
  3749. buf.Append (GetName (type));
  3750. AppendParameters (buf, invoke, invoke.Parameters);
  3751. AppendGenericTypeConstraints (buf, type);
  3752. buf.Append (";");
  3753. return buf.ToString();
  3754. }
  3755. if (type.IsAbstract && !type.IsInterface)
  3756. buf.Append("abstract ");
  3757. if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
  3758. buf.Append("sealed ");
  3759. buf.Replace ("abstract sealed", "static");
  3760. buf.Append (GetTypeKind (type));
  3761. buf.Append (" ");
  3762. buf.Append (GetCSharpType (type.FullName) == null
  3763. ? GetName (type)
  3764. : type.Name);
  3765. if (!type.IsEnum) {
  3766. TypeReference basetype = type.BaseType;
  3767. if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
  3768. basetype = null;
  3769. List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
  3770. .Select (iface => full.GetName (iface))
  3771. .OrderBy (s => s)
  3772. .ToList ();
  3773. if (basetype != null || interface_names.Count > 0)
  3774. buf.Append (" : ");
  3775. if (basetype != null) {
  3776. buf.Append (full.GetName (basetype));
  3777. if (interface_names.Count > 0)
  3778. buf.Append (", ");
  3779. }
  3780. for (int i = 0; i < interface_names.Count; i++){
  3781. if (i != 0)
  3782. buf.Append (", ");
  3783. buf.Append (interface_names [i]);
  3784. }
  3785. AppendGenericTypeConstraints (buf, type);
  3786. }
  3787. return buf.ToString ();
  3788. }
  3789. static string GetTypeKind (TypeDefinition t)
  3790. {
  3791. if (t.IsEnum)
  3792. return "enum";
  3793. if (t.IsValueType)
  3794. return "struct";
  3795. if (t.IsClass || t.FullName == "System.Enum")
  3796. return "class";
  3797. if (t.IsInterface)
  3798. return "interface";
  3799. throw new ArgumentException(t.FullName);
  3800. }
  3801. static string GetTypeVisibility (TypeAttributes ta)
  3802. {
  3803. switch (ta & TypeAttributes.VisibilityMask) {
  3804. case TypeAttributes.Public:
  3805. case TypeAttributes.NestedPublic:
  3806. return "public";
  3807. case TypeAttributes.NestedFamily:
  3808. case TypeAttributes.NestedFamORAssem:
  3809. return "protected";
  3810. default:
  3811. return null;
  3812. }
  3813. }
  3814. protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
  3815. {
  3816. if (type.GenericParameters.Count == 0)
  3817. return buf;
  3818. return AppendConstraints (buf, type.GenericParameters);
  3819. }
  3820. private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
  3821. {
  3822. foreach (GenericParameter genArg in genArgs) {
  3823. GenericParameterAttributes attrs = genArg.Attributes;
  3824. IList<TypeReference> constraints = genArg.Constraints;
  3825. if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
  3826. continue;
  3827. bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
  3828. bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
  3829. bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
  3830. bool comma = false;
  3831. if (!isref && !isvt && !isnew && constraints.Count == 0)
  3832. continue;
  3833. buf.Append (" where ").Append (genArg.Name).Append (" : ");
  3834. if (isref) {
  3835. buf.Append ("class");
  3836. comma = true;
  3837. }
  3838. else if (isvt) {
  3839. buf.Append ("struct");
  3840. comma = true;
  3841. }
  3842. if (constraints.Count > 0 && !isvt) {
  3843. if (comma)
  3844. buf.Append (", ");
  3845. buf.Append (GetTypeName (constraints [0]));
  3846. for (int i = 1; i < constraints.Count; ++i)
  3847. buf.Append (", ").Append (GetTypeName (constraints [i]));
  3848. }
  3849. if (isnew && !isvt) {
  3850. if (comma)
  3851. buf.Append (", ");
  3852. buf.Append ("new()");
  3853. }
  3854. }
  3855. return buf;
  3856. }
  3857. protected override string GetConstructorDeclaration (MethodDefinition constructor)
  3858. {
  3859. StringBuilder buf = new StringBuilder ();
  3860. AppendVisibility (buf, constructor);
  3861. if (buf.Length == 0)
  3862. return null;
  3863. buf.Append (' ');
  3864. base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
  3865. AppendParameters (buf, constructor, constructor.Parameters);
  3866. buf.Append (';');
  3867. return buf.ToString ();
  3868. }
  3869. protected override string GetMethodDeclaration (MethodDefinition method)
  3870. {
  3871. string decl = base.GetMethodDeclaration (method);
  3872. if (decl != null)
  3873. return decl + ";";
  3874. return null;
  3875. }
  3876. protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
  3877. {
  3878. if (DocUtils.IsExplicitlyImplemented (method)) {
  3879. TypeReference iface;
  3880. MethodReference ifaceMethod;
  3881. DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
  3882. return buf.Append (new CSharpMemberFormatter ().GetName (iface))
  3883. .Append ('.')
  3884. .Append (ifaceMethod.Name);
  3885. }
  3886. return base.AppendMethodName (buf, method);
  3887. }
  3888. protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
  3889. {
  3890. if (method.GenericParameters.Count == 0)
  3891. return buf;
  3892. return AppendConstraints (buf, method.GenericParameters);
  3893. }
  3894. protected override string RefTypeModifier {
  3895. get {return "";}
  3896. }
  3897. protected override string GetFinalizerName (MethodDefinition method)
  3898. {
  3899. return "~" + method.DeclaringType.Name + " ()";
  3900. }
  3901. protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
  3902. {
  3903. if (method == null)
  3904. return buf;
  3905. if (method.IsPublic)
  3906. return buf.Append ("public");
  3907. if (method.IsFamily || method.IsFamilyOrAssembly)
  3908. return buf.Append ("protected");
  3909. return buf;
  3910. }
  3911. protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
  3912. {
  3913. string modifiers = String.Empty;
  3914. if (method.IsStatic) modifiers += " static";
  3915. if (method.IsVirtual && !method.IsAbstract) {
  3916. if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
  3917. else modifiers += " override";
  3918. }
  3919. TypeDefinition declType = (TypeDefinition) method.DeclaringType;
  3920. if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
  3921. if (method.IsFinal) modifiers += " sealed";
  3922. if (modifiers == " virtual sealed") modifiers = "";
  3923. return buf.Append (modifiers);
  3924. }
  3925. protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
  3926. {
  3927. if (method.IsGenericMethod ()) {
  3928. IList<GenericParameter> args = method.GenericParameters;
  3929. if (args.Count > 0) {
  3930. buf.Append ("<");
  3931. buf.Append (args [0].Name);
  3932. for (int i = 1; i < args.Count; ++i)
  3933. buf.Append (",").Append (args [i].Name);
  3934. buf.Append (">");
  3935. }
  3936. }
  3937. return buf;
  3938. }
  3939. protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
  3940. {
  3941. return AppendParameters (buf, method, parameters, '(', ')');
  3942. }
  3943. private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
  3944. {
  3945. buf.Append (begin);
  3946. if (parameters.Count > 0) {
  3947. if (DocUtils.IsExtensionMethod (method))
  3948. buf.Append ("this ");
  3949. AppendParameter (buf, parameters [0]);
  3950. for (int i = 1; i < parameters.Count; ++i) {
  3951. buf.Append (", ");
  3952. AppendParameter (buf, parameters [i]);
  3953. }
  3954. }
  3955. return buf.Append (end);
  3956. }
  3957. private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
  3958. {
  3959. if (parameter.ParameterType is ByReferenceType) {
  3960. if (parameter.IsOut)
  3961. buf.Append ("out ");
  3962. else
  3963. buf.Append ("ref ");
  3964. }
  3965. buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
  3966. buf.Append (parameter.Name);
  3967. if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
  3968. buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
  3969. }
  3970. return buf;
  3971. }
  3972. protected override string GetPropertyDeclaration (PropertyDefinition property)
  3973. {
  3974. MethodDefinition method;
  3975. string get_visible = null;
  3976. if ((method = property.GetMethod) != null &&
  3977. (DocUtils.IsExplicitlyImplemented (method) ||
  3978. (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
  3979. get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
  3980. string set_visible = null;
  3981. if ((method = property.SetMethod) != null &&
  3982. (DocUtils.IsExplicitlyImplemented (method) ||
  3983. (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
  3984. set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
  3985. if ((set_visible == null) && (get_visible == null))
  3986. return null;
  3987. string visibility;
  3988. StringBuilder buf = new StringBuilder ();
  3989. if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
  3990. buf.Append (visibility = get_visible);
  3991. else if (set_visible != null && get_visible == null)
  3992. buf.Append (visibility = set_visible);
  3993. else
  3994. buf.Append (visibility = "public");
  3995. // Pick an accessor to use for static/virtual/override/etc. checks.
  3996. method = property.SetMethod;
  3997. if (method == null)
  3998. method = property.GetMethod;
  3999. string modifiers = String.Empty;
  4000. if (method.IsStatic) modifiers += " static";
  4001. if (method.IsVirtual && !method.IsAbstract) {
  4002. if ((method.Attributes & MethodAttributes.NewSlot) != 0)
  4003. modifiers += " virtual";
  4004. else
  4005. modifiers += " override";
  4006. }
  4007. TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
  4008. if (method.IsAbstract && !declDef.IsInterface)
  4009. modifiers += " abstract";
  4010. if (method.IsFinal)
  4011. modifiers += " sealed";
  4012. if (modifiers == " virtual sealed")
  4013. modifiers = "";
  4014. buf.Append (modifiers).Append (' ');
  4015. buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
  4016. IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
  4017. string name = property.Name;
  4018. foreach (MemberReference mi in defs) {
  4019. if (mi == property) {
  4020. name = "this";
  4021. break;
  4022. }
  4023. }
  4024. buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
  4025. if (property.Parameters.Count != 0) {
  4026. AppendParameters (buf, method, property.Parameters, '[', ']');
  4027. }
  4028. buf.Append (" {");
  4029. if (get_visible != null) {
  4030. if (get_visible != visibility)
  4031. buf.Append (' ').Append (get_visible);
  4032. buf.Append (" get;");
  4033. }
  4034. if (set_visible != null) {
  4035. if (set_visible != visibility)
  4036. buf.Append (' ').Append (set_visible);
  4037. buf.Append (" set;");
  4038. }
  4039. buf.Append (" }");
  4040. return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
  4041. }
  4042. protected override string GetFieldDeclaration (FieldDefinition field)
  4043. {
  4044. TypeDefinition declType = (TypeDefinition) field.DeclaringType;
  4045. if (declType.IsEnum && field.Name == "value__")
  4046. return null; // This member of enums aren't documented.
  4047. StringBuilder buf = new StringBuilder ();
  4048. AppendFieldVisibility (buf, field);
  4049. if (buf.Length == 0)
  4050. return null;
  4051. if (declType.IsEnum)
  4052. return field.Name;
  4053. if (field.IsStatic && !field.IsLiteral)
  4054. buf.Append (" static");
  4055. if (field.IsInitOnly)
  4056. buf.Append (" readonly");
  4057. if (field.IsLiteral)
  4058. buf.Append (" const");
  4059. buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
  4060. buf.Append (field.Name);
  4061. AppendFieldValue (buf, field);
  4062. buf.Append (';');
  4063. return buf.ToString ();
  4064. }
  4065. static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
  4066. {
  4067. if (field.IsPublic)
  4068. return buf.Append ("public");
  4069. if (field.IsFamily || field.IsFamilyOrAssembly)
  4070. return buf.Append ("protected");
  4071. return buf;
  4072. }
  4073. static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
  4074. {
  4075. // enums have a value__ field, which we ignore
  4076. if (((TypeDefinition ) field.DeclaringType).IsEnum ||
  4077. field.DeclaringType.IsGenericType ())
  4078. return buf;
  4079. if (field.HasConstant && field.IsLiteral) {
  4080. object val = null;
  4081. try {
  4082. val = field.Constant;
  4083. } catch {
  4084. return buf;
  4085. }
  4086. if (val == null)
  4087. buf.Append (" = ").Append ("null");
  4088. else if (val is Enum)
  4089. buf.Append (" = ").Append (val.ToString ());
  4090. else if (val is IFormattable) {
  4091. string value = ((IFormattable)val).ToString();
  4092. if (val is string)
  4093. value = "\"" + value + "\"";
  4094. buf.Append (" = ").Append (value);
  4095. }
  4096. }
  4097. return buf;
  4098. }
  4099. protected override string GetEventDeclaration (EventDefinition e)
  4100. {
  4101. StringBuilder buf = new StringBuilder ();
  4102. if (AppendVisibility (buf, e.AddMethod).Length == 0) {
  4103. return null;
  4104. }
  4105. AppendModifiers (buf, e.AddMethod);
  4106. buf.Append (" event ");
  4107. buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
  4108. buf.Append (e.Name).Append (';');
  4109. return buf.ToString ();
  4110. }
  4111. }
  4112. class CSharpMemberFormatter : CSharpFullMemberFormatter {
  4113. protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
  4114. {
  4115. return buf;
  4116. }
  4117. }
  4118. class DocTypeFullMemberFormatter : MemberFormatter {
  4119. public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
  4120. protected override char NestedTypeSeparator {
  4121. get {return '+';}
  4122. }
  4123. }
  4124. class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
  4125. protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
  4126. {
  4127. return buf;
  4128. }
  4129. }
  4130. class SlashDocMemberFormatter : MemberFormatter {
  4131. protected override char[] GenericTypeContainer {
  4132. get {return new char[]{'{', '}'};}
  4133. }
  4134. private bool AddTypeCount = true;
  4135. private TypeReference genDeclType;
  4136. private MethodReference genDeclMethod;
  4137. protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
  4138. {
  4139. if (type is GenericParameter) {
  4140. int l = buf.Length;
  4141. if (genDeclType != null) {
  4142. IList<GenericParameter> genArgs = genDeclType.GenericParameters;
  4143. for (int i = 0; i < genArgs.Count; ++i) {
  4144. if (genArgs [i].Name == type.Name) {
  4145. buf.Append ('`').Append (i);
  4146. break;
  4147. }
  4148. }
  4149. }
  4150. if (genDeclMethod != null) {
  4151. IList<GenericParameter> genArgs = null;
  4152. if (genDeclMethod.IsGenericMethod ()) {
  4153. genArgs = genDeclMethod.GenericParameters;
  4154. for (int i = 0; i < genArgs.Count; ++i) {
  4155. if (genArgs [i].Name == type.Name) {
  4156. buf.Append ("``").Append (i);
  4157. break;
  4158. }
  4159. }
  4160. }
  4161. }
  4162. if (genDeclType == null && genDeclMethod == null) {
  4163. // Probably from within an explicitly implemented interface member,
  4164. // where CSC uses parameter names instead of indices (why?), e.g.
  4165. // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
  4166. // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
  4167. buf.Append (type.Name);
  4168. }
  4169. if (buf.Length == l) {
  4170. throw new Exception (string.Format (
  4171. "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
  4172. type.Name, genDeclType, genDeclMethod));
  4173. }
  4174. }
  4175. else {
  4176. base.AppendTypeName (buf, type, context);
  4177. if (AddTypeCount) {
  4178. int numArgs = type.GenericParameters.Count;
  4179. if (type.DeclaringType != null)
  4180. numArgs -= type.GenericParameters.Count;
  4181. if (numArgs > 0) {
  4182. buf.Append ('`').Append (numArgs);
  4183. }
  4184. }
  4185. }
  4186. return buf;
  4187. }
  4188. protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
  4189. {
  4190. buf.Append (ArrayDelimeters [0]);
  4191. int rank = array.Rank;
  4192. if (rank > 1) {
  4193. buf.Append ("0:");
  4194. for (int i = 1; i < rank; ++i) {
  4195. buf.Append (",0:");
  4196. }
  4197. }
  4198. return buf.Append (ArrayDelimeters [1]);
  4199. }
  4200. protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
  4201. {
  4202. if (!AddTypeCount)
  4203. base.AppendGenericType (buf, type, context);
  4204. else
  4205. AppendType (buf, type, context);
  4206. return buf;
  4207. }
  4208. private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
  4209. {
  4210. List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
  4211. bool insertNested = false;
  4212. int prevParamCount = 0;
  4213. foreach (var decl in decls) {
  4214. if (insertNested)
  4215. buf.Append (NestedTypeSeparator);
  4216. insertNested = true;
  4217. base.AppendTypeName (buf, decl, context);
  4218. int argCount = DocUtils.GetGenericArgumentCount (decl);
  4219. int numArgs = argCount - prevParamCount;
  4220. prevParamCount = argCount;
  4221. if (numArgs > 0)
  4222. buf.Append ('`').Append (numArgs);
  4223. }
  4224. return buf;
  4225. }
  4226. public override string GetDeclaration (MemberReference member)
  4227. {
  4228. TypeReference r = member as TypeReference;
  4229. if (r != null) {
  4230. return "T:" + GetTypeName (r);
  4231. }
  4232. return base.GetDeclaration (member);
  4233. }
  4234. protected override string GetConstructorName (MethodReference constructor)
  4235. {
  4236. return GetMethodDefinitionName (constructor, "#ctor");
  4237. }
  4238. protected override string GetMethodName (MethodReference method)
  4239. {
  4240. string name = null;
  4241. MethodDefinition methodDef = method as MethodDefinition;
  4242. if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
  4243. name = method.Name;
  4244. else {
  4245. TypeReference iface;
  4246. MethodReference ifaceMethod;
  4247. DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
  4248. AddTypeCount = false;
  4249. name = GetTypeName (iface) + "." + ifaceMethod.Name;
  4250. AddTypeCount = true;
  4251. }
  4252. return GetMethodDefinitionName (method, name);
  4253. }
  4254. private string GetMethodDefinitionName (MethodReference method, string name)
  4255. {
  4256. StringBuilder buf = new StringBuilder ();
  4257. buf.Append (GetTypeName (method.DeclaringType));
  4258. buf.Append ('.');
  4259. buf.Append (name.Replace (".", "#"));
  4260. if (method.IsGenericMethod ()) {
  4261. IList<GenericParameter> genArgs = method.GenericParameters;
  4262. if (genArgs.Count > 0)
  4263. buf.Append ("``").Append (genArgs.Count);
  4264. }
  4265. IList<ParameterDefinition> parameters = method.Parameters;
  4266. try {
  4267. genDeclType = method.DeclaringType;
  4268. genDeclMethod = method;
  4269. AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
  4270. }
  4271. finally {
  4272. genDeclType = null;
  4273. genDeclMethod = null;
  4274. }
  4275. return buf.ToString ();
  4276. }
  4277. private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
  4278. {
  4279. if (parameters.Count == 0)
  4280. return buf;
  4281. buf.Append ('(');
  4282. AppendParameter (buf, genArgs, parameters [0]);
  4283. for (int i = 1; i < parameters.Count; ++i) {
  4284. buf.Append (',');
  4285. AppendParameter (buf, genArgs, parameters [i]);
  4286. }
  4287. return buf.Append (')');
  4288. }
  4289. private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
  4290. {
  4291. AddTypeCount = false;
  4292. buf.Append (GetTypeName (parameter.ParameterType));
  4293. AddTypeCount = true;
  4294. return buf;
  4295. }
  4296. protected override string GetPropertyName (PropertyReference property)
  4297. {
  4298. string name = null;
  4299. PropertyDefinition propertyDef = property as PropertyDefinition;
  4300. MethodDefinition method = null;
  4301. if (propertyDef != null)
  4302. method = propertyDef.GetMethod ?? propertyDef.SetMethod;
  4303. if (method != null && !DocUtils.IsExplicitlyImplemented (method))
  4304. name = property.Name;
  4305. else {
  4306. TypeReference iface;
  4307. MethodReference ifaceMethod;
  4308. DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
  4309. AddTypeCount = false;
  4310. name = string.Join ("#", new string[]{
  4311. GetTypeName (iface).Replace (".", "#"),
  4312. DocUtils.GetMember (property.Name)
  4313. });
  4314. AddTypeCount = true;
  4315. }
  4316. StringBuilder buf = new StringBuilder ();
  4317. buf.Append (GetName (property.DeclaringType));
  4318. buf.Append ('.');
  4319. buf.Append (name);
  4320. IList<ParameterDefinition> parameters = property.Parameters;
  4321. if (parameters.Count > 0) {
  4322. genDeclType = property.DeclaringType;
  4323. buf.Append ('(');
  4324. IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
  4325. AppendParameter (buf, genArgs, parameters [0]);
  4326. for (int i = 1; i < parameters.Count; ++i) {
  4327. buf.Append (',');
  4328. AppendParameter (buf, genArgs, parameters [i]);
  4329. }
  4330. buf.Append (')');
  4331. genDeclType = null;
  4332. }
  4333. return buf.ToString ();
  4334. }
  4335. protected override string GetFieldName (FieldReference field)
  4336. {
  4337. return string.Format ("{0}.{1}",
  4338. GetName (field.DeclaringType), field.Name);
  4339. }
  4340. protected override string GetEventName (EventReference e)
  4341. {
  4342. return string.Format ("{0}.{1}",
  4343. GetName (e.DeclaringType), e.Name);
  4344. }
  4345. protected override string GetTypeDeclaration (TypeDefinition type)
  4346. {
  4347. string name = GetName (type);
  4348. if (type == null)
  4349. return null;
  4350. return "T:" + name;
  4351. }
  4352. protected override string GetConstructorDeclaration (MethodDefinition constructor)
  4353. {
  4354. string name = GetName (constructor);
  4355. if (name == null)
  4356. return null;
  4357. return "M:" + name;
  4358. }
  4359. protected override string GetMethodDeclaration (MethodDefinition method)
  4360. {
  4361. string name = GetName (method);
  4362. if (name == null)
  4363. return null;
  4364. if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
  4365. genDeclType = method.DeclaringType;
  4366. genDeclMethod = method;
  4367. name += "~" + GetName (method.ReturnType);
  4368. genDeclType = null;
  4369. genDeclMethod = null;
  4370. }
  4371. return "M:" + name;
  4372. }
  4373. protected override string GetPropertyDeclaration (PropertyDefinition property)
  4374. {
  4375. string name = GetName (property);
  4376. if (name == null)
  4377. return null;
  4378. return "P:" + name;
  4379. }
  4380. protected override string GetFieldDeclaration (FieldDefinition field)
  4381. {
  4382. string name = GetName (field);
  4383. if (name == null)
  4384. return null;
  4385. return "F:" + name;
  4386. }
  4387. protected override string GetEventDeclaration (EventDefinition e)
  4388. {
  4389. string name = GetName (e);
  4390. if (name == null)
  4391. return null;
  4392. return "E:" + name;
  4393. }
  4394. }
  4395. class FileNameMemberFormatter : SlashDocMemberFormatter {
  4396. protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
  4397. {
  4398. return buf;
  4399. }
  4400. protected override char NestedTypeSeparator {
  4401. get {return '+';}
  4402. }
  4403. }
  4404. }