xpcom/analysis/type-printer.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 let dumpTypes = options['dump-types'].split(',');
michael@0 6
michael@0 7 let interestingList = {};
michael@0 8 let typelist = {};
michael@0 9
michael@0 10 function interestingType(t)
michael@0 11 {
michael@0 12 let name = t.name;
michael@0 13
michael@0 14 if (dumpTypes.some(function(n) n == name)) {
michael@0 15 interestingList[name] = t;
michael@0 16 typelist[name] = t;
michael@0 17 return true;
michael@0 18 }
michael@0 19
michael@0 20 for each (let base in t.bases) {
michael@0 21 if (base.access == 'public' && interestingType(base.type)) {
michael@0 22 typelist[name] = t;
michael@0 23 return true;
michael@0 24 }
michael@0 25 }
michael@0 26
michael@0 27 return false;
michael@0 28 }
michael@0 29
michael@0 30 function addSubtype(t, subt)
michael@0 31 {
michael@0 32 if (subt.typedef === undefined &&
michael@0 33 subt.kind === undefined)
michael@0 34 throw Error("Unexpected subtype: not class or typedef: " + subt);
michael@0 35
michael@0 36 if (t.subtypes === undefined)
michael@0 37 t.subtypes = [];
michael@0 38
michael@0 39 t.subtypes.push(subt);
michael@0 40 }
michael@0 41
michael@0 42 function process_type(t)
michael@0 43 {
michael@0 44 interestingType(t);
michael@0 45
michael@0 46 for each (let base in t.bases)
michael@0 47 addSubtype(base.type, t);
michael@0 48 }
michael@0 49
michael@0 50 function process_decl(d)
michael@0 51 {
michael@0 52 if (d.typedef !== undefined && d.memberOf)
michael@0 53 addSubtype(d.memberOf, d);
michael@0 54 }
michael@0 55
michael@0 56 function publicBases(t)
michael@0 57 {
michael@0 58 yield t;
michael@0 59
michael@0 60 for each (let base in t.bases)
michael@0 61 if (base.access == "public")
michael@0 62 for each (let gbase in publicBases(base.type))
michael@0 63 yield gbase;
michael@0 64 }
michael@0 65
michael@0 66 function publicMembers(t)
michael@0 67 {
michael@0 68 for each (let base in publicBases(t)) {
michael@0 69 for each (let member in base.members) {
michael@0 70 if (member.access === undefined)
michael@0 71 throw Error("Harumph: member without access? " + member);
michael@0 72
michael@0 73 if (member.access != "public")
michael@0 74 continue;
michael@0 75
michael@0 76 yield member;
michael@0 77 }
michael@0 78 }
michael@0 79 }
michael@0 80
michael@0 81 /**
michael@0 82 * Get the short name of a decl name. E.g. turn
michael@0 83 * "MyNamespace::MyClass::Method(int j) const" into
michael@0 84 * "Method"
michael@0 85 */
michael@0 86 function getShortName(decl)
michael@0 87 {
michael@0 88 let name = decl.name;
michael@0 89 let lp = name.lastIndexOf('(');
michael@0 90 if (lp != -1)
michael@0 91 name = name.slice(0, lp);
michael@0 92
michael@0 93 lp = name.lastIndexOf('::');
michael@0 94 if (lp != -1)
michael@0 95 name = name.slice(lp + 2);
michael@0 96
michael@0 97 return name;
michael@0 98 }
michael@0 99
michael@0 100 /**
michael@0 101 * Remove functions in a base class which were overridden in a derived
michael@0 102 * class.
michael@0 103 *
michael@0 104 * Although really, we should perhaps do this the other way around, or even
michael@0 105 * group the two together, but that can come later.
michael@0 106 */
michael@0 107 function removeOverrides(members)
michael@0 108 {
michael@0 109 let overrideMap = {};
michael@0 110 for (let i = members.length - 1; i >= 0; --i) {
michael@0 111 let m = members[i];
michael@0 112 if (!m.isFunction)
michael@0 113 continue;
michael@0 114
michael@0 115 let shortName = getShortName(m);
michael@0 116
michael@0 117 let overrides = overrideMap[shortName];
michael@0 118 if (overrides === undefined) {
michael@0 119 overrideMap[shortName] = [m];
michael@0 120 continue;
michael@0 121 }
michael@0 122
michael@0 123 let found = false;
michael@0 124 for each (let override in overrides) {
michael@0 125 if (signaturesMatch(override, m)) {
michael@0 126 // remove members[i], it was overridden
michael@0 127 members.splice(i, 1);
michael@0 128 found = true;
michael@0 129 }
michael@0 130 }
michael@0 131 if (found)
michael@0 132 continue;
michael@0 133
michael@0 134 overrides.push(m);
michael@0 135 }
michael@0 136 }
michael@0 137
michael@0 138 /**
michael@0 139 * Generates the starting position of lines within a file.
michael@0 140 */
michael@0 141 function getLineLocations(fdata)
michael@0 142 {
michael@0 143 yield 0;
michael@0 144
michael@0 145 let r = /\n/y;
michael@0 146 let pos = 0;
michael@0 147 let i = 1;
michael@0 148 for (;;) {
michael@0 149 pos = fdata.indexOf('\n', pos) + 1;
michael@0 150 if (pos == 0)
michael@0 151 break;
michael@0 152
michael@0 153 yield pos;
michael@0 154 i++;
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 /**
michael@0 159 * Find and return the doxygen comment immediately prior to the location
michael@0 160 * object that was passed in.
michael@0 161 *
michael@0 162 * @todo: parse doccomment data such as @param, @returns
michael@0 163 * @todo: parse comments for markup
michael@0 164 */
michael@0 165 function getDocComment(loc)
michael@0 166 {
michael@0 167 let fdata = read_file(loc.file);
michael@0 168 let linemap = [l for (l in getLineLocations(fdata))];
michael@0 169
michael@0 170 if (loc.line >= linemap.length) {
michael@0 171 warning("Location larger than actual header: " + loc);
michael@0 172 return <></>;
michael@0 173 }
michael@0 174
michael@0 175 let endpos = linemap[loc.line - 1] + loc.column - 1;
michael@0 176 let semipos = fdata.lastIndexOf(';', endpos);
michael@0 177 let bracepos = fdata.lastIndexOf('}', endpos);
michael@0 178 let searchslice = fdata.slice(Math.max(semipos, bracepos) + 1, endpos);
michael@0 179
michael@0 180 let m = searchslice.match(/\/\*\*[\s\S]*?\*\//gm);
michael@0 181 if (m === null)
michael@0 182 return <></>;
michael@0 183
michael@0 184 let dc = m[m.length - 1].slice(3, -2);
michael@0 185 dc = dc.replace(/^\s*(\*+[ \t]*)?/gm, "");
michael@0 186
michael@0 187 return <pre class="doccomment">{dc}</pre>;
michael@0 188 }
michael@0 189
michael@0 190 function typeName(t)
michael@0 191 {
michael@0 192 if (t.name !== undefined)
michael@0 193 return t.name;
michael@0 194
michael@0 195 if (t.isPointer)
michael@0 196 return "%s%s*".format(t.isConst ? "const " : "", typeName(t.type));
michael@0 197
michael@0 198 if (t.isReference)
michael@0 199 return "%s%s&".format(t.isConst ? "const " : "", typeName(t.type));
michael@0 200
michael@0 201 return t.toString();
michael@0 202 }
michael@0 203
michael@0 204 function publicBaseList(t)
michael@0 205 {
michael@0 206 let l = <ul/>;
michael@0 207 for each (let b in t.bases) {
michael@0 208 if (b.access == 'public')
michael@0 209 l.* += <li><a href={"/en/%s".format(b.type.name)}>{b.type.name}</a></li>;
michael@0 210 }
michael@0 211
michael@0 212 if (l.*.length() == 0)
michael@0 213 return <></>;
michael@0 214
michael@0 215 return <>
michael@0 216 <h2>Base Classes</h2>
michael@0 217 {l}
michael@0 218 </>;
michael@0 219 }
michael@0 220
michael@0 221 /**
michael@0 222 * Get a source-link for a given location.
michael@0 223 */
michael@0 224 function getLocLink(loc, text)
michael@0 225 {
michael@0 226 return <a class="loc"
michael@0 227 href={"http://hg.mozilla.org/mozilla-central/file/%s/[LOC%s]#l%i".format(options.rev, loc.file, loc.line)}>{text}</a>;
michael@0 228 }
michael@0 229
michael@0 230 function dumpType(t)
michael@0 231 {
michael@0 232 print("DUMP-TYPE(%s)".format(t.name));
michael@0 233
michael@0 234 let methodOverview = <tbody />;
michael@0 235 let methodList = <div/>;
michael@0 236 let memberList = <></>;
michael@0 237
michael@0 238 let shortNameMap = {};
michael@0 239
michael@0 240 let members = [m for (m in publicMembers(t))];
michael@0 241
michael@0 242 removeOverrides(members);
michael@0 243
michael@0 244 for each (let m in members) {
michael@0 245 let qname = m.memberOf.name + '::';
michael@0 246
michael@0 247 // we don't inherit constructors from base classes
michael@0 248 if (m.isConstructor && m.memberOf !== t)
michael@0 249 continue;
michael@0 250
michael@0 251 if (m.name.indexOf(qname) != 0)
michael@0 252 throw Error("Member name not qualified?");
michael@0 253
michael@0 254 let name = m.name.slice(qname.length);
michael@0 255
michael@0 256 if (name.indexOf('~') == 0)
michael@0 257 continue;
michael@0 258
michael@0 259 if (m.isFunction) {
michael@0 260 let innerList;
michael@0 261
michael@0 262 let shortName = getShortName(m);
michael@0 263 if (m.isConstructor)
michael@0 264 shortName = 'Constructors';
michael@0 265
michael@0 266 if (shortNameMap.hasOwnProperty(shortName)) {
michael@0 267 innerList = shortNameMap[shortName];
michael@0 268 }
michael@0 269 else {
michael@0 270 let overview =
michael@0 271 <tr><td>
michael@0 272 <a href={'#%s'.format(escape(shortName))}>{shortName}</a>
michael@0 273 </td></tr>;
michael@0 274
michael@0 275 if (m.isConstructor)
michael@0 276 methodOverview.insertChildAfter(null, overview);
michael@0 277 else
michael@0 278 methodOverview.appendChild(overview);
michael@0 279
michael@0 280 let shortMarkup =
michael@0 281 <div>
michael@0 282 <h3 id={shortName}>{shortName}</h3>
michael@0 283 <dl/>
michael@0 284 </div>;
michael@0 285
michael@0 286
michael@0 287 if (m.isConstructor)
michael@0 288 methodList.insertChildAfter(null, shortMarkup);
michael@0 289 else
michael@0 290 methodList.appendChild(shortMarkup);
michael@0 291
michael@0 292 innerList = shortMarkup.dl;
michael@0 293 shortNameMap[shortName] = innerList;
michael@0 294 }
michael@0 295
michael@0 296 let parameters = <ul/>;
michael@0 297 for each (p in m.parameters) {
michael@0 298 let name = p.name;
michael@0 299 if (name == 'this')
michael@0 300 continue;
michael@0 301
michael@0 302 if (/^D_\d+$/.test(name))
michael@0 303 name = '<anonymous>';
michael@0 304
michael@0 305 parameters.* += <li>{typeName(p.type)} {name}</li>;
michael@0 306 }
michael@0 307
michael@0 308 innerList.* +=
michael@0 309 <>
michael@0 310 <dt id={name} class="methodName">
michael@0 311 <code>{typeName(m.type.type)} {name}</code> - {getLocLink(m.loc, "source")}
michael@0 312 </dt>
michael@0 313 <dd>
michael@0 314 {getDocComment(m.loc)}
michael@0 315 {parameters.*.length() > 0 ?
michael@0 316 <>
michael@0 317 <h4>Parameters</h4>
michael@0 318 {parameters}
michael@0 319 </> : <></>}
michael@0 320 </dd>
michael@0 321 </>;
michael@0 322 }
michael@0 323 else {
michael@0 324 memberList += <li class="member">{name}</li>;
michael@0 325 }
michael@0 326 }
michael@0 327
michael@0 328 let r =
michael@0 329 <body>
michael@0 330 <p>{getLocLink(t.loc, "Class Declaration")}</p>
michael@0 331
michael@0 332 {getDocComment(t.loc)}
michael@0 333
michael@0 334 {dumpTypes.some(function(n) n == t.name) ?
michael@0 335 <>
michael@0 336 [MAP{t.name}-graph.map]
michael@0 337 <img src={"/@api/deki/pages/=en%%252F%s/files/=%s-graph.png".format(t.name, t.name)} usemap="#classes" />
michael@0 338 </> : <></>
michael@0 339 }
michael@0 340
michael@0 341 {methodOverview.*.length() > 0 ?
michael@0 342 <>
michael@0 343 <h2>Method Overview</h2>
michael@0 344 <table class="standard-table">{methodOverview}</table>
michael@0 345 </> :
michael@0 346 ""
michael@0 347 }
michael@0 348
michael@0 349 {publicBaseList(t)}
michael@0 350
michael@0 351 <h2>Data Members</h2>
michael@0 352
michael@0 353 {memberList.*.length() > 0 ?
michael@0 354 memberList :
michael@0 355 <p><em>No public members.</em></p>
michael@0 356 }
michael@0 357
michael@0 358 <h2>Methods</h2>
michael@0 359
michael@0 360 {methodList.*.length() > 0 ?
michael@0 361 methodList :
michael@0 362 <p><em>No public methods.</em></p>
michael@0 363 }
michael@0 364
michael@0 365 </body>;
michael@0 366
michael@0 367 write_file(t.name + ".html", r.toXMLString());
michael@0 368 }
michael@0 369
michael@0 370 function graphType(t)
michael@0 371 {
michael@0 372 print("GRAPH-TYPE(%s)".format(t.name));
michael@0 373
michael@0 374 let contents = "digraph classes {\n"
michael@0 375 + " node [shape=rectangle fontsize=11]\n"
michael@0 376 + " %s;\n".format(t.name);
michael@0 377
michael@0 378 function graphClass(c)
michael@0 379 {
michael@0 380 contents += '%s [URL="http://developer.mozilla.org/en/%s"]\n'.format(c.name, c.name);
michael@0 381
michael@0 382 for each (let st in c.subtypes) {
michael@0 383 contents += " %s -> %s;\n".format(c.name, st.name);
michael@0 384 graphClass(st);
michael@0 385 }
michael@0 386 }
michael@0 387
michael@0 388 graphClass(t);
michael@0 389
michael@0 390 contents += "}\n";
michael@0 391
michael@0 392 write_file(t.name + "-graph.gv", contents);
michael@0 393 }
michael@0 394
michael@0 395 function input_end()
michael@0 396 {
michael@0 397 for (let p in typelist)
michael@0 398 dumpType(typelist[p]);
michael@0 399
michael@0 400 for (let n in interestingList)
michael@0 401 graphType(interestingList[n]);
michael@0 402 }

mercurial