| |
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/. */ |
| |
4 |
| |
5 let dumpTypes = options['dump-types'].split(','); |
| |
6 |
| |
7 let interestingList = {}; |
| |
8 let typelist = {}; |
| |
9 |
| |
10 function interestingType(t) |
| |
11 { |
| |
12 let name = t.name; |
| |
13 |
| |
14 if (dumpTypes.some(function(n) n == name)) { |
| |
15 interestingList[name] = t; |
| |
16 typelist[name] = t; |
| |
17 return true; |
| |
18 } |
| |
19 |
| |
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 } |
| |
26 |
| |
27 return false; |
| |
28 } |
| |
29 |
| |
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); |
| |
35 |
| |
36 if (t.subtypes === undefined) |
| |
37 t.subtypes = []; |
| |
38 |
| |
39 t.subtypes.push(subt); |
| |
40 } |
| |
41 |
| |
42 function process_type(t) |
| |
43 { |
| |
44 interestingType(t); |
| |
45 |
| |
46 for each (let base in t.bases) |
| |
47 addSubtype(base.type, t); |
| |
48 } |
| |
49 |
| |
50 function process_decl(d) |
| |
51 { |
| |
52 if (d.typedef !== undefined && d.memberOf) |
| |
53 addSubtype(d.memberOf, d); |
| |
54 } |
| |
55 |
| |
56 function publicBases(t) |
| |
57 { |
| |
58 yield t; |
| |
59 |
| |
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 } |
| |
65 |
| |
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); |
| |
72 |
| |
73 if (member.access != "public") |
| |
74 continue; |
| |
75 |
| |
76 yield member; |
| |
77 } |
| |
78 } |
| |
79 } |
| |
80 |
| |
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); |
| |
92 |
| |
93 lp = name.lastIndexOf('::'); |
| |
94 if (lp != -1) |
| |
95 name = name.slice(lp + 2); |
| |
96 |
| |
97 return name; |
| |
98 } |
| |
99 |
| |
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; |
| |
114 |
| |
115 let shortName = getShortName(m); |
| |
116 |
| |
117 let overrides = overrideMap[shortName]; |
| |
118 if (overrides === undefined) { |
| |
119 overrideMap[shortName] = [m]; |
| |
120 continue; |
| |
121 } |
| |
122 |
| |
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; |
| |
133 |
| |
134 overrides.push(m); |
| |
135 } |
| |
136 } |
| |
137 |
| |
138 /** |
| |
139 * Generates the starting position of lines within a file. |
| |
140 */ |
| |
141 function getLineLocations(fdata) |
| |
142 { |
| |
143 yield 0; |
| |
144 |
| |
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; |
| |
152 |
| |
153 yield pos; |
| |
154 i++; |
| |
155 } |
| |
156 } |
| |
157 |
| |
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))]; |
| |
169 |
| |
170 if (loc.line >= linemap.length) { |
| |
171 warning("Location larger than actual header: " + loc); |
| |
172 return <></>; |
| |
173 } |
| |
174 |
| |
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); |
| |
179 |
| |
180 let m = searchslice.match(/\/\*\*[\s\S]*?\*\//gm); |
| |
181 if (m === null) |
| |
182 return <></>; |
| |
183 |
| |
184 let dc = m[m.length - 1].slice(3, -2); |
| |
185 dc = dc.replace(/^\s*(\*+[ \t]*)?/gm, ""); |
| |
186 |
| |
187 return <pre class="doccomment">{dc}</pre>; |
| |
188 } |
| |
189 |
| |
190 function typeName(t) |
| |
191 { |
| |
192 if (t.name !== undefined) |
| |
193 return t.name; |
| |
194 |
| |
195 if (t.isPointer) |
| |
196 return "%s%s*".format(t.isConst ? "const " : "", typeName(t.type)); |
| |
197 |
| |
198 if (t.isReference) |
| |
199 return "%s%s&".format(t.isConst ? "const " : "", typeName(t.type)); |
| |
200 |
| |
201 return t.toString(); |
| |
202 } |
| |
203 |
| |
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 } |
| |
211 |
| |
212 if (l.*.length() == 0) |
| |
213 return <></>; |
| |
214 |
| |
215 return <> |
| |
216 <h2>Base Classes</h2> |
| |
217 {l} |
| |
218 </>; |
| |
219 } |
| |
220 |
| |
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 } |
| |
229 |
| |
230 function dumpType(t) |
| |
231 { |
| |
232 print("DUMP-TYPE(%s)".format(t.name)); |
| |
233 |
| |
234 let methodOverview = <tbody />; |
| |
235 let methodList = <div/>; |
| |
236 let memberList = <></>; |
| |
237 |
| |
238 let shortNameMap = {}; |
| |
239 |
| |
240 let members = [m for (m in publicMembers(t))]; |
| |
241 |
| |
242 removeOverrides(members); |
| |
243 |
| |
244 for each (let m in members) { |
| |
245 let qname = m.memberOf.name + '::'; |
| |
246 |
| |
247 // we don't inherit constructors from base classes |
| |
248 if (m.isConstructor && m.memberOf !== t) |
| |
249 continue; |
| |
250 |
| |
251 if (m.name.indexOf(qname) != 0) |
| |
252 throw Error("Member name not qualified?"); |
| |
253 |
| |
254 let name = m.name.slice(qname.length); |
| |
255 |
| |
256 if (name.indexOf('~') == 0) |
| |
257 continue; |
| |
258 |
| |
259 if (m.isFunction) { |
| |
260 let innerList; |
| |
261 |
| |
262 let shortName = getShortName(m); |
| |
263 if (m.isConstructor) |
| |
264 shortName = 'Constructors'; |
| |
265 |
| |
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>; |
| |
274 |
| |
275 if (m.isConstructor) |
| |
276 methodOverview.insertChildAfter(null, overview); |
| |
277 else |
| |
278 methodOverview.appendChild(overview); |
| |
279 |
| |
280 let shortMarkup = |
| |
281 <div> |
| |
282 <h3 id={shortName}>{shortName}</h3> |
| |
283 <dl/> |
| |
284 </div>; |
| |
285 |
| |
286 |
| |
287 if (m.isConstructor) |
| |
288 methodList.insertChildAfter(null, shortMarkup); |
| |
289 else |
| |
290 methodList.appendChild(shortMarkup); |
| |
291 |
| |
292 innerList = shortMarkup.dl; |
| |
293 shortNameMap[shortName] = innerList; |
| |
294 } |
| |
295 |
| |
296 let parameters = <ul/>; |
| |
297 for each (p in m.parameters) { |
| |
298 let name = p.name; |
| |
299 if (name == 'this') |
| |
300 continue; |
| |
301 |
| |
302 if (/^D_\d+$/.test(name)) |
| |
303 name = '<anonymous>'; |
| |
304 |
| |
305 parameters.* += <li>{typeName(p.type)} {name}</li>; |
| |
306 } |
| |
307 |
| |
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 } |
| |
327 |
| |
328 let r = |
| |
329 <body> |
| |
330 <p>{getLocLink(t.loc, "Class Declaration")}</p> |
| |
331 |
| |
332 {getDocComment(t.loc)} |
| |
333 |
| |
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 } |
| |
340 |
| |
341 {methodOverview.*.length() > 0 ? |
| |
342 <> |
| |
343 <h2>Method Overview</h2> |
| |
344 <table class="standard-table">{methodOverview}</table> |
| |
345 </> : |
| |
346 "" |
| |
347 } |
| |
348 |
| |
349 {publicBaseList(t)} |
| |
350 |
| |
351 <h2>Data Members</h2> |
| |
352 |
| |
353 {memberList.*.length() > 0 ? |
| |
354 memberList : |
| |
355 <p><em>No public members.</em></p> |
| |
356 } |
| |
357 |
| |
358 <h2>Methods</h2> |
| |
359 |
| |
360 {methodList.*.length() > 0 ? |
| |
361 methodList : |
| |
362 <p><em>No public methods.</em></p> |
| |
363 } |
| |
364 |
| |
365 </body>; |
| |
366 |
| |
367 write_file(t.name + ".html", r.toXMLString()); |
| |
368 } |
| |
369 |
| |
370 function graphType(t) |
| |
371 { |
| |
372 print("GRAPH-TYPE(%s)".format(t.name)); |
| |
373 |
| |
374 let contents = "digraph classes {\n" |
| |
375 + " node [shape=rectangle fontsize=11]\n" |
| |
376 + " %s;\n".format(t.name); |
| |
377 |
| |
378 function graphClass(c) |
| |
379 { |
| |
380 contents += '%s [URL="http://developer.mozilla.org/en/%s"]\n'.format(c.name, c.name); |
| |
381 |
| |
382 for each (let st in c.subtypes) { |
| |
383 contents += " %s -> %s;\n".format(c.name, st.name); |
| |
384 graphClass(st); |
| |
385 } |
| |
386 } |
| |
387 |
| |
388 graphClass(t); |
| |
389 |
| |
390 contents += "}\n"; |
| |
391 |
| |
392 write_file(t.name + "-graph.gv", contents); |
| |
393 } |
| |
394 |
| |
395 function input_end() |
| |
396 { |
| |
397 for (let p in typelist) |
| |
398 dumpType(typelist[p]); |
| |
399 |
| |
400 for (let n in interestingList) |
| |
401 graphType(interestingList[n]); |
| |
402 } |