mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
529 lines
14 KiB
JavaScript
529 lines
14 KiB
JavaScript
/* vim: set sw=4 ts=4 et tw=78: */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Narcissus JavaScript engine.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Brendan Eich <brendan@mozilla.org>.
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Shu-Yu Guo <shu@rfrn.org>
|
|
* Bruno Jouhier
|
|
* Gregor Richards
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* Narcissus - JS implemented in JS.
|
|
*
|
|
* Decompiler and pretty-printer.
|
|
*/
|
|
|
|
Narcissus.decompiler = (function() {
|
|
|
|
const parser = Narcissus.parser;
|
|
const definitions = Narcissus.definitions;
|
|
const tokens = definitions.tokens;
|
|
|
|
// Set constants in the local scope.
|
|
eval(definitions.consts);
|
|
|
|
function indent(n, s) {
|
|
var ss = "", d = true;
|
|
|
|
for (var i = 0, j = s.length; i < j; i++) {
|
|
if (d)
|
|
for (var k = 0; k < n; k++)
|
|
ss += " ";
|
|
ss += s[i];
|
|
d = s[i] === '\n';
|
|
}
|
|
|
|
return ss;
|
|
}
|
|
|
|
function isBlock(n) {
|
|
return n && (n.type === BLOCK);
|
|
}
|
|
|
|
function isNonEmptyBlock(n) {
|
|
return isBlock(n) && n.children.length > 0;
|
|
}
|
|
|
|
function nodeStr(n) {
|
|
return '"' +
|
|
n.value.replace(/\\/g, "\\\\")
|
|
.replace(/"/g, "\\\"")
|
|
.replace(/\n/g, "\\n")
|
|
.replace(/\r/g, "\\r") +
|
|
'"';
|
|
}
|
|
|
|
function pp(n, d, inLetHead) {
|
|
var topScript = false;
|
|
|
|
if (!n)
|
|
return "";
|
|
if (!(n instanceof Object))
|
|
return n;
|
|
if (!d) {
|
|
topScript = true;
|
|
d = 1;
|
|
}
|
|
|
|
var p = "";
|
|
|
|
if (n.parenthesized)
|
|
p += "(";
|
|
|
|
switch (n.type) {
|
|
case FUNCTION:
|
|
case GETTER:
|
|
case SETTER:
|
|
if (n.type === FUNCTION)
|
|
p += "function";
|
|
else if (n.type === GETTER)
|
|
p += "get";
|
|
else
|
|
p += "set";
|
|
|
|
p += (n.name ? " " + n.name : "") + "(";
|
|
for (var i = 0, j = n.params.length; i < j; i++)
|
|
p += (i > 0 ? ", " : "") + pp(n.params[i], d);
|
|
p += ") " + pp(n.body, d);
|
|
break;
|
|
|
|
case SCRIPT:
|
|
case BLOCK:
|
|
var nc = n.children;
|
|
if (topScript) {
|
|
// No indentation.
|
|
for (var i = 0, j = nc.length; i < j; i++) {
|
|
if (i > 0)
|
|
p += "\n";
|
|
p += pp(nc[i], d);
|
|
var eoc = p[p.length - 1];
|
|
if (eoc != ";")
|
|
p += ";";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
p += "{";
|
|
if (n.id !== undefined)
|
|
p += " /* " + n.id + " */";
|
|
p += "\n";
|
|
for (var i = 0, j = nc.length; i < j; i++) {
|
|
if (i > 0)
|
|
p += "\n";
|
|
p += indent(4, pp(nc[i], d));
|
|
var eoc = p[p.length - 1];
|
|
if (eoc != ";")
|
|
p += ";";
|
|
}
|
|
p += "\n}";
|
|
break;
|
|
|
|
case LET_BLOCK:
|
|
p += "let (" + pp(n.variables, d, true) + ") ";
|
|
if (n.expression)
|
|
p += pp(n.expression, d);
|
|
else
|
|
p += pp(n.block, d);
|
|
break;
|
|
|
|
case IF:
|
|
p += "if (" + pp(n.condition, d) + ") ";
|
|
|
|
var tp = n.thenPart, ep = n.elsePart;
|
|
var b = isBlock(tp) || isBlock(ep);
|
|
if (!b)
|
|
p += "{\n";
|
|
p += (b ? pp(tp, d) : indent(4, pp(tp, d))) + "\n";
|
|
|
|
if (ep) {
|
|
if (!b)
|
|
p += "} else {\n";
|
|
else
|
|
p += " else ";
|
|
|
|
p += (b ? pp(ep, d) : indent(4, pp(ep, d))) + "\n";
|
|
}
|
|
if (!b)
|
|
p += "}";
|
|
break;
|
|
|
|
case SWITCH:
|
|
p += "switch (" + pp(n.discriminant, d) + ") {\n";
|
|
for (var i = 0, j = n.cases.length; i < j; i++) {
|
|
var ca = n.cases[i];
|
|
if (ca.type === CASE)
|
|
p += " case " + pp(ca.caseLabel, d) + ":\n";
|
|
else
|
|
p += " default:\n";
|
|
ps = pp(ca.statements, d);
|
|
p += ps.slice(2, ps.length - 2) + "\n";
|
|
}
|
|
p += "}";
|
|
break;
|
|
|
|
case FOR:
|
|
p += "for (" + pp(n.setup, d) + "; "
|
|
+ pp(n.condition, d) + "; "
|
|
+ pp(n.update, d) + ") ";
|
|
|
|
var pb = pp(n.body, d);
|
|
if (!isBlock(n.body))
|
|
p += "{\n" + indent(4, pb) + ";\n}";
|
|
else if (n.body)
|
|
p += pb;
|
|
break;
|
|
|
|
case WHILE:
|
|
p += "while (" + pp(n.condition, d) + ") ";
|
|
|
|
var pb = pp(n.body, d);
|
|
if (!isBlock(n.body))
|
|
p += "{\n" + indent(4, pb) + ";\n}";
|
|
else
|
|
p += pb;
|
|
break;
|
|
|
|
case FOR_IN:
|
|
var u = n.varDecl;
|
|
p += n.isEach ? "for each (" : "for (";
|
|
p += (u ? pp(u, d) : pp(n.iterator, d)) + " in " +
|
|
pp(n.object, d) + ") ";
|
|
|
|
var pb = pp(n.body, d);
|
|
if (!isBlock(n.body))
|
|
p += "{\n" + indent(4, pb) + ";\n}";
|
|
else if (n.body)
|
|
p += pb;
|
|
break;
|
|
|
|
case DO:
|
|
p += "do " + pp(n.body, d);
|
|
p += " while (" + pp(n.condition, d) + ");";
|
|
break;
|
|
|
|
case BREAK:
|
|
p += "break" + (n.label ? " " + n.label : "") + ";";
|
|
break;
|
|
|
|
case CONTINUE:
|
|
p += "continue" + (n.label ? " " + n.label : "") + ";";
|
|
break;
|
|
|
|
case TRY:
|
|
p += "try ";
|
|
p += pp(n.tryBlock, d);
|
|
for (var i = 0, j = n.catchClauses.length; i < j; i++) {
|
|
var t = n.catchClauses[i];
|
|
p += " catch (" + pp(t.varName, d) +
|
|
(t.guard ? " if " + pp(t.guard, d) : "") +
|
|
") ";
|
|
p += pp(t.block, d);
|
|
}
|
|
if (n.finallyBlock) {
|
|
p += " finally ";
|
|
p += pp(n.finallyBlock, d);
|
|
}
|
|
break;
|
|
|
|
case THROW:
|
|
p += "throw " + pp(n.exception, d);
|
|
break;
|
|
|
|
case RETURN:
|
|
p += "return";
|
|
if (n.value)
|
|
p += " " + pp(n.value, d);
|
|
break;
|
|
|
|
case YIELD:
|
|
p += "yield";
|
|
if (n.value.type)
|
|
p += " " + pp(n.value, d);
|
|
break;
|
|
|
|
case GENERATOR:
|
|
p += pp(n.expression, d) + " " + pp(n.tail, d);
|
|
break;
|
|
|
|
case WITH:
|
|
p += "with (" + pp(n.object, d) + ") ";
|
|
p += pp(n.body, d);
|
|
break;
|
|
|
|
case LET:
|
|
case VAR:
|
|
case CONST:
|
|
var nc = n.children;
|
|
if (!inLetHead) {
|
|
p += tokens[n.type] + " ";
|
|
}
|
|
for (var i = 0, j = nc.length; i < j; i++) {
|
|
if (i > 0)
|
|
p += ", ";
|
|
var u = nc[i];
|
|
p += pp(u.name, d);
|
|
if (u.initializer)
|
|
p += " = " + pp(u.initializer, d);
|
|
}
|
|
break;
|
|
|
|
case DEBUGGER:
|
|
p += "debugger NYI\n";
|
|
break;
|
|
|
|
case SEMICOLON:
|
|
if (n.expression) {
|
|
p += pp(n.expression, d) + ";";
|
|
}
|
|
break;
|
|
|
|
case LABEL:
|
|
p += n.label + ":\n" + pp(n.statement, d);
|
|
break;
|
|
|
|
case COMMA:
|
|
case LIST:
|
|
var nc = n.children;
|
|
for (var i = 0, j = nc.length; i < j; i++) {
|
|
if (i > 0)
|
|
p += ", ";
|
|
p += pp(nc[i], d);
|
|
}
|
|
break;
|
|
|
|
case ASSIGN:
|
|
var nc = n.children;
|
|
var t = n.assignOp;
|
|
p += pp(nc[0], d) + " " + (t ? tokens[t] : "") + "="
|
|
+ " " + pp(nc[1], d);
|
|
break;
|
|
|
|
case HOOK:
|
|
var nc = n.children;
|
|
p += "(" + pp(nc[0], d) + " ? "
|
|
+ pp(nc[1], d) + " : "
|
|
+ pp(nc[2], d);
|
|
p += ")";
|
|
break;
|
|
|
|
case OR:
|
|
case AND:
|
|
var nc = n.children;
|
|
p += "(" + pp(nc[0], d) + " " + tokens[n.type] + " "
|
|
+ pp(nc[1], d);
|
|
p += ")";
|
|
break;
|
|
|
|
case BITWISE_OR:
|
|
case BITWISE_XOR:
|
|
case BITWISE_AND:
|
|
case EQ:
|
|
case NE:
|
|
case STRICT_EQ:
|
|
case STRICT_NE:
|
|
case LT:
|
|
case LE:
|
|
case GE:
|
|
case GT:
|
|
case IN:
|
|
case INSTANCEOF:
|
|
case LSH:
|
|
case RSH:
|
|
case URSH:
|
|
case PLUS:
|
|
case MINUS:
|
|
case MUL:
|
|
case DIV:
|
|
case MOD:
|
|
var nc = n.children;
|
|
p += "(" + pp(nc[0], d) + " " + tokens[n.type] + " "
|
|
+ pp(nc[1], d) + ")";
|
|
break;
|
|
|
|
case DELETE:
|
|
case VOID:
|
|
case TYPEOF:
|
|
p += tokens[n.type] + " " + pp(n.children[0], d);
|
|
break;
|
|
|
|
case NOT:
|
|
case BITWISE_NOT:
|
|
p += tokens[n.type] + pp(n.children[0], d);
|
|
break;
|
|
|
|
case UNARY_PLUS:
|
|
p += "+" + pp(n.children[0], d);
|
|
break;
|
|
|
|
case UNARY_MINUS:
|
|
p += "-" + pp(n.children[0], d);
|
|
break;
|
|
|
|
case INCREMENT:
|
|
case DECREMENT:
|
|
if (n.postfix) {
|
|
p += pp(n.children[0], d) + tokens[n.type];
|
|
} else {
|
|
p += tokens[n.type] + pp(n.children[0], d);
|
|
}
|
|
break;
|
|
|
|
case DOT:
|
|
var nc = n.children;
|
|
p += pp(nc[0], d) + "." + pp(nc[1], d);
|
|
break;
|
|
|
|
case INDEX:
|
|
var nc = n.children;
|
|
p += pp(nc[0], d) + "[" + pp(nc[1], d) + "]";
|
|
break;
|
|
|
|
case CALL:
|
|
var nc = n.children;
|
|
p += pp(nc[0], d) + "(" + pp(nc[1], d) + ")";
|
|
break;
|
|
|
|
case NEW:
|
|
case NEW_WITH_ARGS:
|
|
var nc = n.children;
|
|
p += "new " + pp(nc[0], d);
|
|
if (nc[1])
|
|
p += "(" + pp(nc[1], d) + ")";
|
|
break;
|
|
|
|
case ARRAY_INIT:
|
|
p += "[";
|
|
var nc = n.children;
|
|
for (var i = 0, j = nc.length; i < j; i++) {
|
|
if(nc[i])
|
|
p += pp(nc[i], d);
|
|
p += ","
|
|
}
|
|
p += "]";
|
|
break;
|
|
|
|
case ARRAY_COMP:
|
|
p += "[" + pp (n.expression, d) + " ";
|
|
p += pp(n.tail, d);
|
|
p += "]";
|
|
break;
|
|
|
|
case COMP_TAIL:
|
|
var nc = n.children;
|
|
for (var i = 0, j = nc.length; i < j; i++) {
|
|
if (i > 0)
|
|
p += " ";
|
|
p += pp(nc[i], d);
|
|
}
|
|
if (n.guard)
|
|
p += " if (" + pp(n.guard, d) + ")";
|
|
break;
|
|
|
|
case OBJECT_INIT:
|
|
var nc = n.children;
|
|
if (nc[0] && nc[0].type === PROPERTY_INIT)
|
|
p += "{\n";
|
|
else
|
|
p += "{";
|
|
for (var i = 0, j = nc.length; i < j; i++) {
|
|
if (i > 0) {
|
|
p += ",\n";
|
|
}
|
|
|
|
var t = nc[i];
|
|
if (t.type === PROPERTY_INIT) {
|
|
var tc = t.children;
|
|
var l;
|
|
// see if the left needs to be a string
|
|
if (tc[0].value === "" || /[^A-Za-z0-9_$]/.test(tc[0].value)) {
|
|
l = nodeStr(tc[0]);
|
|
} else {
|
|
l = pp(tc[0], d);
|
|
}
|
|
p += indent(4, l) + ": " +
|
|
indent(4, pp(tc[1], d)).substring(4);
|
|
} else {
|
|
p += indent(4, pp(t, d));
|
|
}
|
|
}
|
|
p += "\n}";
|
|
break;
|
|
|
|
case NULL:
|
|
p += "null";
|
|
break;
|
|
|
|
case THIS:
|
|
p += "this";
|
|
break;
|
|
|
|
case TRUE:
|
|
p += "true";
|
|
break;
|
|
|
|
case FALSE:
|
|
p += "false";
|
|
break;
|
|
|
|
case IDENTIFIER:
|
|
case NUMBER:
|
|
case REGEXP:
|
|
p += n.value;
|
|
break;
|
|
|
|
case STRING:
|
|
p += nodeStr(n);
|
|
break;
|
|
|
|
case GROUP:
|
|
p += "(" + pp(n.children[0], d) + ")";
|
|
break;
|
|
|
|
default:
|
|
throw "PANIC: unknown operation " + tokens[n.type] + " " + n.toSource();
|
|
}
|
|
|
|
if (n.parenthesized)
|
|
p += ")";
|
|
|
|
return p;
|
|
}
|
|
|
|
return {
|
|
pp: pp
|
|
};
|
|
|
|
}());
|