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