1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/analysis/type-printer.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,402 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +let dumpTypes = options['dump-types'].split(','); 1.9 + 1.10 +let interestingList = {}; 1.11 +let typelist = {}; 1.12 + 1.13 +function interestingType(t) 1.14 +{ 1.15 + let name = t.name; 1.16 + 1.17 + if (dumpTypes.some(function(n) n == name)) { 1.18 + interestingList[name] = t; 1.19 + typelist[name] = t; 1.20 + return true; 1.21 + } 1.22 + 1.23 + for each (let base in t.bases) { 1.24 + if (base.access == 'public' && interestingType(base.type)) { 1.25 + typelist[name] = t; 1.26 + return true; 1.27 + } 1.28 + } 1.29 + 1.30 + return false; 1.31 +} 1.32 + 1.33 +function addSubtype(t, subt) 1.34 +{ 1.35 + if (subt.typedef === undefined && 1.36 + subt.kind === undefined) 1.37 + throw Error("Unexpected subtype: not class or typedef: " + subt); 1.38 + 1.39 + if (t.subtypes === undefined) 1.40 + t.subtypes = []; 1.41 + 1.42 + t.subtypes.push(subt); 1.43 +} 1.44 + 1.45 +function process_type(t) 1.46 +{ 1.47 + interestingType(t); 1.48 + 1.49 + for each (let base in t.bases) 1.50 + addSubtype(base.type, t); 1.51 +} 1.52 + 1.53 +function process_decl(d) 1.54 +{ 1.55 + if (d.typedef !== undefined && d.memberOf) 1.56 + addSubtype(d.memberOf, d); 1.57 +} 1.58 + 1.59 +function publicBases(t) 1.60 +{ 1.61 + yield t; 1.62 + 1.63 + for each (let base in t.bases) 1.64 + if (base.access == "public") 1.65 + for each (let gbase in publicBases(base.type)) 1.66 + yield gbase; 1.67 +} 1.68 + 1.69 +function publicMembers(t) 1.70 +{ 1.71 + for each (let base in publicBases(t)) { 1.72 + for each (let member in base.members) { 1.73 + if (member.access === undefined) 1.74 + throw Error("Harumph: member without access? " + member); 1.75 + 1.76 + if (member.access != "public") 1.77 + continue; 1.78 + 1.79 + yield member; 1.80 + } 1.81 + } 1.82 +} 1.83 + 1.84 +/** 1.85 + * Get the short name of a decl name. E.g. turn 1.86 + * "MyNamespace::MyClass::Method(int j) const" into 1.87 + * "Method" 1.88 + */ 1.89 +function getShortName(decl) 1.90 +{ 1.91 + let name = decl.name; 1.92 + let lp = name.lastIndexOf('('); 1.93 + if (lp != -1) 1.94 + name = name.slice(0, lp); 1.95 + 1.96 + lp = name.lastIndexOf('::'); 1.97 + if (lp != -1) 1.98 + name = name.slice(lp + 2); 1.99 + 1.100 + return name; 1.101 +} 1.102 + 1.103 +/** 1.104 + * Remove functions in a base class which were overridden in a derived 1.105 + * class. 1.106 + * 1.107 + * Although really, we should perhaps do this the other way around, or even 1.108 + * group the two together, but that can come later. 1.109 + */ 1.110 +function removeOverrides(members) 1.111 +{ 1.112 + let overrideMap = {}; 1.113 + for (let i = members.length - 1; i >= 0; --i) { 1.114 + let m = members[i]; 1.115 + if (!m.isFunction) 1.116 + continue; 1.117 + 1.118 + let shortName = getShortName(m); 1.119 + 1.120 + let overrides = overrideMap[shortName]; 1.121 + if (overrides === undefined) { 1.122 + overrideMap[shortName] = [m]; 1.123 + continue; 1.124 + } 1.125 + 1.126 + let found = false; 1.127 + for each (let override in overrides) { 1.128 + if (signaturesMatch(override, m)) { 1.129 + // remove members[i], it was overridden 1.130 + members.splice(i, 1); 1.131 + found = true; 1.132 + } 1.133 + } 1.134 + if (found) 1.135 + continue; 1.136 + 1.137 + overrides.push(m); 1.138 + } 1.139 +} 1.140 + 1.141 +/** 1.142 + * Generates the starting position of lines within a file. 1.143 + */ 1.144 +function getLineLocations(fdata) 1.145 +{ 1.146 + yield 0; 1.147 + 1.148 + let r = /\n/y; 1.149 + let pos = 0; 1.150 + let i = 1; 1.151 + for (;;) { 1.152 + pos = fdata.indexOf('\n', pos) + 1; 1.153 + if (pos == 0) 1.154 + break; 1.155 + 1.156 + yield pos; 1.157 + i++; 1.158 + } 1.159 +} 1.160 + 1.161 +/** 1.162 + * Find and return the doxygen comment immediately prior to the location 1.163 + * object that was passed in. 1.164 + * 1.165 + * @todo: parse doccomment data such as @param, @returns 1.166 + * @todo: parse comments for markup 1.167 + */ 1.168 +function getDocComment(loc) 1.169 +{ 1.170 + let fdata = read_file(loc.file); 1.171 + let linemap = [l for (l in getLineLocations(fdata))]; 1.172 + 1.173 + if (loc.line >= linemap.length) { 1.174 + warning("Location larger than actual header: " + loc); 1.175 + return <></>; 1.176 + } 1.177 + 1.178 + let endpos = linemap[loc.line - 1] + loc.column - 1; 1.179 + let semipos = fdata.lastIndexOf(';', endpos); 1.180 + let bracepos = fdata.lastIndexOf('}', endpos); 1.181 + let searchslice = fdata.slice(Math.max(semipos, bracepos) + 1, endpos); 1.182 + 1.183 + let m = searchslice.match(/\/\*\*[\s\S]*?\*\//gm); 1.184 + if (m === null) 1.185 + return <></>; 1.186 + 1.187 + let dc = m[m.length - 1].slice(3, -2); 1.188 + dc = dc.replace(/^\s*(\*+[ \t]*)?/gm, ""); 1.189 + 1.190 + return <pre class="doccomment">{dc}</pre>; 1.191 +} 1.192 + 1.193 +function typeName(t) 1.194 +{ 1.195 + if (t.name !== undefined) 1.196 + return t.name; 1.197 + 1.198 + if (t.isPointer) 1.199 + return "%s%s*".format(t.isConst ? "const " : "", typeName(t.type)); 1.200 + 1.201 + if (t.isReference) 1.202 + return "%s%s&".format(t.isConst ? "const " : "", typeName(t.type)); 1.203 + 1.204 + return t.toString(); 1.205 +} 1.206 + 1.207 +function publicBaseList(t) 1.208 +{ 1.209 + let l = <ul/>; 1.210 + for each (let b in t.bases) { 1.211 + if (b.access == 'public') 1.212 + l.* += <li><a href={"/en/%s".format(b.type.name)}>{b.type.name}</a></li>; 1.213 + } 1.214 + 1.215 + if (l.*.length() == 0) 1.216 + return <></>; 1.217 + 1.218 + return <> 1.219 + <h2>Base Classes</h2> 1.220 + {l} 1.221 + </>; 1.222 +} 1.223 + 1.224 +/** 1.225 + * Get a source-link for a given location. 1.226 + */ 1.227 +function getLocLink(loc, text) 1.228 +{ 1.229 + return <a class="loc" 1.230 + href={"http://hg.mozilla.org/mozilla-central/file/%s/[LOC%s]#l%i".format(options.rev, loc.file, loc.line)}>{text}</a>; 1.231 +} 1.232 + 1.233 +function dumpType(t) 1.234 +{ 1.235 + print("DUMP-TYPE(%s)".format(t.name)); 1.236 + 1.237 + let methodOverview = <tbody />; 1.238 + let methodList = <div/>; 1.239 + let memberList = <></>; 1.240 + 1.241 + let shortNameMap = {}; 1.242 + 1.243 + let members = [m for (m in publicMembers(t))]; 1.244 + 1.245 + removeOverrides(members); 1.246 + 1.247 + for each (let m in members) { 1.248 + let qname = m.memberOf.name + '::'; 1.249 + 1.250 + // we don't inherit constructors from base classes 1.251 + if (m.isConstructor && m.memberOf !== t) 1.252 + continue; 1.253 + 1.254 + if (m.name.indexOf(qname) != 0) 1.255 + throw Error("Member name not qualified?"); 1.256 + 1.257 + let name = m.name.slice(qname.length); 1.258 + 1.259 + if (name.indexOf('~') == 0) 1.260 + continue; 1.261 + 1.262 + if (m.isFunction) { 1.263 + let innerList; 1.264 + 1.265 + let shortName = getShortName(m); 1.266 + if (m.isConstructor) 1.267 + shortName = 'Constructors'; 1.268 + 1.269 + if (shortNameMap.hasOwnProperty(shortName)) { 1.270 + innerList = shortNameMap[shortName]; 1.271 + } 1.272 + else { 1.273 + let overview = 1.274 + <tr><td> 1.275 + <a href={'#%s'.format(escape(shortName))}>{shortName}</a> 1.276 + </td></tr>; 1.277 + 1.278 + if (m.isConstructor) 1.279 + methodOverview.insertChildAfter(null, overview); 1.280 + else 1.281 + methodOverview.appendChild(overview); 1.282 + 1.283 + let shortMarkup = 1.284 + <div> 1.285 + <h3 id={shortName}>{shortName}</h3> 1.286 + <dl/> 1.287 + </div>; 1.288 + 1.289 + 1.290 + if (m.isConstructor) 1.291 + methodList.insertChildAfter(null, shortMarkup); 1.292 + else 1.293 + methodList.appendChild(shortMarkup); 1.294 + 1.295 + innerList = shortMarkup.dl; 1.296 + shortNameMap[shortName] = innerList; 1.297 + } 1.298 + 1.299 + let parameters = <ul/>; 1.300 + for each (p in m.parameters) { 1.301 + let name = p.name; 1.302 + if (name == 'this') 1.303 + continue; 1.304 + 1.305 + if (/^D_\d+$/.test(name)) 1.306 + name = '<anonymous>'; 1.307 + 1.308 + parameters.* += <li>{typeName(p.type)} {name}</li>; 1.309 + } 1.310 + 1.311 + innerList.* += 1.312 + <> 1.313 + <dt id={name} class="methodName"> 1.314 + <code>{typeName(m.type.type)} {name}</code> - {getLocLink(m.loc, "source")} 1.315 + </dt> 1.316 + <dd> 1.317 + {getDocComment(m.loc)} 1.318 + {parameters.*.length() > 0 ? 1.319 + <> 1.320 + <h4>Parameters</h4> 1.321 + {parameters} 1.322 + </> : <></>} 1.323 + </dd> 1.324 + </>; 1.325 + } 1.326 + else { 1.327 + memberList += <li class="member">{name}</li>; 1.328 + } 1.329 + } 1.330 + 1.331 + let r = 1.332 + <body> 1.333 + <p>{getLocLink(t.loc, "Class Declaration")}</p> 1.334 + 1.335 + {getDocComment(t.loc)} 1.336 + 1.337 + {dumpTypes.some(function(n) n == t.name) ? 1.338 + <> 1.339 + [MAP{t.name}-graph.map] 1.340 + <img src={"/@api/deki/pages/=en%%252F%s/files/=%s-graph.png".format(t.name, t.name)} usemap="#classes" /> 1.341 + </> : <></> 1.342 + } 1.343 + 1.344 + {methodOverview.*.length() > 0 ? 1.345 + <> 1.346 + <h2>Method Overview</h2> 1.347 + <table class="standard-table">{methodOverview}</table> 1.348 + </> : 1.349 + "" 1.350 + } 1.351 + 1.352 + {publicBaseList(t)} 1.353 + 1.354 + <h2>Data Members</h2> 1.355 + 1.356 + {memberList.*.length() > 0 ? 1.357 + memberList : 1.358 + <p><em>No public members.</em></p> 1.359 + } 1.360 + 1.361 + <h2>Methods</h2> 1.362 + 1.363 + {methodList.*.length() > 0 ? 1.364 + methodList : 1.365 + <p><em>No public methods.</em></p> 1.366 + } 1.367 + 1.368 + </body>; 1.369 + 1.370 + write_file(t.name + ".html", r.toXMLString()); 1.371 +} 1.372 + 1.373 +function graphType(t) 1.374 +{ 1.375 + print("GRAPH-TYPE(%s)".format(t.name)); 1.376 + 1.377 + let contents = "digraph classes {\n" 1.378 + + " node [shape=rectangle fontsize=11]\n" 1.379 + + " %s;\n".format(t.name); 1.380 + 1.381 + function graphClass(c) 1.382 + { 1.383 + contents += '%s [URL="http://developer.mozilla.org/en/%s"]\n'.format(c.name, c.name); 1.384 + 1.385 + for each (let st in c.subtypes) { 1.386 + contents += " %s -> %s;\n".format(c.name, st.name); 1.387 + graphClass(st); 1.388 + } 1.389 + } 1.390 + 1.391 + graphClass(t); 1.392 + 1.393 + contents += "}\n"; 1.394 + 1.395 + write_file(t.name + "-graph.gv", contents); 1.396 +} 1.397 + 1.398 +function input_end() 1.399 +{ 1.400 + for (let p in typelist) 1.401 + dumpType(typelist[p]); 1.402 + 1.403 + for (let n in interestingList) 1.404 + graphType(interestingList[n]); 1.405 +}