[Pkg-mozext-commits] [adblock-plus] 71/464: Added a decompiler and a way to test it more thoroughly.
David Prévot
taffit at moszumanska.debian.org
Tue Jul 22 20:44:04 UTC 2014
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository adblock-plus.
commit cfc87334885db61ad67a81245f2e7bae1ad4ef5c
Author: Joshua Cranmer <Pidgeot18 at gmail.com>
Date: Wed Mar 24 14:41:09 2010 -0400
Added a decompiler and a way to test it more thoroughly.
---
.hgignore | 1 +
Makefile | 14 +-
configure | 15 +-
fake_js.sh | 3 +
jshydra_bridge.h | 1 +
jshydra_tokens.h | 16 +-
scripts/decompile.js | 446 +++++++++++++++++++++++++++++++++++++++++++++++++++
utils/astml.js | 251 ++++++++++++++++++++++-------
utils/dumpast.js | 2 +-
9 files changed, 677 insertions(+), 72 deletions(-)
diff --git a/.hgignore b/.hgignore
index 1749416..7b91165 100644
--- a/.hgignore
+++ b/.hgignore
@@ -4,3 +4,4 @@
^\.deps/
^mozilla/
+^jstest/
diff --git a/Makefile b/Makefile
index b8f336d..6561024 100644
--- a/Makefile
+++ b/Makefile
@@ -23,8 +23,10 @@ jshydra: jshydra.o jshydra_funcs.o jshydra_bridge.o
clean:
@rm -rf jshydra *.o .deps
+-include $(wildcard .deps/*.pp)
+
TESTS := $(notdir $(wildcard autotest/test_*.js))
-check: jshydra
+check:: jshydra
@cd autotest && for f in $(TESTS); do \
eval $$(cat $$f | sed -e '/^\/\/ [A-Za-z]*:/!q' -e 's+^// \([A-Za-z]*\): \(.*\)$$+export \1="\2"+'); \
echo -n "$$Name... "; \
@@ -41,3 +43,13 @@ check: jshydra
echo-variable-%:
@echo "$($*)"
+
+full-check:: jshydra
+ @cp -R $(MOZ_SRCDIR)/js/src/tests jstest
+ @echo "Decompiling JS ASTs.."
+ set -e; \
+ for f in $$(find jstest -name '*.js'); do \
+ echo $$f; \
+ ./jshydra scripts/decompile.js "$(MOZ_SRCDIR)/js/src/tests$${f#jstest}" >$$f; \
+ done
+ #python jstest/jstests.py --tinderbox fake_js.sh
diff --git a/configure b/configure
index b1da9d2..b7c19f8 100755
--- a/configure
+++ b/configure
@@ -6,11 +6,12 @@ REV=ea128fc02710
usage() {
echo "Usage: $0 ...
The following options control the setup for the SpiderMonkey build. Only set these if you wish to reuse source; otherwise, it will be downloaded for you:
---moz-src Location of the mozilla source directory
---moz-obj Location of the object directory for the above tree.
+--moz-src Location of the mozilla source directory
+--moz-obj Location of the object directory for the above tree.
Other arguments:
---moz-repo Repository to clone the source from"
+--moz-repo Repository to clone the source from
+--enable-parse-test Enables use of parse tests for jshydra"
}
VAL=
@@ -37,6 +38,9 @@ do
arg $1
repo="$VAL"
;;
+ --enable-parse-test)
+ parse="TRUE"
+ ;;
-h|--help)
usage
exit 1
@@ -81,6 +85,11 @@ else
NEEDSBUILD="true"
fi
+if [ ! -z $parse ]; then
+ cvs -d :pserver:anonymous at cvs-mirror.mozilla.org:/cvsroot co -d jstest \
+ mozilla/js/tests
+fi
+
AUTOCONF=$(which autoconf-2.13 autoconf2.13 autoconf213 2>/dev/null | grep -v '^no autoconf' | head -1)
if [ ! -e $objdir ]; then
# We need to make the objdir and configure
diff --git a/fake_js.sh b/fake_js.sh
new file mode 100755
index 0000000..9f7fb2c
--- /dev/null
+++ b/fake_js.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+/src/build/trunk/mail/mozilla/js/src/js "$@"
diff --git a/jshydra_bridge.h b/jshydra_bridge.h
index d2e23b2..0fd7229 100644
--- a/jshydra_bridge.h
+++ b/jshydra_bridge.h
@@ -28,6 +28,7 @@ jsuint jshydra_getArrayLength(JSContext *cx, JSObject *array);
jsval jshydra_getToplevelFunction(JSContext *cx, char const *name);
void jshydra_rootObject(JSContext *cx, JSObject *obj);
+JSObject *jshydra_getRegexPrototype(JSContext *cx);
JSObject *jshydra_getRegexPrototype(JSContext *cx);
#endif
diff --git a/jshydra_tokens.h b/jshydra_tokens.h
index f14237d..a50e35c 100644
--- a/jshydra_tokens.h
+++ b/jshydra_tokens.h
@@ -1,5 +1,5 @@
TOK(TOK_EOF, NULLARY)
-TOK(TOK_EOL, ERROR)
+TOK(TOK_EOL, ERROR) /* (not present) */
TOK(TOK_SEMI, UNARY)
TOK(TOK_COMMA, LIST)
TOK(TOK_ASSIGN, BINARY)
@@ -50,13 +50,13 @@ TOK(TOK_RETURN, UNARY)
TOK(TOK_NEW, LIST)
TOK(TOK_DELETE, UNARY)
TOK(TOK_DEFSHARP, UNARY)
-TOK(TOK_USESHARP, NULLARY) /*(use pn_num)*/
+TOK(TOK_USESHARP, NULLARY)
TOK(TOK_TRY, TERNARY)
TOK(TOK_CATCH, TERNARY)
-TOK(TOK_FINALLY, ERROR)
+TOK(TOK_FINALLY, ERROR) /* (not present) */
TOK(TOK_THROW, UNARY)
TOK(TOK_INSTANCEOF, BINARY)
-TOK(TOK_DEBUGGER, ERROR)
+TOK(TOK_DEBUGGER, NULLARY)
// We don't support E4X yet.
TOK(TOK_XMLSTAGO, ERROR)
TOK(TOK_XMLETAGO, ERROR)
@@ -65,7 +65,7 @@ TOK(TOK_XMLTAGC, ERROR)
TOK(TOK_XMLNAME, ERROR)
TOK(TOK_XMLATTR, ERROR)
TOK(TOK_XMLSPACE, ERROR)
-TOK(TOK_XMLTEXT, ERROR)
+TOK(TOK_XMLTEXT, NAME)
TOK(TOK_XMLCOMMENT, ERROR)
TOK(TOK_XMLCDATA, ERROR)
TOK(TOK_XMLPI, ERROR)
@@ -74,14 +74,14 @@ TOK(TOK_DBLCOLON, ERROR)
TOK(TOK_ANYNAME, ERROR)
TOK(TOK_DBLDOT, NAME)
TOK(TOK_FILTER, ERROR)
-TOK(TOK_XMLELEM, ERROR)
+TOK(TOK_XMLELEM, LIST)
TOK(TOK_XMLLIST, ERROR)
TOK(TOK_YIELD, UNARY)
TOK(TOK_ARRAYCOMP, LIST)
TOK(TOK_ARRAYPUSH, UNARY)
-TOK(TOK_LEXICALSCOPE, LEXICAL)
+TOK(TOK_LEXICALSCOPE, LEXICAL) // XXX: I don't think this is right?
TOK(TOK_LET, LIST)
-TOK(TOK_SEQ, ERROR)
+TOK(TOK_SEQ, ERROR) /* (not present) */
TOK(TOK_FORHEAD, TERNARY)
TOK(TOK_ARGSBODY, LIST)
TOK(TOK_UPVARS, NAMESET)
diff --git a/scripts/decompile.js b/scripts/decompile.js
new file mode 100644
index 0000000..ec2b4f8
--- /dev/null
+++ b/scripts/decompile.js
@@ -0,0 +1,446 @@
+// Decompile a JS file. This will be painful.
+include("../utils/dumpast.js");
+include("../utils/astml.js");
+
+let visitor = {
+ _visitArray: function (arr, pre, post, comma) {
+ if (pre === undefined) pre = '(';
+ if (post === undefined) post = ')';
+ if (comma === undefined) comma = ', ';
+ output(pre);
+ for each (let arg in arr) {
+ arg.visit(this);
+ output(comma);
+ }
+ if (arr.length > 0)
+ unwrite(comma.length);
+ output(post);
+ },
+ _visitMaybeBlock: function (body) {
+ if (body.type == "BlockStatement") {
+ output(" ");
+ body.visit(this);
+ } else {
+ flush().indent();
+ body.visit(this);
+ unindent();
+ }
+ },
+ _visitNeedBlock: function (stmt) {
+ if (stmt.type == "EmptyStatement")
+ output("{}").flush();
+ else
+ stmt.visit(this);
+ },
+ visitProgram: function (program) {},
+ visitFunctionDeclaration: function (func) {
+ output("function ");
+ if (func.name)
+ output(func.name);
+ this._visitArray(func.arguments, '(', ') ');
+ if (func.body.type == "EmptyStatement")
+ output("{ }");
+
+ func.body.visit(this);
+ return true;
+ },
+ visitParameter: function (p) {
+ output(p.name);
+ },
+ visitBlockStatement: function (stmt) {
+ output("{").flush().indent();
+ },
+ postvisitBlockStatement: function (stmt) {
+ output("}").unindent().flush();
+ },
+ visitVarStatement: function (stmt) {
+ output(stmt.vartype).output(" ");
+ this._visitArray(stmt.variables, '', '');
+ if (!this._noFlush)
+ output(';').flush();
+ this._noFlush = false;
+ return true;
+ },
+ visitVarDeclaration: function (decl) {
+ output(decl.name);
+ if ("initializer" in decl)
+ output(" = ");
+ },
+ visitLetStatement: function (stmt) {
+ output("let ");
+ this._visitArray(stmt.variables, '(', ')');
+ if (!this._noFlush)
+ output(';').flush();
+ this._noFlush = false;
+ return true;
+ },
+ visitExpressionStatement: function (stmt) {},
+ postvisitExpressionStatement: function (stmt) {
+ output(";").flush();
+ },
+ visitEmptyStatement: function (stmt) { output(";").flush(); },
+ visitIfStatement: function (stmt) {
+ output("if (");
+ stmt.cond.visit(this);
+ output(")");
+ this._visitMaybeBlock(stmt.body);
+ if (stmt.elsebody) {
+ output(" else");
+ this._visitMaybeBlock(stmt.elsebody);
+ }
+ return true;
+ },
+ visitDoWhileStatement: function (stmt) {
+ output("do");
+ this._visitMaybeBlock(stmt.body);
+ output("while (");
+ stmt.cond.visit(this);
+ output(");").flush();
+ return true;
+ },
+ visitWhileStatement: function (stmt) {
+ output("while (");
+ stmt.cond.visit(this);
+ output(")");
+ this._visitMaybeBlock(stmt.body);
+ return true;
+ },
+ visitForStatement: function (stmt) {
+ output("for (");
+ stmt.init.visit(this);
+ stmt.cond.visit(this);
+ if (stmt.cond.type != "EmptyStatement")
+ output("; ");
+ stmt.inc.visit(this);
+ output(")");
+ this._visitMaybeBlock(stmt.body);
+ return true;
+ },
+ visitForInStatement: function (stmt) {
+ output(stmt.itertype).output(" (");
+ this._noFlush = true;
+ stmt.itervar.visit(this);
+ output(" in ");
+ stmt.iterrange.visit(this);
+ output(")");
+ this._visitMaybeBlock(stmt.body);
+ return true;
+ },
+ visitContinueStatement: function (stmt) {
+ output("continue");
+ if ("label" in stmt)
+ output(" ").output(stmt.label);
+ output(";").flush();
+ },
+ visitBreakStatement: function (stmt) {
+ output("break");
+ if ("label" in stmt)
+ output(" ").output(stmt.label);
+ output(";").flush();
+ },
+ visitReturnStatement: function (stmt) { output("return "); },
+ postvisitReturnStatement: function (stmt) { output(";").flush(); },
+ visitWithStatement: function (stmt) {
+ output("with (");
+ stmt.variable.visit(this);
+ output(")");
+ this._visitMaybeBlock(stmt.body);
+ return true;
+ },
+ visitLabeledStatement: function (stmt) { output(stmt.label).output(": "); },
+ visitSwitchStatement: function (stmt) {
+ output("switch (");
+ stmt.expr.visit(this);
+ output(") {").flush().indent();
+ this._visitArray(stmt.cases, '', '', '');
+ output("}").unindent().flush();
+ return true;
+ },
+ visitSwitchCase: function (stmt) {
+ if ("expr" in stmt) {
+ output("case ");
+ stmt.expr.visit(this);
+ output(": ");
+ } else
+ output("default: ");
+ stmt.body.visit(this);
+ return true;
+ },
+ visitThrowStatement: function (stmt) { output("throw "); },
+ postvisitThrowStatement: function (stmt) { output(";").flush(); },
+ visitTryStatement: function (stmt) {
+ output("try ");
+ this._visitNeedBlock(stmt.body);
+ this._visitArray(stmt.catchers, '', '', '\n' + indentStr);
+ if (stmt.fin) {
+ output("finally ");
+ this._visitNeedBlock(stmt.fin);
+ }
+ return true;
+ },
+ visitCatchStatement: function (stmt) {
+ output("catch (");
+ stmt.variable.visit(this);
+ if ("cond" in stmt) {
+ output(" if ");
+ stmt.cond.visit(this);
+ }
+ output(")");
+ this._visitNeedBlock(stmt.body);
+ },
+ visitDebuggerStatement: function (stmt) { output("debugger;").flush(); },
+
+ visitThisExpression: function (expr) { output("this"); },
+ visitMemberExpression: function (expr) {
+ let needparen = expr.precedence + 1 < expr.container.precedence;
+ if (needparen)
+ output("(");
+ expr.container.visit(this);
+ if (needparen)
+ output(")");
+
+ if ("constmember" in expr)
+ output(".").output(expr.constmember);
+ else {
+ output("[");
+ expr.member.visit(this);
+ output("]");
+ }
+ return true;
+ },
+ visitNewExpression: function (expr) {
+ let needparen = expr.precedence < expr.constructor.precedence;
+ output("new ");
+ if (needparen)
+ output("(");
+ expr.constructor.visit(this);
+ if (needparen)
+ output(")");
+ this._visitArray(expr.arguments);
+ return true;
+ },
+ visitCallExpression: function (expr) {
+ let needparen = expr.precedence < expr.func.precedence;
+ if (needparen)
+ output("(");
+ expr.func.visit(this);
+ if (needparen)
+ output(")");
+ this._visitArray(expr.arguments);
+ return true;
+ },
+ visitLiteralExpression: function (expr) {
+ switch (expr.objtype) {
+ case "string":
+ output('"').output(sanitize(expr.value, '"')).output('"');
+ break;
+ case "number":
+ case "boolean":
+ case "regex":
+ output(expr.value.toString());
+ break;
+ case "null":
+ output("null");
+ break;
+ default:
+ throw "Unknown literal " + expr.objtype;
+ };
+ },
+ visitObjectLiteral: function (obj) {
+ output('{').flush().indent();
+ this._visitArray(obj.setters, '', '', ',\n' + indentStr);
+ flush().output('}').unindent();
+ return true;
+ },
+ visitPropertyLiteral: function (prop) {
+ if ("proptype" in prop) {
+ if (prop.value.type == "LiteralExpression") {
+ prop.property.visit(this);
+ output(" ").output(prop.proptype).output(": ");
+ prop.value.visit(this);
+ return true;
+ }
+ if (prop.proptype == "getter")
+ output("get ");
+ else if (prop.proptype == "setter")
+ output("set ");
+ else
+ throw "Unknown type: " + prop.proptype;
+ prop.property.visit(this);
+ if (prop.value.type != "FunctionDeclaration")
+ throw "Expection function, found: " + prop.value.type;
+ if (prop.value.name) {
+ output(" ").output(prop.value.name);
+ }
+ this._visitArray(prop.value.arguments, '(', ') ');
+ this._visitNeedBlock(prop.value.body);
+ return true;
+ }
+ prop.property.visit(this);
+ output(": ");
+ prop.value.visit(this);
+ return true;
+ },
+ visitArrayLiteral: function (arr) {
+ this._visitArray(arr.members, '[', ']', ', ');
+ return true;
+ },
+ visitArrayComprehensionExpression: function (arrcomp) {
+ output('[');
+ let enp = arrcomp.element.precedence > 16;
+ if (enp)
+ output("(");
+ arrcomp.element.visit(this);
+ if (enp)
+ output(")");
+ output(" ").output(arrcomp.itertype).output("(");
+ arrcomp.itervar.visit(this);
+ output(" in ");
+ arrcomp.iterrange.visit(this);
+ output(")");
+ if ("iterif" in arrcomp) {
+ output(" if (");
+ arrcomp.iterif.visit(this);
+ output(")");
+ }
+ output("]");
+ return true;
+ },
+ visitIdentifierExpression: function (expr) {
+ output(expr.name);
+ if ("initializer" in expr) {
+ output(" = ");
+ expr.initializer.visit(this);
+ return true;
+ }
+ },
+ visitPostfixExpression: function (expr) {},
+ postvisitPostfixExpression: function (expr) {
+ output(expr.operator);
+ },
+ visitUnaryExpression: function (expr) {
+ if (expr.operator != '()') {
+ output(expr.operator);
+ if (expr.operator.length > 1)
+ output(" ");
+ }
+ let np = expr.precedence < expr.operand.precedence;
+ if (expr.operator == '()' || np) {
+ output("(");
+ expr.operand.visit(this);
+ output(")");
+ return true;
+ }
+ },
+ visitBinaryExpression: function (expr) {
+ let lhp = expr.precedence < expr.lhs.precedence;
+ let rhp = expr.precedence < expr.rhs.precedence;
+ if (lhp)
+ output("(");
+ expr.lhs.visit(this);
+ if (lhp)
+ output(")");
+ output(" ").output(expr.operator).output(" ");
+ if (rhp)
+ output("(");
+ expr.rhs.visit(this);
+ if (rhp)
+ output(")");
+ return true;
+ },
+ visitConditionalExpression: function (expr) {
+ let lhp = expr.precedence < expr.cond.precedence;
+ let mhp = expr.precedence < expr.iftrue.precedence;
+ let rhp = expr.precedence < expr.iffalse.precedence;
+ if (lhp)
+ output("(");
+ expr.cond.visit(this);
+ if (lhp)
+ output(")");
+ output(" ? ");
+ if (mhp)
+ output("(");
+ expr.iftrue.visit(this);
+ if (mhp)
+ output(")");
+ output(" : ");
+ if (rhp)
+ output("(");
+ expr.iffalse.visit(this);
+ if (rhp)
+ output(")");
+ return true;
+ },
+ visitAssignmentExpression: function (expr) {
+ let lhp = expr.precedence < expr.lhs.precedence;
+ let rhp = expr.precedence < expr.rhs.precedence;
+ if (lhp)
+ output("(");
+ expr.lhs.visit(this);
+ if (lhp)
+ output(")");
+ output(" ").output(expr.operator).output("= ");
+ if (rhp)
+ output("(");
+ expr.rhs.visit(this);
+ if (rhp)
+ output(")");
+ return true;
+ },
+};
+
+/* Reminder */
+for (let f in structure) {
+ if (!("visit" + f in visitor))
+ throw "Please visit " + f;
+}
+
+function process_js(ast) {
+ if (!ast)
+ return;
+ ast = makeAST(ast);
+ walkAST(ast, visitor);
+}
+
+/* Output functions */
+let buffer = "", indentStr = "";
+function output(str) {
+ buffer += str;
+ return global;
+}
+function unwrite(numChars) {
+ buffer = buffer.substring(0, buffer.length - numChars);
+ return global;
+}
+function flush() {
+ _print(buffer);
+ buffer = indentStr;
+ return global;
+}
+function indent() {
+ indentStr += " ";
+ buffer = " " + buffer;
+ return global;
+}
+function unindent() {
+ indentStr = indentStr.substring(2);
+ buffer = buffer.substring(2);
+ return global;
+}
+
+function sanitize(str, q) {
+ function replace(x) {
+ if (x == q) return '\\' + q;
+ if (x == '\\') return '\\\\';
+ if (x == '\b') return '\\b';
+ if (x == '\f') return '\\f';
+ if (x == '\n') return '\\n';
+ if (x == '\r') return '\\r';
+ if (x == '\t') return '\\t';
+ if (x == '\v') return '\\v';
+ let val = x.charCodeAt(0)
+ if (x < ' ') return '\\x' + (val - val % 16) / 16 + (val % 16);
+ return x;
+ }
+ return [replace(x) for each (x in str)].join('');
+}
diff --git a/utils/astml.js b/utils/astml.js
index 5dee77b..3df14ad 100644
--- a/utils/astml.js
+++ b/utils/astml.js
@@ -31,21 +31,116 @@
+ lhs, rhs: left-hand, right-hand expressions for the operator
*/
include("../utils/dumpast.js");
-include("../utils/cleanast.js");
+
+function makeAST(pn) {
+ let ast = shellNode(pn, "Program");
+ ast.sourceElements = parseToAst(pn).statements;
+ return ast;
+}
+
+// Broken things:
+// * Generators (i for (i in foo), not array comp)
+// * let {a: x, b: y} = baz();
+// * let (x = 5, y = 12) {} (really!)
+// * function ( {a: 1, b: 2} )
+// * E4x
+
+let structure = {
+ // Program : SourceElement*
+ "Program": [ "sourceElements" ],
+ // SourceElement : FunctionDeclaration | Statement
+ // FunctionDeclaration : function [name] ( Parameter * ) { SourceElement * }
+ "FunctionDeclaration": [ "arguments", "body" ],
+ "Parameter": [ ], // name: Parameter name
+
+ // Statements
+ "BlockStatement": [ "statements" ],
+ "VarStatement": [ "variables" ],
+ "VarDeclaration": [ "initializer" ], // name: name of variable
+ "LetStatement": [ "variables", "body" ],
+ "EmptyStatement": [],
+ "ExpressionStatement": [ "expr" ],
+ "IfStatement": [ "cond", "body", "elsebody" ],
+ "DoWhileStatement": [ "cond", "body" ],
+ "WhileStatement": [ "cond", "body" ],
+ "ForStatement": [ "init", "cond", "inc", "body" ],
+ "ForInStatement": [ "itervar", "iterrange", "body" ], // itertype: for (each)
+ "ContinueStatement": [ ], // label: label to break to
+ "BreakStatement": [ ], // label: label to break to
+ "ReturnStatement": [ "expr" ],
+ "WithStatement": [ "variable", "body" ],
+ "LabeledStatement": [ "body" ], // label: label of statement
+ "SwitchStatement": [ "expr", "cases" ],
+ "SwitchCase": [ "expr", "body"], // default: no expr
+ "ThrowStatement": [ "expr" ],
+ "TryStatement": [ "body", "catchers", "fin" ],
+ "CatchStatement": [ "variable", "cond", "body" ],
+ "DebuggerStatement": [ ],
+
+ // Expressions (all have a precedence attribute, 0 (primary) -17)
+ "ThisExpression": [],
+ "LiteralExpression": [], // objtype: typeof literal, value: value
+ "ObjectLiteral": [ "setters" ],
+ "PropertyLiteral": [ "property", "value" ], // proptype: getter, setter, not
+ "ArrayLiteral": [ "members" ],
+ "ArrayComprehensionExpression": [ "element", "itervar", "iterrange",
+ "iterif" ], // itertype
+ "IdentifierExpression": [], // name: name of node
+ "MemberExpression": [ "container", "member"], //constmember if constant
+ "NewExpression": [ "constructor", "arguments" ],
+ "CallExpression": [ "func", "arguments" ],
+ "PostfixExpression": [ "operand" ], // operator
+ // XXX: jorendorff says yield is weird precedence
+ // For now, it's an unary with precedence = 16
+ "UnaryExpression": [ "operand" ], // operator
+ "BinaryExpression": [ "lhs", "rhs" ], // operator
+ "ConditionalExpression": [ "cond", "iftrue", "iffalse" ],
+ "AssignmentExpression": [ "lhs", "rhs" ], // operator
+};
+function walkAST(ast, visitor) {
+ function astVisitor(node) {
+ let info = structure[node.type];
+ if (!info)
+ throw "Need to define " + node.type;
+ let cback = "visit" + node.type;
+ let deep = false;
+ if (cback in visitor)
+ deep = visitor[cback](node);
+ if (!deep) {
+ for each (let part in info) {
+ let piece = node[part];
+ if (piece instanceof Array) {
+ [astVisitor(x) for each (x in piece)];
+ } else if (piece) {
+ astVisitor(piece);
+ }
+ }
+ }
+ cback = "post" + cback;
+ if (cback in visitor)
+ visitor[cback](node);
+ }
+ astVisitor(ast);
+}
function getLocation(pn) {
return pn.line + ":" + pn.column;
}
function shellNode(pn, type) {
- return {type: type, location: getLocation(pn)};
+ function visit(visitor) {
+ return walkAST(this, visitor);
+ }
+ return {type: type, location: getLocation(pn), visit: visit };
}
-function binaryNode(pn, operator) {
+function binaryNode(pn, operator, precedence) {
let ast = shellNode(pn, "BinaryExpression");
+ ast.precedence = precedence;
ast.operator = operator;
ast.lhs = parseToAst(pn.kids[0]);
ast.rhs = parseToAst(pn.kids[1]);
for (let i = 2; i < pn.kids.length; i++) {
let sup = shellNode(pn.kids[i], "BinaryExpression");
+ sup.precedence = precedence;
sup.operator = operator;
sup.lhs = ast;
sup.rhs = parseToAst(pn.kids[i]);
@@ -54,15 +149,6 @@ function binaryNode(pn, operator) {
return ast;
}
-function makeAST(pn) {
- let ast = {
- type: "Program",
- location: getLocation(pn),
- sourceElements: parseToAst(pn).statements
- };
- return ast;
-}
-
function parseToAst(pn) {
if (!pn)
return pn;
@@ -70,8 +156,8 @@ function parseToAst(pn) {
return global["convert" + decode_type(pn.type)](pn);
} catch (e if e instanceof TypeError) {
dump_ast(pn);
- //throw e;
- throw "Unexpected token " + decode_type(pn.type);
+ throw e;
+ //throw "Unexpected token " + decode_type(pn.type);
}
}
@@ -90,15 +176,22 @@ function convertTOK_SEMI(pn) {
}
function convertTOK_COMMA(pn) {
- return shellNode(pn, "EmptyExpression");
+ if (pn.kids.length == 0) {
+ let ast = shellNode(pn, "EmptyExpression");
+ ast.precedence = 17;
+ return ast;
+ } else {
+ return binaryNode(pn, ",", 17);
+ }
}
function convertTOK_ASSIGN(pn) {
let ast = shellNode(pn, "AssignmentExpression");
+ ast.precedence = 16;
ast.lhs = parseToAst(pn.kids[0]);
ast.rhs = parseToAst(pn.kids[1]);
switch (pn.op) {
- case JSOP_NOP: break;
+ case JSOP_NOP: ast.operator = ''; break;
case JSOP_BITOR: ast.operator = '|'; break;
case JSOP_BITXOR: ast.operator = '^'; break;
case JSOP_BITAND: ast.operator = '&'; break;
@@ -117,63 +210,75 @@ function convertTOK_ASSIGN(pn) {
function convertTOK_HOOK(pn) {
let ast = shellNode(pn, "ConditionalExpression");
- ast.condition = parseToAst(pn.kids[0]);
+ ast.precedence = 15;
+ ast.cond = parseToAst(pn.kids[0]);
ast.iftrue = parseToAst(pn.kids[1]);
ast.iffalse = parseToAst(pn.kids[2]);
return ast;
}
function convertTOK_COLON(pn) {
+ if (pn.kids.length == 1) {
+ let ast = shellNode(pn, "LabeledStatement");
+ ast.label = pn.atom;
+ ast.body = parseToAst(pn.kids[0]);
+ return ast;
+ }
let ast = shellNode(pn, "PropertyLiteral");
ast.property = parseToAst(pn.kids[0]);
ast.value = parseToAst(pn.kids[1]);
+ if (pn.op == JSOP_GETTER)
+ ast.proptype = "getter";
+ else if (pn.op == JSOP_SETTER)
+ ast.proptype = "setter";
return ast;
}
-function convertTOK_OR(pn) { return binaryNode(pn, "||"); }
-function convertTOK_AND(pn) { return binaryNode(pn, "&&"); }
-function convertTOK_BITOR(pn) { return binaryNode(pn, "|"); }
-function convertTOK_BITXOR(pn) { return binaryNode(pn, "^"); }
-function convertTOK_BITAND(pn) { return binaryNode(pn, "&"); }
+function convertTOK_OR(pn) { return binaryNode(pn, "||", 14); }
+function convertTOK_AND(pn) { return binaryNode(pn, "&&", 13); }
+function convertTOK_BITOR(pn) { return binaryNode(pn, "|", 12); }
+function convertTOK_BITXOR(pn) { return binaryNode(pn, "^", 11); }
+function convertTOK_BITAND(pn) { return binaryNode(pn, "&", 10); }
function convertTOK_EQOP(pn) {
switch (pn.op) {
- case JSOP_EQ: return binaryNode(pn, "==");
- case JSOP_NE: return binaryNode(pn, "!=");
- case JSOP_STRICTEQ: return binaryNode(pn, "===");
- case JSOP_STRICTNE: return binaryNode(pn, "!==");
+ case JSOP_EQ: return binaryNode(pn, "==", 9);
+ case JSOP_NE: return binaryNode(pn, "!=", 9);
+ case JSOP_STRICTEQ: return binaryNode(pn, "===", 9);
+ case JSOP_STRICTNE: return binaryNode(pn, "!==", 9);
}
throw "Unknown operator: " + decode_op(pn.op);
}
function convertTOK_RELOP(pn) {
switch (pn.op) {
- case JSOP_LT: return binaryNode(pn, "<");
- case JSOP_LE: return binaryNode(pn, "<=");
- case JSOP_GT: return binaryNode(pn, ">");
- case JSOP_GE: return binaryNode(pn, ">=");
+ case JSOP_LT: return binaryNode(pn, "<", 8);
+ case JSOP_LE: return binaryNode(pn, "<=", 8);
+ case JSOP_GT: return binaryNode(pn, ">", 8);
+ case JSOP_GE: return binaryNode(pn, ">=", 8);
}
throw "Unknown operator: " + decode_op(pn.op);
}
function convertTOK_SHOP(pn) {
switch (pn.op) {
- case JSOP_LSH: return binaryNode(pn, "<<");
- case JSOP_RSH: return binaryNode(pn, ">>");
- case JSOP_URSH: return binaryNode(pn, ">>>");
+ case JSOP_LSH: return binaryNode(pn, "<<", 7);
+ case JSOP_RSH: return binaryNode(pn, ">>", 7);
+ case JSOP_URSH: return binaryNode(pn, ">>>", 7);
}
throw "Unknown operator: " + decode_op(pn.op);
}
-function convertTOK_PLUS(pn) { return binaryNode(pn, "+"); }
-function convertTOK_MINUS(pn) { return binaryNode(pn, "-"); }
-function convertTOK_STAR(pn) { return binaryNode(pn, "*"); }
+function convertTOK_PLUS(pn) { return binaryNode(pn, "+", 6); }
+function convertTOK_MINUS(pn) { return binaryNode(pn, "-", 6); }
+function convertTOK_STAR(pn) { return binaryNode(pn, "*", 5); }
function convertTOK_DIVOP(pn) {
switch (pn.op) {
- case JSOP_MUL: return binaryNode(pn, "*");
- case JSOP_DIV: return binaryNode(pn, "/");
- case JSOP_MOD: return binaryNode(pn, "%");
+ case JSOP_MUL: return binaryNode(pn, "*", 5);
+ case JSOP_DIV: return binaryNode(pn, "/", 5);
+ case JSOP_MOD: return binaryNode(pn, "%", 5);
}
throw "Unknown operator: " + decode_op(pn.op);
}
function convertTOK_UNARYOP(pn) {
let ast = shellNode(pn, "UnaryExpression");
+ ast.precedence = 4;
ast.operand = parseToAst(pn.kids[0]);
switch (pn.op) {
case JSOP_NEG: ast.operator = "-"; break;
@@ -192,6 +297,7 @@ function convertTOK_INC(pn) { return convertPrePost(pn, '++'); }
function convertTOK_DEC(pn) { return convertPrePost(pn, '--'); }
function convertPrePost(pn, op) {
let ast = shellNode(pn, "UnaryExpression");
+ ast.precedence = 3;
ast.operator = op;
ast.operand = parseToAst(pn.kids[0]);
switch (pn.op) {
@@ -217,8 +323,9 @@ function convertPrePost(pn, op) {
function convertTOK_DOT(pn) {
let ast = shellNode(pn, "MemberExpression");
+ ast.precedence = 1;
ast.container = parseToAst(pn.kids[0]);
- ast.member = shellNode(pn, "Literal");
+ ast.member = shellNode(pn, "LiteralExpression");
ast.member.objtype = "string";
ast.member.value = pn.atom;
ast.constmember = pn.atom;
@@ -227,6 +334,7 @@ function convertTOK_DOT(pn) {
function convertTOK_LB(pn) {
let ast = shellNode(pn, "MemberExpression");
+ ast.precedence = 1;
ast.container = parseToAst(pn.kids[0]);
ast.member = parseToAst(pn.kids[1]);
return ast;
@@ -234,6 +342,7 @@ function convertTOK_LB(pn) {
function convertTOK_RB(pn) {
let ast = shellNode(pn, "ArrayLiteral");
+ ast.precedence = 0;
ast.members = [parseToAst(x) for each (x in pn.kids)];
return ast;
}
@@ -255,7 +364,13 @@ function convertTOK_RC(pn) {
}
function convertTOK_LP(pn) {
+ if (pn.op != JSOP_CALL) {
+ let ast = shellNode(pn, "LetStatement");
+ ast.variables = [parseToAst(x) for each (x in pn.kids)];
+ return ast;
+ }
let ast = shellNode(pn, "CallExpression");
+ ast.precedence = 2;
ast.func = parseToAst(pn.kids[0]);
ast.arguments = [];
for (let i = 1; i < pn.kids.length; i++)
@@ -265,13 +380,15 @@ function convertTOK_LP(pn) {
function convertTOK_RP(pn) {
let ast = shellNode(pn, "UnaryExpression");
- ast.expr = parseToAst(pn.kids[0]);
+ ast.precedence = 2;
+ ast.operand = parseToAst(pn.kids[0]);
ast.operator = "()";
return ast;
}
function convertTOK_NAME(pn) {
let ast = shellNode(pn, "IdentifierExpression");
+ ast.precedence = 0;
ast.name = pn.atom;
if (pn.kids.length > 0 && pn.kids[0]) {
ast.initializer = parseToAst(pn.kids[0]);
@@ -282,6 +399,7 @@ function convertTOK_NAME(pn) {
function convertTOK_NUMBER(pn) {
let ast = shellNode(pn, "LiteralExpression");
+ ast.precedence = 0;
ast.objtype = "number";
ast.value = pn.value;
return ast;
@@ -289,6 +407,7 @@ function convertTOK_NUMBER(pn) {
function convertTOK_STRING(pn) {
let ast = shellNode(pn, "LiteralExpression");
+ ast.precedence = 0;
ast.objtype = "string";
ast.value = pn.atom;
return ast;
@@ -296,6 +415,7 @@ function convertTOK_STRING(pn) {
function convertTOK_REGEXP(pn) {
let ast = shellNode(pn, "LiteralExpression");
+ ast.precedence = 0;
ast.objtype = "regex";
ast.value = pn.value;
return ast;
@@ -303,6 +423,7 @@ function convertTOK_REGEXP(pn) {
function convertTOK_PRIMARY(pn) {
let ast = shellNode(pn, "LiteralExpression");
+ ast.precedence = 0;
switch (pn.op) {
case JSOP_ZERO: ast.objtype = "number"; ast.value = 0; break;
case JSOP_ONE: ast.objtype = "number"; ast.value = 1; break;
@@ -319,6 +440,8 @@ function convertTOK_PRIMARY(pn) {
function convertTOK_FUNCTION(pn) {
let ast = shellNode(pn, "FunctionDeclaration");
+ // Precedence needs to be highest -> always wrapped
+ ast.precedence = 1.0 / 0.0;
ast.name = pn.name;
if (pn.kids[0].type == TOK_UPVARS)
pn = pn.kids[0];
@@ -353,19 +476,19 @@ function convertTOK_SWITCH(pn) {
if (rhs instanceof Array)
ast.cases = rhs;
else
- throw "What if this isn't an array?";
+ ast.cases = rhs.statements;
return ast;
}
function convertTOK_CASE(pn) {
let ast = shellNode(pn, "SwitchCase");
ast.expr = parseToAst(pn.kids[0]);
- ast.block = parseToAst(pn.kids[1]);
+ ast.body = parseToAst(pn.kids[1]);
return ast;
}
function convertTOK_DEFAULT(pn) {
let ast = shellNode(pn, "SwitchCase");
- ast.block = parseToAst(pn.kids[1]);
+ ast.body = parseToAst(pn.kids[1]);
return ast;
}
@@ -387,13 +510,13 @@ function convertTOK_FOR(pn) {
let expr = parseToAst(pn.kids[0]);
if (expr.type == "Forehead") {
ast.init = expr.init;
- ast.condition = expr.condition;
- ast.increment = expr.increment;
+ ast.cond = expr.condition;
+ ast.inc = expr.increment;
} else {
ast.type = "ForInStatement";
ast.itervar = expr.lhs;
ast.iterrange = expr.rhs;
- ast.itertype = (pn.iflags & 0x2 ? "foreach" : "for");
+ ast.itertype = (pn.iflags & 0x2 ? "for each" : "for");
}
ast.body = parseToAst(pn.kids[1]);
return ast;
@@ -412,14 +535,18 @@ function convertTOK_CONTINUE(pn) {
return ast;
}
-function convertTOK_IN(pn) { return binaryNode(pn, "in"); }
+function convertTOK_IN(pn) { return binaryNode(pn, "in", 8); }
function convertTOK_VAR(pn) {
let ast = shellNode(pn, "VarStatement");
if (pn.op == JSOP_DEFCONST)
- ast.constant = true;
+ ast.vartype = "const";
+ else
+ ast.vartype = "var";
ast.variables = [parseToAst(x) for each (x in pn.kids)];
for each (let x in ast.variables) {
+ if (x.type == "LetStatement")
+ return x;
x.type = "VarDeclaration";
}
return ast;
@@ -427,8 +554,8 @@ function convertTOK_VAR(pn) {
function convertTOK_WITH(pn) {
let ast = shellNode(pn, "WithStatement");
- ast.expr = parseToAst(pn.kids[0]);
- ast.block = parseToAst(pn.kids[1]);
+ ast.variable = parseToAst(pn.kids[0]);
+ ast.body = parseToAst(pn.kids[1]);
return ast;
}
@@ -440,15 +567,17 @@ function convertTOK_RETURN(pn) {
function convertTOK_NEW(pn) {
let ast = shellNode(pn, "NewExpression");
+ ast.precedence = 1;
ast.constructor = parseToAst(pn.kids[0]);
- ast.args = [];
+ ast.arguments = [];
for (let i = 1; i < pn.kids.length; i++)
- ast.args.push(parseToAst(pn.kids[i]));
+ ast.arguments.push(parseToAst(pn.kids[i]));
return ast;
}
function convertTOK_DELETE(pn) {
let ast = shellNode(pn, "UnaryExpression");
+ ast.precedence = 4;
ast.operator = "delete";
ast.operand = parseToAst(pn.kids[0]);
return ast;
@@ -482,8 +611,8 @@ function convertTOK_CATCH(pn) {
let ast = shellNode(pn, "CatchStatement");
ast.variable = parseToAst(pn.kids[0]);
if (pn.kids[1])
- ast.condition = parseToAst(pn.kids[1]);
- ast.block = parseToAst(pn.kids[2]);
+ ast.cond = parseToAst(pn.kids[1]);
+ ast.body = parseToAst(pn.kids[2]);
return ast;
}
@@ -493,19 +622,22 @@ function convertTOK_THROW(pn) {
return ast;
}
-function convertTOK_INSTANCEOF(pn) { return binaryNode(pn, "instanceof"); }
+function convertTOK_INSTANCEOF(pn) { return binaryNode(pn, "instanceof", 8); }
function convertTOK_DEBUGGER(pn) { return shellNode(pn, "DebuggerStatement"); }
// XML OPS
function convertTOK_YIELD(pn) {
- let ast = shellNode(pn, "YieldStatement");
- ast.expr = parseToAst(pn.kids[0]);
+ let ast = shellNode(pn, "UnaryExpression");
+ ast.operand = parseToAst(pn.kids[0]);
+ ast.precedence = 16;
+ ast.operator = "yield";
return ast;
}
function convertTOK_ARRAYCOMP(pn) {
let ast = parseToAst(pn.kids[0]);
+ ast.precedence = 0;
ast.type = "ArrayComprehensionExpression";
if ("expr" in ast.body)
ast.element = ast.body.expr;
@@ -529,7 +661,8 @@ function convertTOK_LEXICALSCOPE(pn) {
function convertTOK_LET(pn) {
let ast = convertTOK_VAR(pn);
- ast.type = "LetStatement";
+ if (ast.type == "VarStatement")
+ ast.vartype = "let";
return ast;
}
diff --git a/utils/dumpast.js b/utils/dumpast.js
index ad7cffc..f356345 100644
--- a/utils/dumpast.js
+++ b/utils/dumpast.js
@@ -60,7 +60,7 @@ function dump_trueast(ast, prefix) {
let str = prefix + "+ ";
_print(prefix + ast.type + " @ " + ast.location + ":");
for (let key in ast) {
- if (key == 'type' || key == 'location')
+ if (key == 'type' || key == 'location' || key == 'visit')
continue;
let val = ast[key];
if (val instanceof Array) {
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/adblock-plus.git
More information about the Pkg-mozext-commits
mailing list