xpcom/analysis/type-printer.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial