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