aboutsummaryrefslogtreecommitdiff
path: root/lib/mozilla-ast.js
diff options
context:
space:
mode:
authorIngvar Stepanyan <me@rreverser.com>2014-08-01 15:13:31 +0300
committerIngvar Stepanyan <me@rreverser.com>2014-08-01 23:42:34 +0300
commit0e3ff1f970b173dddc4f624a8935925ea4cfca3e (patch)
tree01615d98f3d25db5b77861ad20bdde042dfd6ad0 /lib/mozilla-ast.js
parent62bda71c853e844f0477873397ae7ae211bc7dc8 (diff)
downloadtracifyjs-0e3ff1f970b173dddc4f624a8935925ea4cfca3e.tar.gz
tracifyjs-0e3ff1f970b173dddc4f624a8935925ea4cfca3e.zip
Improved UglifyJS<->SpiderMonkey AST conversions.
* Added directives recognition in SM AST. * Moved semi-standard SM `Property` type to separate handler. * Added `const` recognition from SM AST. * Removed redundant `this`-as-identifier recognition. * Removed redundant rules for abstract SM types. * Described `CatchClause` using string syntax. * Added support for semi-standard `range` tuple as location source. * Added back-conversion support (to be improved).
Diffstat (limited to 'lib/mozilla-ast.js')
-rw-r--r--lib/mozilla-ast.js339
1 files changed, 274 insertions, 65 deletions
diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js
index bc24dfd6..2d35fb26 100644
--- a/lib/mozilla-ast.js
+++ b/lib/mozilla-ast.js
@@ -46,7 +46,22 @@
(function(){
var MOZ_TO_ME = {
- TryStatement : function(M) {
+ ExpressionStatement: function(M) {
+ var expr = M.expression;
+ if (expr.type === "Literal" && typeof expr.value === "string") {
+ return new AST_Directive({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ value: expr.value
+ });
+ }
+ return new AST_SimpleStatement({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ body: from_moz(expr)
+ });
+ },
+ TryStatement: function(M) {
return new AST_Try({
start : my_start_token(M),
end : my_end_token(M),
@@ -55,44 +70,40 @@
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
});
},
- CatchClause : function(M) {
- return new AST_Catch({
- start : my_start_token(M),
- end : my_end_token(M),
- argname : from_moz(M.param),
- body : from_moz(M.body).body
- });
+ Property: function(M) {
+ var key = M.key;
+ var name = key.type == "Identifier" ? key.name : key.value;
+ var args = {
+ start : my_start_token(key),
+ end : my_end_token(M.value),
+ key : name,
+ value : from_moz(M.value)
+ };
+ switch (M.kind) {
+ case "init":
+ return new AST_ObjectKeyVal(args);
+ case "set":
+ args.value.name = from_moz(key);
+ return new AST_ObjectSetter(args);
+ case "get":
+ args.value.name = from_moz(key);
+ return new AST_ObjectGetter(args);
+ }
},
- ObjectExpression : function(M) {
+ ObjectExpression: function(M) {
return new AST_Object({
start : my_start_token(M),
end : my_end_token(M),
properties : M.properties.map(function(prop){
- var key = prop.key;
- var name = key.type == "Identifier" ? key.name : key.value;
- var args = {
- start : my_start_token(key),
- end : my_end_token(prop.value),
- key : name,
- value : from_moz(prop.value)
- };
- switch (prop.kind) {
- case "init":
- return new AST_ObjectKeyVal(args);
- case "set":
- args.value.name = from_moz(key);
- return new AST_ObjectSetter(args);
- case "get":
- args.value.name = from_moz(key);
- return new AST_ObjectGetter(args);
- }
+ prop.type = "Property";
+ return from_moz(prop)
})
});
},
- SequenceExpression : function(M) {
+ SequenceExpression: function(M) {
return AST_Seq.from_array(M.expressions.map(from_moz));
},
- MemberExpression : function(M) {
+ MemberExpression: function(M) {
return new (M.computed ? AST_Sub : AST_Dot)({
start : my_start_token(M),
end : my_end_token(M),
@@ -100,7 +111,7 @@
expression : from_moz(M.object)
});
},
- SwitchCase : function(M) {
+ SwitchCase: function(M) {
return new (M.test ? AST_Case : AST_Default)({
start : my_start_token(M),
end : my_end_token(M),
@@ -108,7 +119,14 @@
body : M.consequent.map(from_moz)
});
},
- Literal : function(M) {
+ VariableDeclaration: function(M) {
+ return new (M.kind === "const" ? AST_Const : AST_Var)({
+ start : my_start_token(M),
+ end : my_end_token(M),
+ definitions : M.declarations.map(from_moz)
+ });
+ },
+ Literal: function(M) {
var val = M.value, args = {
start : my_start_token(M),
end : my_end_token(M)
@@ -132,8 +150,7 @@
UpdateExpression: From_Moz_Unary,
Identifier: function(M) {
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
- return new (M.name == "this" ? AST_This
- : p.type == "LabeledStatement" ? AST_Label
+ return new ( p.type == "LabeledStatement" ? AST_Label
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
@@ -147,6 +164,136 @@
}
};
+ AST_Directive.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "ExpressionStatement",
+ expression: set_moz_loc(this, {
+ type: "Literal",
+ value: this.value
+ })
+ });
+ };
+
+ AST_SimpleStatement.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "ExpressionStatement",
+ expression: to_moz(this.body)
+ });
+ };
+
+ AST_SwitchBranch.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "SwitchCase",
+ test: to_moz(this.expression),
+ consequent: this.body.map(to_moz)
+ });
+ };
+
+ AST_Try.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "TryStatement",
+ block: {
+ type: "BlockStatement",
+ body: this.body.map(to_moz)
+ },
+ handler: to_moz(this.bcatch),
+ finalizer: this.bfinally ? this.bfinally.body.map(to_moz) : null
+ });
+ };
+
+ AST_Definitions.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "VariableDeclaration",
+ kind: this instanceof AST_Const ? "const" : "var",
+ declarations: this.definitions.map(to_moz)
+ });
+ };
+
+ AST_Seq.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "SequenceExpression",
+ expressions: this.to_array().map(to_moz)
+ });
+ };
+
+ AST_PropAccess.prototype.to_mozilla_ast = function() {
+ var isComputed = this instanceof AST_Sub;
+ return set_moz_loc(this, {
+ type: "MemberExpression",
+ object: to_moz(this.expression),
+ computed: isComputed,
+ property: isComputed ? to_moz(this.property) : {type: "Identifier", name: this.property}
+ });
+ };
+
+ AST_Unary.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: this.operator == "++" || this.operator == "--" ? "UpdateExpression" : "UnaryExpression",
+ operator: this.operator,
+ prefix: this instanceof AST_UnaryPrefix,
+ argument: to_moz(this.argument)
+ });
+ };
+
+ AST_Object.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "ObjectExpression",
+ properties: this.properties.map(to_moz)
+ });
+ };
+
+ AST_ObjectProperty.prototype.to_mozilla_ast = function() {
+ var key;
+ if (is_identifier_string(this.key) && !RESERVED_WORDS(this.key)) {
+ key = {type: "Identifier", name: this.key};
+ } else {
+ key = {type: "Literal", value: this.key};
+ }
+ var kind;
+ if (this instanceof AST_ObjectKeyVal) {
+ kind = "init";
+ } else
+ if (this instanceof AST_ObjectGetter) {
+ kind = "get";
+ } else
+ if (this instanceof AST_ObjectSetter) {
+ kind = "set";
+ }
+ return set_moz_loc(this, {
+ type: "Property",
+ kind: kind,
+ key: key,
+ value: to_moz(this.value)
+ });
+ };
+
+ AST_Symbol.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "Identifier",
+ name: this.name
+ });
+ };
+
+ AST_Null.prototype.to_mozilla_ast =
+ AST_Boolean.prototype.to_mozilla_ast =
+ AST_Constant.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "Literal",
+ value: this.value
+ });
+ };
+
+ AST_Atom.prototype.to_mozilla_ast = function() {
+ return set_moz_loc(this, {
+ type: "Identifier",
+ name: String(this.value)
+ });
+ };
+
+ AST_Hole.prototype.to_mozilla_ast = function() {
+ return null;
+ };
+
function From_Moz_Unary(M) {
var prefix = "prefix" in M ? M.prefix
: M.type == "UnaryExpression" ? true : false;
@@ -158,14 +305,9 @@
});
};
- var ME_TO_MOZ = {};
-
- map("Node", AST_Node);
map("Program", AST_Toplevel, "body@body");
- map("Function", AST_Function, "id>name, params@argnames, body%body");
map("EmptyStatement", AST_EmptyStatement);
map("BlockStatement", AST_BlockStatement, "body@body");
- map("ExpressionStatement", AST_SimpleStatement, "expression>body");
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
map("BreakStatement", AST_Break, "label>label");
@@ -180,74 +322,137 @@
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
map("DebuggerStatement", AST_Debugger);
map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
- map("VariableDeclaration", AST_Var, "declarations@definitions");
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
+ map("CatchClause", AST_Catch, "param>argname, body%body");
map("ThisExpression", AST_This);
map("ArrayExpression", AST_Array, "elements@elements");
map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
- map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
+ map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
map("NewExpression", AST_New, "callee>expression, arguments@args");
map("CallExpression", AST_Call, "callee>expression, arguments@args");
+ AST_Accessor.prototype.to_mozilla_ast = AST_Function.prototype.to_mozilla_ast;
+
+ AST_Binary.prototype.to_mozilla_ast = function () {
+ return {
+ type: this.operator == "&&" || this.operator == "||" ? "LogicalExpression" : "BinaryExpression",
+ left: to_moz(this.left),
+ operator: this.operator,
+ right: to_moz(this.right)
+ };
+ };
+
/* -----[ tools ]----- */
function my_start_token(moznode) {
+ var loc = moznode.loc;
+ var range = moznode.range;
return new AST_Token({
- file : moznode.loc && moznode.loc.source,
- line : moznode.loc && moznode.loc.start.line,
- col : moznode.loc && moznode.loc.start.column,
- pos : moznode.start,
- endpos : moznode.start
+ file : loc && loc.source,
+ line : loc && loc.start.line,
+ col : loc && loc.start.column,
+ pos : range ? range[0] : moznode.start,
+ endpos : range ? range[0] : moznode.start
});
};
function my_end_token(moznode) {
+ var loc = moznode.loc;
+ var range = moznode.range;
return new AST_Token({
- file : moznode.loc && moznode.loc.source,
- line : moznode.loc && moznode.loc.end.line,
- col : moznode.loc && moznode.loc.end.column,
- pos : moznode.end,
- endpos : moznode.end
+ file : loc && loc.source,
+ line : loc && loc.end.line,
+ col : loc && loc.end.column,
+ pos : range ? range[1] : moznode.end,
+ endpos : range ? range[1] : moznode.end
});
};
+ function moz_sub_loc(token) {
+ return token.line ? {
+ line: token.line,
+ column: token.col
+ } : null;
+ };
+
+ function set_moz_loc(mynode, moznode) {
+ var start = mynode.start;
+ var end = mynode.end;
+ if (start.pos != null && end.pos != null) {
+ moznode.range = [start.pos, end.pos];
+ }
+ if (start.line) {
+ moznode.loc = {
+ start: moz_sub_loc(start),
+ end: moz_sub_loc(end)
+ };
+ if (start.file) {
+ moznode.loc.source = start.file;
+ }
+ }
+ return moznode;
+ };
+
function map(moztype, mytype, propmap) {
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
moz_to_me += "return new mytype({\n" +
"start: my_start_token(M),\n" +
"end: my_end_token(M)";
+ var me_to_moz = "function To_Moz_" + moztype + "(){\n";
+ me_to_moz += "return set_moz_loc(this, {\n" +
+ "type: moztype";
+
if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
if (!m) throw new Error("Can't understand property map: " + prop);
- var moz = "M." + m[1], how = m[2], my = m[3];
+ var moz = m[1], how = m[2], my = m[3];
moz_to_me += ",\n" + my + ": ";
- if (how == "@") {
- moz_to_me += moz + ".map(from_moz)";
- } else if (how == ">") {
- moz_to_me += "from_moz(" + moz + ")";
- } else if (how == "=") {
- moz_to_me += moz;
- } else if (how == "%") {
- moz_to_me += "from_moz(" + moz + ").body";
- } else throw new Error("Can't understand operator in propmap: " + prop);
+ me_to_moz += ",\n" + moz + ": ";
+ switch (how) {
+ case "@":
+ moz_to_me += "M." + moz + ".map(from_moz)";
+ me_to_moz += "this." + my + ".map(to_moz)";
+ break;
+ case ">":
+ moz_to_me += "from_moz(M." + moz + ")";
+ me_to_moz += "to_moz(this." + my + ")";
+ break;
+ case "=":
+ moz_to_me += "M." + moz;
+ me_to_moz += "this." + my;
+ break;
+ case "%":
+ moz_to_me += "from_moz(M." + moz + ").body";
+ me_to_moz += "{type: \"BlockStatement\", body: this." + my + ".map(to_moz)}";
+ break;
+ default:
+ throw new Error("Can't understand operator in propmap: " + prop);
+ }
});
- moz_to_me += "\n})}";
- // moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
- // console.log(moz_to_me);
+ moz_to_me += "\n})\n}";
+ me_to_moz += "\n})\n}";
+
+ moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
+ me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
+ //console.log(moz_to_me);
moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
mytype, my_start_token, my_end_token, from_moz
);
- return MOZ_TO_ME[moztype] = moz_to_me;
+ me_to_moz = new Function("moztype", "set_moz_loc", "to_moz", "return(" + me_to_moz + ")")(
+ moztype, set_moz_loc, to_moz
+ );
+ MOZ_TO_ME[moztype] = moz_to_me;
+ mytype.prototype.to_mozilla_ast = me_to_moz;
};
- var FROM_MOZ_STACK = null;
+ var FROM_MOZ_STACK = null, TO_MOZ_STACK = null;
function from_moz(node) {
FROM_MOZ_STACK.push(node);
@@ -264,4 +469,8 @@
return ast;
};
+ function to_moz(node) {
+ return node != null ? node.to_mozilla_ast() : null;
+ };
+
})();