[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