зеркало из https://github.com/mozilla/pjs.git
414 строки
8.7 KiB
JavaScript
414 строки
8.7 KiB
JavaScript
let dumpTypes = options['dump-types'].split(',');
|
|
|
|
let interestingList = {};
|
|
let typelist = {};
|
|
|
|
function interestingType(t)
|
|
{
|
|
let name = t.name;
|
|
|
|
if (dumpTypes.some(function(n) n == name)) {
|
|
interestingList[name] = t;
|
|
typelist[name] = t;
|
|
return true;
|
|
}
|
|
|
|
for each (let base in t.bases) {
|
|
if (base.access == 'public' && interestingType(base.type)) {
|
|
typelist[name] = t;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function addSubtype(t, subt)
|
|
{
|
|
if (subt.typedef === undefined &&
|
|
subt.kind === undefined)
|
|
throw Error("Unexpected subtype: not class or typedef: " + subt);
|
|
|
|
if (t.subtypes === undefined)
|
|
t.subtypes = [];
|
|
|
|
t.subtypes.push(subt);
|
|
}
|
|
|
|
function process_type(t)
|
|
{
|
|
interestingType(t);
|
|
|
|
for each (let base in t.bases)
|
|
addSubtype(base.type, t);
|
|
}
|
|
|
|
function process_decl(d)
|
|
{
|
|
if (d.typedef !== undefined && d.memberOf)
|
|
addSubtype(d.memberOf, d);
|
|
}
|
|
|
|
function publicBases(t)
|
|
{
|
|
yield t;
|
|
|
|
for each (let base in t.bases)
|
|
if (base.access == "public")
|
|
for each (let gbase in publicBases(base.type))
|
|
yield gbase;
|
|
}
|
|
|
|
function publicMembers(t)
|
|
{
|
|
for each (let base in publicBases(t)) {
|
|
for each (let member in base.members) {
|
|
if (member.access === undefined)
|
|
throw Error("Harumph: member without access? " + member);
|
|
|
|
if (member.access != "public")
|
|
continue;
|
|
|
|
yield member;
|
|
}
|
|
}
|
|
}
|
|
|
|
function signaturesMatch(m1, m2)
|
|
{
|
|
let p1 = m1.type.parameters;
|
|
let p2 = m2.type.parameters;
|
|
|
|
if (p1.length != p2.length)
|
|
return false;
|
|
|
|
for (let i = 0; i < p1.length; ++i)
|
|
if (p1[i] !== p2[i])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the short name of a decl name. E.g. turn
|
|
* "MyNamespace::MyClass::Method(int j) const" into
|
|
* "Method"
|
|
*/
|
|
function getShortName(decl)
|
|
{
|
|
let name = decl.name;
|
|
let lp = name.lastIndexOf('(');
|
|
if (lp != -1)
|
|
name = name.slice(0, lp);
|
|
|
|
lp = name.lastIndexOf('::');
|
|
if (lp != -1)
|
|
name = name.slice(lp + 2);
|
|
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Remove functions in a base class which were overridden in a derived
|
|
* class.
|
|
*
|
|
* Although really, we should perhaps do this the other way around, or even
|
|
* group the two together, but that can come later.
|
|
*/
|
|
function removeOverrides(members)
|
|
{
|
|
let overrideMap = {};
|
|
for (let i = members.length - 1; i >= 0; --i) {
|
|
let m = members[i];
|
|
if (!m.isFunction)
|
|
continue;
|
|
|
|
let shortName = getShortName(m);
|
|
|
|
let overrides = overrideMap[shortName];
|
|
if (overrides === undefined) {
|
|
overrideMap[shortName] = [m];
|
|
continue;
|
|
}
|
|
|
|
let found = false;
|
|
for each (let override in overrides) {
|
|
if (signaturesMatch(override, m)) {
|
|
// remove members[i], it was overridden
|
|
members.splice(i, 1);
|
|
found = true;
|
|
}
|
|
}
|
|
if (found)
|
|
continue;
|
|
|
|
overrides.push(m);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the starting position of lines within a file.
|
|
*/
|
|
function getLineLocations(fdata)
|
|
{
|
|
yield 0;
|
|
|
|
let r = /\n/y;
|
|
let pos = 0;
|
|
let i = 1;
|
|
for (;;) {
|
|
pos = fdata.indexOf('\n', pos) + 1;
|
|
if (pos == 0)
|
|
break;
|
|
|
|
yield pos;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find and return the doxygen comment immediately prior to the location
|
|
* object that was passed in.
|
|
*
|
|
* @todo: parse doccomment data such as @param, @returns
|
|
* @todo: parse comments for markup
|
|
*/
|
|
function getDocComment(loc)
|
|
{
|
|
let fdata = read_file(loc.file);
|
|
let linemap = [l for (l in getLineLocations(fdata))];
|
|
|
|
if (loc.line >= linemap.length) {
|
|
warning("Location larger than actual header: " + loc);
|
|
return <></>;
|
|
}
|
|
|
|
let endpos = linemap[loc.line - 1] + loc.column - 1;
|
|
let semipos = fdata.lastIndexOf(';', endpos);
|
|
let bracepos = fdata.lastIndexOf('}', endpos);
|
|
let searchslice = fdata.slice(Math.max(semipos, bracepos) + 1, endpos);
|
|
|
|
let m = searchslice.match(/\/\*\*[\s\S]*?\*\//gm);
|
|
if (m === null)
|
|
return <></>;
|
|
|
|
let dc = m[m.length - 1].slice(3, -2);
|
|
dc = dc.replace(/^\s*(\*+[ \t]*)?/gm, "");
|
|
|
|
return <pre class="doccomment">{dc}</pre>;
|
|
}
|
|
|
|
function typeName(t)
|
|
{
|
|
if (t.name !== undefined)
|
|
return t.name;
|
|
|
|
if (t.isPointer)
|
|
return "%s%s*".format(t.isConst ? "const " : "", typeName(t.type));
|
|
|
|
if (t.isReference)
|
|
return "%s%s&".format(t.isConst ? "const " : "", typeName(t.type));
|
|
|
|
return t.toString();
|
|
}
|
|
|
|
function publicBaseList(t)
|
|
{
|
|
let l = <ul/>;
|
|
for each (let b in t.bases) {
|
|
if (b.access == 'public')
|
|
l.* += <li><a href={"/en/%s".format(b.type.name)}>{b.type.name}</a></li>;
|
|
}
|
|
|
|
if (l.*.length() == 0)
|
|
return <></>;
|
|
|
|
return <>
|
|
<h2>Base Classes</h2>
|
|
{l}
|
|
</>;
|
|
}
|
|
|
|
/**
|
|
* Get a source-link for a given location.
|
|
*/
|
|
function getLocLink(loc, text)
|
|
{
|
|
return <a class="loc"
|
|
href={"http://hg.mozilla.org/mozilla-central/file/%s/[LOC%s]#l%i".format(options.rev, loc.file, loc.line)}>{text}</a>;
|
|
}
|
|
|
|
function dumpType(t)
|
|
{
|
|
print("DUMP-TYPE(%s)".format(t.name));
|
|
|
|
let methodOverview = <tbody />;
|
|
let methodList = <div/>;
|
|
let memberList = <></>;
|
|
|
|
let shortNameMap = {};
|
|
|
|
let members = [m for (m in publicMembers(t))];
|
|
|
|
removeOverrides(members);
|
|
|
|
for each (let m in members) {
|
|
let qname = m.memberOf.name + '::';
|
|
|
|
// we don't inherit constructors from base classes
|
|
if (m.isConstructor && m.memberOf !== t)
|
|
continue;
|
|
|
|
if (m.name.indexOf(qname) != 0)
|
|
throw Error("Member name not qualified?");
|
|
|
|
let name = m.name.slice(qname.length);
|
|
|
|
if (name.indexOf('~') == 0)
|
|
continue;
|
|
|
|
if (m.isFunction) {
|
|
let innerList;
|
|
|
|
let shortName = getShortName(m);
|
|
if (m.isConstructor)
|
|
shortName = 'Constructors';
|
|
|
|
if (shortNameMap.hasOwnProperty(shortName)) {
|
|
innerList = shortNameMap[shortName];
|
|
}
|
|
else {
|
|
let overview =
|
|
<tr><td>
|
|
<a href={'#%s'.format(escape(shortName))}>{shortName}</a>
|
|
</td></tr>;
|
|
|
|
if (m.isConstructor)
|
|
methodOverview.insertChildAfter(null, overview);
|
|
else
|
|
methodOverview.appendChild(overview);
|
|
|
|
let shortMarkup =
|
|
<div>
|
|
<h3 id={shortName}>{shortName}</h3>
|
|
<dl/>
|
|
</div>;
|
|
|
|
|
|
if (m.isConstructor)
|
|
methodList.insertChildAfter(null, shortMarkup);
|
|
else
|
|
methodList.appendChild(shortMarkup);
|
|
|
|
innerList = shortMarkup.dl;
|
|
shortNameMap[shortName] = innerList;
|
|
}
|
|
|
|
let parameters = <ul/>;
|
|
for each (p in m.parameters) {
|
|
let name = p.name;
|
|
if (name == 'this')
|
|
continue;
|
|
|
|
if (/^D_\d+$/.test(name))
|
|
name = '<anonymous>';
|
|
|
|
parameters.* += <li>{typeName(p.type)} {name}</li>;
|
|
}
|
|
|
|
innerList.* +=
|
|
<>
|
|
<dt id={name} class="methodName">
|
|
<code>{typeName(m.type.type)} {name}</code> - {getLocLink(m.loc, "source")}
|
|
</dt>
|
|
<dd>
|
|
{getDocComment(m.loc)}
|
|
{parameters.*.length() > 0 ?
|
|
<>
|
|
<h4>Parameters</h4>
|
|
{parameters}
|
|
</> : <></>}
|
|
</dd>
|
|
</>;
|
|
}
|
|
else {
|
|
memberList += <li class="member">{name}</li>;
|
|
}
|
|
}
|
|
|
|
let r =
|
|
<body>
|
|
<p>{getLocLink(t.loc, "Class Declaration")}</p>
|
|
|
|
{getDocComment(t.loc)}
|
|
|
|
{dumpTypes.some(function(n) n == t.name) ?
|
|
<>
|
|
[MAP{t.name}-graph.map]
|
|
<img src={"/@api/deki/pages/=en%%252F%s/files/=%s-graph.png".format(t.name, t.name)} usemap="#classes" />
|
|
</> : <></>
|
|
}
|
|
|
|
{methodOverview.*.length() > 0 ?
|
|
<>
|
|
<h2>Method Overview</h2>
|
|
<table class="standard-table">{methodOverview}</table>
|
|
</> :
|
|
""
|
|
}
|
|
|
|
{publicBaseList(t)}
|
|
|
|
<h2>Data Members</h2>
|
|
|
|
{memberList.*.length() > 0 ?
|
|
memberList :
|
|
<p><em>No public members.</em></p>
|
|
}
|
|
|
|
<h2>Methods</h2>
|
|
|
|
{methodList.*.length() > 0 ?
|
|
methodList :
|
|
<p><em>No public methods.</em></p>
|
|
}
|
|
|
|
</body>;
|
|
|
|
write_file(t.name + ".html", r.toXMLString());
|
|
}
|
|
|
|
function graphType(t)
|
|
{
|
|
print("GRAPH-TYPE(%s)".format(t.name));
|
|
|
|
let contents = "digraph classes {\n"
|
|
+ " node [shape=rectangle fontsize=11]\n"
|
|
+ " %s;\n".format(t.name);
|
|
|
|
function graphClass(c)
|
|
{
|
|
contents += '%s [URL="http://developer.mozilla.org/en/%s"]\n'.format(c.name, c.name);
|
|
|
|
for each (let st in c.subtypes) {
|
|
contents += " %s -> %s;\n".format(c.name, st.name);
|
|
graphClass(st);
|
|
}
|
|
}
|
|
|
|
graphClass(t);
|
|
|
|
contents += "}\n";
|
|
|
|
write_file(t.name + "-graph.gv", contents);
|
|
}
|
|
|
|
function input_end()
|
|
{
|
|
for (let p in typelist)
|
|
dumpType(typelist[p]);
|
|
|
|
for (let n in interestingList)
|
|
graphType(interestingList[n]);
|
|
}
|