bug 313956: expand installer .exe contents to make complete mar. r=ted.
[gecko.git] / xpcom / analysis / type-printer.js
blob093d1d48bd3d56d34c3d48decc111b3ad8028a31
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 /**
78  * Get the short name of a decl name. E.g. turn
79  * "MyNamespace::MyClass::Method(int j) const" into
80  * "Method"
81  */
82 function getShortName(decl)
84   let name = decl.name;
85   let lp = name.lastIndexOf('(');
86   if (lp != -1)
87     name = name.slice(0, lp);
88   
89   lp = name.lastIndexOf('::');
90   if (lp != -1)
91     name = name.slice(lp + 2);
93   return name;
96 /**
97  * Remove functions in a base class which were overridden in a derived
98  * class.
99  *
100  * Although really, we should perhaps do this the other way around, or even
101  * group the two together, but that can come later.
102  */ 
103 function removeOverrides(members)
105   let overrideMap = {};
106   for (let i = members.length - 1; i >= 0; --i) {
107     let m = members[i];
108     if (!m.isFunction)
109       continue;
111     let shortName = getShortName(m);
113     let overrides = overrideMap[shortName];
114     if (overrides === undefined) {
115       overrideMap[shortName] = [m];
116       continue;
117     }
119     let found = false;
120     for each (let override in overrides) {
121       if (signaturesMatch(override, m)) {
122         // remove members[i], it was overridden
123         members.splice(i, 1);
124         found = true;
125       }
126     }
127     if (found)
128       continue;
129          
130     overrides.push(m);
131   }
135  * Generates the starting position of lines within a file.
136  */
137 function getLineLocations(fdata)
139   yield 0;
140   
141   let r = /\n/y;
142   let pos = 0;
143   let i = 1;
144   for (;;) {
145     pos = fdata.indexOf('\n', pos) + 1;
146     if (pos == 0)
147       break;
149     yield pos;
150     i++;
151   }
153     
155  * Find and return the doxygen comment immediately prior to the location
156  * object that was passed in.
157  * 
158  * @todo: parse doccomment data such as @param, @returns
159  * @todo: parse comments for markup
160  */
161 function getDocComment(loc)
163   let fdata = read_file(loc.file);
164   let linemap = [l for (l in getLineLocations(fdata))];
165   
166   if (loc.line >= linemap.length) {
167     warning("Location larger than actual header: " + loc);
168     return <></>;
169   }
170   
171   let endpos = linemap[loc.line - 1] + loc.column - 1;
172   let semipos = fdata.lastIndexOf(';', endpos);
173   let bracepos = fdata.lastIndexOf('}', endpos);
174   let searchslice = fdata.slice(Math.max(semipos, bracepos) + 1, endpos);
176   let m = searchslice.match(/\/\*\*[\s\S]*?\*\//gm);
177   if (m === null)
178     return <></>;
179   
180   let dc = m[m.length - 1].slice(3, -2);
181   dc = dc.replace(/^\s*(\*+[ \t]*)?/gm, "");
183   return <pre class="doccomment">{dc}</pre>;
186 function typeName(t)
188   if (t.name !== undefined)
189     return t.name;
191   if (t.isPointer)
192     return "%s%s*".format(t.isConst ? "const " : "", typeName(t.type));
193   
194   if (t.isReference)
195     return "%s%s&".format(t.isConst ? "const " : "", typeName(t.type));
197   return t.toString();
200 function publicBaseList(t)
202   let l = <ul/>;
203   for each (let b in t.bases) {
204     if (b.access == 'public')
205       l.* += <li><a href={"/en/%s".format(b.type.name)}>{b.type.name}</a></li>;
206   }
208   if (l.*.length() == 0)
209     return <></>;
210   
211   return <>
212     <h2>Base Classes</h2>
213     {l}
214   </>;
218  * Get a source-link for a given location.
219  */
220 function getLocLink(loc, text)
222   return <a class="loc"
223             href={"http://hg.mozilla.org/mozilla-central/file/%s/[LOC%s]#l%i".format(options.rev, loc.file, loc.line)}>{text}</a>;
226 function dumpType(t)
228   print("DUMP-TYPE(%s)".format(t.name));
230   let methodOverview = <tbody />;
231   let methodList = <div/>;
232   let memberList = <></>;
234   let shortNameMap = {};
236   let members = [m for (m in publicMembers(t))];
237   
238   removeOverrides(members);
240   for each (let m in members) {
241     let qname = m.memberOf.name + '::';
243     // we don't inherit constructors from base classes
244     if (m.isConstructor && m.memberOf !== t)
245       continue;
246     
247     if (m.name.indexOf(qname) != 0)
248       throw Error("Member name not qualified?");
249     
250     let name = m.name.slice(qname.length);
251     
252     if (name.indexOf('~') == 0)
253       continue;
255     if (m.isFunction) {
256       let innerList;
258       let shortName = getShortName(m);
259       if (m.isConstructor)
260         shortName = 'Constructors';
262       if (shortNameMap.hasOwnProperty(shortName)) {
263         innerList = shortNameMap[shortName];
264       }
265       else {
266         let overview = 
267           <tr><td>
268             <a href={'#%s'.format(escape(shortName))}>{shortName}</a>
269           </td></tr>;
271         if (m.isConstructor)
272           methodOverview.insertChildAfter(null, overview);
273         else
274           methodOverview.appendChild(overview);
275         
276         let shortMarkup =
277           <div>
278             <h3 id={shortName}>{shortName}</h3>
279             <dl/>
280           </div>;
282         
283         if (m.isConstructor)
284           methodList.insertChildAfter(null, shortMarkup);
285         else
286           methodList.appendChild(shortMarkup);
288         innerList = shortMarkup.dl;
289         shortNameMap[shortName] = innerList;
290       }
291       
292       let parameters = <ul/>;
293       for each (p in m.parameters) {
294         let name = p.name;
295         if (name == 'this')
296           continue;
297         
298         if (/^D_\d+$/.test(name))
299           name = '<anonymous>';
300         
301         parameters.* += <li>{typeName(p.type)} {name}</li>;
302       }
304       innerList.* +=
305         <>
306           <dt id={name} class="methodName">
307             <code>{typeName(m.type.type)} {name}</code> - {getLocLink(m.loc, "source")}
308           </dt>
309           <dd>
310             {getDocComment(m.loc)}
311             {parameters.*.length() > 0 ?
312              <>
313                <h4>Parameters</h4>
314                {parameters}
315              </> : <></>}
316           </dd>
317         </>;
318     }
319     else {
320       memberList += <li class="member">{name}</li>;
321     }
322   }
324   let r =
325     <body>
326       <p>{getLocLink(t.loc, "Class Declaration")}</p>
327   
328       {getDocComment(t.loc)}
330       {dumpTypes.some(function(n) n == t.name) ?
331          <>
332            [MAP{t.name}-graph.map]
333            <img src={"/@api/deki/pages/=en%%252F%s/files/=%s-graph.png".format(t.name, t.name)} usemap="#classes" />
334          </> : <></>
335       }
336   
337       {methodOverview.*.length() > 0 ?
338          <>
339            <h2>Method Overview</h2>
340            <table class="standard-table">{methodOverview}</table>
341          </> :
342          ""
343       }
345       {publicBaseList(t)}
346   
347       <h2>Data Members</h2>
349       {memberList.*.length() > 0 ?
350          memberList :
351          <p><em>No public members.</em></p>
352       }
354       <h2>Methods</h2>
355   
356       {methodList.*.length() > 0 ?
357          methodList :
358          <p><em>No public methods.</em></p>
359       }
360   
361     </body>;
363   write_file(t.name + ".html", r.toXMLString());
366 function graphType(t)
368   print("GRAPH-TYPE(%s)".format(t.name));
370   let contents = "digraph classes {\n"
371                + "  node [shape=rectangle fontsize=11]\n"
372                + "  %s;\n".format(t.name);
374   function graphClass(c)
375   {
376     contents += '%s [URL="http://developer.mozilla.org/en/%s"]\n'.format(c.name, c.name);
377     
378     for each (let st in c.subtypes) {
379       contents += "  %s -> %s;\n".format(c.name, st.name);
380       graphClass(st);
381     }
382   }
384   graphClass(t);
385   
386   contents += "}\n";
387   
388   write_file(t.name + "-graph.gv", contents);
390   
391 function input_end()
393   for (let p in typelist)
394     dumpType(typelist[p]);
396   for (let n in interestingList)
397     graphType(interestingList[n]);