aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2021-04-26 04:23:52 +0800
committerGitHub <noreply@github.com>2021-04-26 04:23:52 +0800
commit324587f76980114e030c59165a60bd285ca05be0 (patch)
tree1f609c396c01110faeb115151e45784c146a90fc
parent80efaa2f331b2c3f3ec35218d1086830a39bec93 (diff)
downloadtracifyjs-324587f76980114e030c59165a60bd285ca05be0.tar.gz
tracifyjs-324587f76980114e030c59165a60bd285ca05be0.zip
upgrade AST<->ESTree translation (#4870)
fixes #968
-rwxr-xr-xbin/uglifyjs15
-rw-r--r--lib/compress.js4
-rw-r--r--lib/mozilla-ast.js900
-rw-r--r--lib/parse.js2
-rw-r--r--package.json2
-rw-r--r--test/mozilla-ast.js37
-rw-r--r--test/ufuzz/index.js1
-rw-r--r--tools/node.js7
8 files changed, 796 insertions, 172 deletions
diff --git a/bin/uglifyjs b/bin/uglifyjs
index 682c6a5a..325dacdd 100755
--- a/bin/uglifyjs
+++ b/bin/uglifyjs
@@ -235,7 +235,7 @@ if (options.mangle && options.mangle.properties) {
});
}
}
-if (output == "ast") options.output = {
+if (output == "ast" || output == "spidermonkey") options.output = {
ast: true,
code: false,
};
@@ -313,9 +313,11 @@ function run() {
if (options.parse.acorn) {
files = convert_ast(function(toplevel, name) {
return require("acorn").parse(files[name], {
+ ecmaVersion: "latest",
locations: true,
program: toplevel,
- sourceFile: name
+ sourceFile: name,
+ sourceType: "module",
});
});
} else if (options.parse.spidermonkey) {
@@ -409,14 +411,7 @@ function run() {
return value;
}, 2));
} else if (output == "spidermonkey") {
- print(JSON.stringify(UglifyJS.minify(result.code, {
- compress: false,
- mangle: false,
- output: {
- ast: true,
- code: false
- },
- }).ast.to_mozilla_ast(), null, 2));
+ print(JSON.stringify(result.ast.to_mozilla_ast(), null, 2));
} else if (output) {
fs.writeFileSync(output, result.code);
if (result.map) fs.writeFileSync(output + ".map", result.map);
diff --git a/lib/compress.js b/lib/compress.js
index 6c475629..28ac7f48 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -6562,6 +6562,10 @@ merge(Compressor.prototype, {
}
return insert_statements(body, node, in_list);
}
+ if (node instanceof AST_Import) {
+ if (node.properties && node.properties == 0) node.properties = null;
+ return node;
+ }
if (node instanceof AST_Sequence) {
if (node.expressions.length > 1) return;
return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js
index 3ceaf584..09c5b105 100644
--- a/lib/mozilla-ast.js
+++ b/lib/mozilla-ast.js
@@ -44,53 +44,161 @@
"use strict";
(function() {
- function normalize_directives(body) {
- var in_directive = true;
- for (var i = 0; i < body.length; i++) {
- if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) {
- body[i] = new AST_Directive({
- start: body[i].start,
- end: body[i].end,
- value: body[i].body.value
- });
- } else if (in_directive && !(body[i] instanceof AST_Statement && body[i].body instanceof AST_String)) {
- in_directive = false;
- }
- }
- return body;
- }
-
var MOZ_TO_ME = {
Program: function(M) {
return new AST_Toplevel({
start: my_start_token(M),
end: my_end_token(M),
- body: normalize_directives(M.body.map(from_moz))
+ body: normalize_directives(M.body.map(from_moz)),
});
},
+ ArrowFunctionExpression: function(M) {
+ var argnames = [], rest = null;
+ M.params.forEach(function(param) {
+ if (param.type == "RestElement") {
+ rest = from_moz(param.argument);
+ } else {
+ argnames.push(from_moz(param));
+ }
+ });
+ var fn = new (M.async ? AST_AsyncArrow : AST_Arrow)({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ argnames: argnames,
+ rest: rest,
+ });
+ var node = from_moz(M.body);
+ if (node instanceof AST_BlockStatement) {
+ fn.body = normalize_directives(node.body);
+ fn.value = null;
+ } else {
+ fn.body = [];
+ fn.value = node;
+ }
+ return fn;
+ },
FunctionDeclaration: function(M) {
- return new AST_Defun({
+ var ctor;
+ if (M.async) {
+ ctor = M.generator ? AST_AsyncGeneratorDefun : AST_AsyncDefun;
+ } else {
+ ctor = M.generator ? AST_GeneratorDefun : AST_Defun;
+ }
+ var argnames = [], rest = null;
+ M.params.forEach(function(param) {
+ if (param.type == "RestElement") {
+ rest = from_moz(param.argument);
+ } else {
+ argnames.push(from_moz(param));
+ }
+ });
+ return new ctor({
start: my_start_token(M),
end: my_end_token(M),
name: from_moz(M.id),
- argnames: M.params.map(from_moz),
- body: normalize_directives(from_moz(M.body).body)
+ argnames: argnames,
+ rest: rest,
+ body: normalize_directives(from_moz(M.body).body),
});
},
FunctionExpression: function(M) {
- return new AST_Function({
+ var ctor;
+ if (M.async) {
+ ctor = M.generator ? AST_AsyncGeneratorFunction : AST_AsyncFunction;
+ } else {
+ ctor = M.generator ? AST_GeneratorFunction : AST_Function;
+ }
+ var argnames = [], rest = null;
+ M.params.forEach(function(param) {
+ if (param.type == "RestElement") {
+ rest = from_moz(param.argument);
+ } else {
+ argnames.push(from_moz(param));
+ }
+ });
+ return new ctor({
start: my_start_token(M),
end: my_end_token(M),
name: from_moz(M.id),
- argnames: M.params.map(from_moz),
- body: normalize_directives(from_moz(M.body).body)
+ argnames: argnames,
+ rest: rest,
+ body: normalize_directives(from_moz(M.body).body),
});
},
- ExpressionStatement: function(M) {
- return new AST_SimpleStatement({
+ ClassDeclaration: function(M) {
+ return new AST_DefClass({
start: my_start_token(M),
end: my_end_token(M),
- body: from_moz(M.expression)
+ name: from_moz(M.id),
+ extends: from_moz(M.superClass),
+ properties: M.body.body.map(from_moz),
+ });
+ },
+ ClassExpression: function(M) {
+ return new AST_ClassExpression({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ name: from_moz(M.id),
+ extends: from_moz(M.superClass),
+ properties: M.body.body.map(from_moz),
+ });
+ },
+ MethodDefinition: function(M) {
+ var key = M.key, internal = false;
+ if (M.computed) {
+ key = from_moz(key);
+ } else if (key.type == "PrivateIdentifier") {
+ internal = true;
+ key = "#" + key.name;
+ } else {
+ key = read_name(key);
+ }
+ var ctor = AST_ClassMethod, value = from_moz(M.value);
+ switch (M.kind) {
+ case "get":
+ ctor = AST_ClassGetter;
+ value = new AST_Accessor(value);
+ break;
+ case "set":
+ ctor = AST_ClassSetter;
+ value = new AST_Accessor(value);
+ break;
+ }
+ return new ctor({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ key: key,
+ private: internal,
+ static: M.static,
+ value: value,
+ });
+ },
+ PropertyDefinition: function(M) {
+ var key = M.key, internal = false;
+ if (M.computed) {
+ key = from_moz(key);
+ } else if (key.type == "PrivateIdentifier") {
+ internal = true;
+ key = "#" + key.name;
+ } else {
+ key = read_name(key);
+ }
+ return new AST_ClassField({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ key: key,
+ private: internal,
+ static: M.static,
+ value: from_moz(M.value),
+ });
+ },
+ ForOfStatement: function(M) {
+ return new (M.await ? AST_ForAwaitOf : AST_ForOf)({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ init: from_moz(M.left),
+ object: from_moz(M.right),
+ body: from_moz(M.body),
});
},
TryStatement: function(M) {
@@ -103,54 +211,86 @@
end : my_end_token(M),
body : from_moz(M.block).body,
bcatch : from_moz(handlers[0]),
- bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
+ bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null,
});
},
Property: function(M) {
- var key = M.key;
+ var key = M.computed ? from_moz(M.key) : read_name(M.key);
var args = {
- start : my_start_token(key),
- end : my_end_token(M.value),
- key : "" + key[key.type == "Identifier" ? "name" : "value"],
- value : from_moz(M.value)
+ start: my_start_token(M),
+ end: my_end_token(M),
+ key: key,
+ value: from_moz(M[M.shorthand ? "key" : "value"]),
};
- if (M.kind == "init") return new AST_ObjectKeyVal(args);
+ if (M.kind == "init") return new (M.method ? AST_ObjectMethod : AST_ObjectKeyVal)(args);
args.value = new AST_Accessor(args.value);
if (M.kind == "get") return new AST_ObjectGetter(args);
if (M.kind == "set") return new AST_ObjectSetter(args);
},
ArrayExpression: function(M) {
return new AST_Array({
- start : my_start_token(M),
- end : my_end_token(M),
- elements : M.elements.map(function(elem) {
+ start: my_start_token(M),
+ end: my_end_token(M),
+ elements: M.elements.map(function(elem) {
return elem === null ? new AST_Hole() : from_moz(elem);
- })
+ }),
});
},
- ObjectExpression: function(M) {
- return new AST_Object({
- start : my_start_token(M),
- end : my_end_token(M),
- properties : M.properties.map(function(prop) {
- prop.type = "Property";
- return from_moz(prop)
- })
+ ArrayPattern: function(M) {
+ var elements = [], rest = null;
+ M.elements.forEach(function(el) {
+ if (el === null) {
+ elements.push(new AST_Hole());
+ } else if (el.type == "RestElement") {
+ rest = from_moz(el.argument);
+ } else {
+ elements.push(from_moz(el));
+ }
+ });
+ return new AST_DestructuredArray({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ elements: elements,
+ rest: rest,
});
},
- SequenceExpression: function(M) {
- return new AST_Sequence({
- start : my_start_token(M),
- end : my_end_token(M),
- expressions: M.expressions.map(from_moz)
+ ObjectPattern: function(M) {
+ var props = [], rest = null;
+ M.properties.forEach(function(prop) {
+ if (prop.type == "RestElement") {
+ rest = from_moz(prop.argument);
+ } else {
+ props.push(new AST_DestructuredKeyVal(from_moz(prop)));
+ }
+ });
+ return new AST_DestructuredObject({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ properties: props,
+ rest: rest,
});
},
MemberExpression: function(M) {
return new (M.computed ? AST_Sub : AST_Dot)({
- start : my_start_token(M),
- end : my_end_token(M),
- property : M.computed ? from_moz(M.property) : M.property.name,
- expression : from_moz(M.object)
+ start: my_start_token(M),
+ end: my_end_token(M),
+ expression: from_moz(M.object),
+ property: M.computed ? from_moz(M.property) : M.property.name,
+ });
+ },
+ MetaProperty: function(M) {
+ var expr = from_moz(M.meta);
+ var prop = read_name(M.property);
+ if (expr.name == "new" && prop == "target") return new AST_NewTarget({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ name: "new.target",
+ });
+ return new AST_Dot({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ expression: expr,
+ property: prop,
});
},
SwitchCase: function(M) {
@@ -158,21 +298,123 @@
start : my_start_token(M),
end : my_end_token(M),
expression : from_moz(M.test),
- body : M.consequent.map(from_moz)
+ body : M.consequent.map(from_moz),
+ });
+ },
+ ExportAllDeclaration: function(M) {
+ var alias = M.exported ? read_name(M.exported) : "*";
+ return new AST_ExportForeign({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ aliases: [ alias ],
+ keys: [ "*" ],
+ path: M.source.value,
+ });
+ },
+ ExportDefaultDeclaration: function(M) {
+ var decl = from_moz(M.declaration);
+ if (!decl.name) switch (decl.CTOR) {
+ case AST_AsyncDefun:
+ decl = new AST_AsyncFunction(decl);
+ break;
+ case AST_AsyncGeneratorDefun:
+ decl = new AST_AsyncGeneratorFunction(decl);
+ break;
+ case AST_DefClass:
+ decl = new AST_ClassExpression(decl);
+ break;
+ case AST_Defun:
+ decl = new AST_Function(decl);
+ break;
+ case AST_GeneratorDefun:
+ decl = new AST_GeneratorFunction(decl);
+ break;
+ }
+ return new AST_ExportDefault({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ body: decl,
+ });
+ },
+ ExportNamedDeclaration: function(M) {
+ if (M.declaration) return new AST_ExportDeclaration({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ body: from_moz(M.declaration),
+ });
+ if (M.source) {
+ var aliases = [], keys = [];
+ M.specifiers.forEach(function(prop) {
+ aliases.push(read_name(prop.exported));
+ keys.push(read_name(prop.local));
+ });
+ return new AST_ExportForeign({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ aliases: aliases,
+ keys: keys,
+ path: M.source.value,
+ });
+ }
+ return new AST_ExportReferences({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ properties: M.specifiers.map(function(prop) {
+ var sym = new AST_SymbolExport(from_moz(prop.local));
+ sym.alias = read_name(prop.exported);
+ return sym;
+ }),
+ });
+ },
+ ImportDeclaration: function(M) {
+ var all = null, def = null, props = null;
+ M.specifiers.forEach(function(prop) {
+ var sym = new AST_SymbolImport(from_moz(prop.local));
+ switch (prop.type) {
+ case "ImportDefaultSpecifier":
+ def = sym;
+ def.key = "";
+ break;
+ case "ImportNamespaceSpecifier":
+ all = sym;
+ all.key = "*";
+ break;
+ default:
+ sym.key = prop.imported.name || syn.name;
+ if (!props) props = [];
+ props.push(sym);
+ break;
+ }
+ });
+ return new AST_Import({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ all: all,
+ default: def,
+ properties: props,
+ path: M.source.value,
});
},
VariableDeclaration: function(M) {
- return new AST_Var({
- start : my_start_token(M),
- end : my_end_token(M),
- definitions : M.declarations.map(from_moz)
+ return new ({
+ const: AST_Const,
+ let: AST_Let,
+ }[M.kind] || 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)
+ var args = {
+ start: my_start_token(M),
+ end: my_end_token(M),
};
+ if (M.bigint) {
+ args.value = M.bigint.toLowerCase() + "n";
+ return new AST_BigInt(args);
+ }
+ var val = M.value;
if (val === null) return new AST_Null(args);
var rx = M.regex;
if (rx && rx.pattern) {
@@ -196,25 +438,84 @@
return new (val ? AST_True : AST_False)(args);
}
},
+ TemplateLiteral: function(M) {
+ return new AST_Template({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ expressions: M.expressions.map(from_moz),
+ strings: M.quasis.map(function(el) {
+ return el.value.raw;
+ }),
+ });
+ },
+ TaggedTemplateExpression: function(M) {
+ var tmpl = from_moz(M.quasi);
+ tmpl.start = my_start_token(M);
+ tmpl.end = my_end_token(M);
+ tmpl.tag = from_moz(M.tag);
+ return tmpl;
+ },
Identifier: function(M) {
- var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
- return new ( p.type == "LabeledStatement" ? AST_Label
- : p.type == "VariableDeclarator" && p.id === M ? AST_SymbolVar
- : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
- : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
- : p.type == "CatchClause" ? AST_SymbolCatch
- : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef
- : AST_SymbolRef)({
- start : my_start_token(M),
- end : my_end_token(M),
- name : M.name
- });
+ var p, level = FROM_MOZ_STACK.length - 1;
+ do {
+ p = FROM_MOZ_STACK[--level];
+ } while (p.type == "ArrayPattern"
+ || p.type == "AssignmentPattern" && p.left === FROM_MOZ_STACK[level + 1]
+ || p.type == "ObjectPattern"
+ || p.type == "Property" && p[p.shorthand ? "key" : "value"] === FROM_MOZ_STACK[level + 1]
+ || p.type == "VariableDeclarator" && p.id === FROM_MOZ_STACK[level + 1]);
+ var ctor = AST_SymbolRef;
+ switch (p.type) {
+ case "ArrowFunctionExpression":
+ if (p.body !== FROM_MOZ_STACK[level + 1]) ctor = AST_SymbolFunarg;
+ break;
+ case "BreakStatement":
+ case "ContinueStatement":
+ ctor = AST_LabelRef;
+ break;
+ case "CatchClause":
+ ctor = AST_SymbolCatch;
+ break;
+ case "ClassDeclaration":
+ if (p.id === FROM_MOZ_STACK[level + 1]) ctor = AST_SymbolDefClass;
+ break;
+ case "ClassExpression":
+ if (p.id === FROM_MOZ_STACK[level + 1]) ctor = AST_SymbolClass;
+ break;
+ case "FunctionDeclaration":
+ ctor = p.id === FROM_MOZ_STACK[level + 1] ? AST_SymbolDefun : AST_SymbolFunarg;
+ break;
+ case "FunctionExpression":
+ ctor = p.id === FROM_MOZ_STACK[level + 1] ? AST_SymbolLambda : AST_SymbolFunarg;
+ break;
+ case "LabeledStatement":
+ ctor = AST_Label;
+ break;
+ case "VariableDeclaration":
+ ctor = {
+ const: AST_SymbolConst,
+ let: AST_SymbolLet,
+ }[p.kind] || AST_SymbolVar;
+ break;
+ }
+ return new ctor({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ name: M.name,
+ });
+ },
+ Super: function(M) {
+ return new AST_Super({
+ start: my_start_token(M),
+ end: my_end_token(M),
+ name: "super",
+ });
},
ThisExpression: function(M) {
return new AST_This({
- start : my_start_token(M),
- end : my_end_token(M),
- name : "this",
+ start: my_start_token(M),
+ end: my_end_token(M),
+ name: "this",
});
},
};
@@ -232,6 +533,7 @@
};
map("EmptyStatement", AST_EmptyStatement);
+ map("ExpressionStatement", AST_SimpleStatement, "expression>body");
map("BlockStatement", AST_BlockStatement, "body@body");
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
@@ -252,46 +554,145 @@
map("BinaryExpression", AST_Binary, "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("AssignmentPattern", AST_DefaultValue, "left>name, right>value");
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");
+ map("SequenceExpression", AST_Sequence, "expressions@expressions");
+ map("SpreadElement", AST_Spread, "argument>expression");
+ map("ObjectExpression", AST_Object, "properties@properties");
+ map("AwaitExpression", AST_Await, "argument>expression");
+ map("YieldExpression", AST_Yield, "argument>expression, delegate=nested");
def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
return to_moz_scope("Program", M);
});
- def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
+ def_to_moz(AST_LambdaDefinition, function To_Moz_FunctionDeclaration(M) {
+ var params = M.argnames.map(to_moz);
+ if (M.rest) params.push({
+ type: "RestElement",
+ argument: to_moz(M.rest),
+ });
return {
type: "FunctionDeclaration",
id: to_moz(M.name),
- params: M.argnames.map(to_moz),
- body: to_moz_scope("BlockStatement", M)
- }
+ async: is_async(M),
+ generator: is_generator(M),
+ params: params,
+ body: to_moz_scope("BlockStatement", M),
+ };
});
- def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) {
+ def_to_moz(AST_Lambda, function To_Moz_FunctionExpression(M) {
+ var params = M.argnames.map(to_moz);
+ if (M.rest) params.push({
+ type: "RestElement",
+ argument: to_moz(M.rest),
+ });
+ if (is_arrow(M)) return {
+ type: "ArrowFunctionExpression",
+ async: is_async(M),
+ params: params,
+ body: M.value ? to_moz(M.value) : to_moz_scope("BlockStatement", M),
+ };
return {
type: "FunctionExpression",
id: to_moz(M.name),
- params: M.argnames.map(to_moz),
- body: to_moz_scope("BlockStatement", M)
- }
+ async: is_async(M),
+ generator: is_generator(M),
+ params: params,
+ body: to_moz_scope("BlockStatement", M),
+ };
});
- def_to_moz(AST_Directive, function To_Moz_Directive(M) {
+ def_to_moz(AST_DefClass, function To_Moz_ClassDeclaration(M) {
return {
- type: "ExpressionStatement",
- expression: {
+ type: "ClassDeclaration",
+ id: to_moz(M.name),
+ superClass: to_moz(M.extends),
+ body: {
+ type: "ClassBody",
+ body: M.properties.map(to_moz),
+ },
+ };
+ });
+
+ def_to_moz(AST_ClassExpression, function To_Moz_ClassExpression(M) {
+ return {
+ type: "ClassExpression",
+ id: to_moz(M.name),
+ superClass: to_moz(M.extends),
+ body: {
+ type: "ClassBody",
+ body: M.properties.map(to_moz),
+ },
+ };
+ });
+
+ function To_Moz_MethodDefinition(kind) {
+ return function(M) {
+ var computed = M.key instanceof AST_Node;
+ var key = computed ? to_moz(M.key) : M.private ? {
+ type: "PrivateIdentifier",
+ name: M.key.slice(1),
+ } : {
type: "Literal",
- value: M.value
- }
+ value: M.key,
+ };
+ return {
+ type: "MethodDefinition",
+ kind: kind,
+ computed: computed,
+ key: key,
+ static: M.static,
+ value: to_moz(M.value),
+ };
+ };
+ }
+ def_to_moz(AST_ClassGetter, To_Moz_MethodDefinition("get"));
+ def_to_moz(AST_ClassSetter, To_Moz_MethodDefinition("set"));
+ def_to_moz(AST_ClassMethod, To_Moz_MethodDefinition("method"));
+
+ def_to_moz(AST_ClassField, function To_Moz_PropertyDefinition(M) {
+ var computed = M.key instanceof AST_Node;
+ var key = computed ? to_moz(M.key) : M.private ? {
+ type: "PrivateIdentifier",
+ name: M.key.slice(1),
+ } : {
+ type: "Literal",
+ value: M.key,
+ };
+ return {
+ type: "PropertyDefinition",
+ computed: computed,
+ key: key,
+ static: M.static,
+ value: to_moz(M.value),
};
});
- def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) {
+ function To_Moz_ForOfStatement(is_await) {
+ return function(M) {
+ return {
+ type: "ForOfStatement",
+ await: is_await,
+ left: to_moz(M.init),
+ right: to_moz(M.object),
+ body: to_moz(M.body),
+ };
+ };
+ }
+ def_to_moz(AST_ForAwaitOf, To_Moz_ForOfStatement(true));
+ def_to_moz(AST_ForOf, To_Moz_ForOfStatement(false));
+
+ def_to_moz(AST_Directive, function To_Moz_Directive(M) {
return {
type: "ExpressionStatement",
- expression: to_moz(M.body)
+ expression: set_moz_loc(M, {
+ type: "Literal",
+ value: M.value,
+ }),
};
});
@@ -299,7 +700,7 @@
return {
type: "SwitchCase",
test: to_moz(M.expression),
- consequent: M.body.map(to_moz)
+ consequent: M.body.map(to_moz),
};
});
@@ -309,7 +710,7 @@
block: to_moz_block(M),
handler: to_moz(M.bcatch),
guardedHandlers: [],
- finalizer: to_moz(M.bfinally)
+ finalizer: to_moz(M.bfinally),
};
});
@@ -318,32 +719,132 @@
type: "CatchClause",
param: to_moz(M.argname),
guard: null,
- body: to_moz_block(M)
+ body: to_moz_block(M),
};
});
- def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
+ def_to_moz(AST_ExportDeclaration, function To_Moz_ExportNamedDeclaration_declaration(M) {
return {
- type: "VariableDeclaration",
- kind: "var",
- declarations: M.definitions.map(to_moz)
+ type: "ExportNamedDeclaration",
+ declaration: to_moz(M.body),
};
});
- def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) {
+ def_to_moz(AST_ExportDefault, function To_Moz_ExportDefaultDeclaration(M) {
+ var decl = to_moz(M.body);
+ switch (decl.type) {
+ case "ClassExpression":
+ decl.type = "ClassDeclaration";
+ break;
+ case "FunctionExpression":
+ decl.type = "FunctionDeclaration";
+ break;
+ }
return {
- type: "SequenceExpression",
- expressions: M.expressions.map(to_moz)
+ type: "ExportDefaultDeclaration",
+ declaration: decl,
+ };
+ });
+
+ def_to_moz(AST_ExportForeign, function To_Moz_ExportAllDeclaration_ExportNamedDeclaration(M) {
+ if (M.keys[0] == "*") return {
+ type: "ExportAllDeclaration",
+ exported: M.aliases[0] == "*" ? null : {
+ type: "Identifier",
+ name: M.aliases[0],
+ },
+ source: {
+ type: "Literal",
+ value: M.path,
+ },
+ };
+ var specifiers = [];
+ for (var i = 0; i < M.aliases.length; i++) {
+ specifiers.push({
+ type: "ExportSpecifier",
+ exported: {
+ type: "Identifier",
+ name: M.aliases[i],
+ },
+ local: {
+ type: "Identifier",
+ name: M.keys[i],
+ },
+ });
+ }
+ return {
+ type: "ExportNamedDeclaration",
+ specifiers: specifiers,
+ source: {
+ type: "Literal",
+ value: M.path,
+ },
+ };
+ });
+
+ def_to_moz(AST_ExportReferences, function To_Moz_ExportNamedDeclaration_specifiers(M) {
+ return {
+ type: "ExportNamedDeclaration",
+ specifiers: M.properties.map(function(prop) {
+ return {
+ type: "ExportSpecifier",
+ local: to_moz(prop),
+ exported: {
+ type: "Identifier",
+ name: prop.alias,
+ },
+ };
+ }),
+ };
+ });
+
+ def_to_moz(AST_Import, function To_Moz_ImportDeclaration(M) {
+ var specifiers = M.properties ? M.properties.map(function(prop) {
+ return {
+ type: "ImportSpecifier",
+ local: to_moz(prop),
+ imported: {
+ type: "Identifier",
+ name: prop.key,
+ },
+ };
+ }) : [];
+ if (M.all) specifiers.unshift({
+ type: "ImportNamespaceSpecifier",
+ local: to_moz(M.all),
+ });
+ if (M.default) specifiers.unshift({
+ type: "ImportDefaultSpecifier",
+ local: to_moz(M.default),
+ });
+ return {
+ type: "ImportDeclaration",
+ specifiers: specifiers,
+ source: {
+ type: "Literal",
+ value: M.path,
+ },
+ };
+ });
+
+ def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
+ return {
+ type: "VariableDeclaration",
+ kind: M.TYPE.toLowerCase(),
+ declarations: M.definitions.map(to_moz),
};
});
def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) {
- var isComputed = M instanceof AST_Sub;
+ var computed = M instanceof AST_Sub;
return {
type: "MemberExpression",
object: to_moz(M.expression),
- computed: isComputed,
- property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
+ computed: computed,
+ property: computed ? to_moz(M.property) : {
+ type: "Identifier",
+ name: M.property,
+ },
};
});
@@ -368,37 +869,70 @@
def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) {
return {
type: "ArrayExpression",
- elements: M.elements.map(to_moz)
+ elements: M.elements.map(to_moz),
};
});
- def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
+ def_to_moz(AST_DestructuredArray, function To_Moz_ArrayPattern(M) {
+ var elements = M.elements.map(to_moz);
+ if (M.rest) elements.push({
+ type: "RestElement",
+ argument: to_moz(M.rest),
+ });
return {
- type: "ObjectExpression",
- properties: M.properties.map(to_moz)
+ type: "ArrayPattern",
+ elements: elements,
+ };
+ });
+
+ def_to_moz(AST_DestructuredKeyVal, function To_Moz_Property(M) {
+ var computed = M.key instanceof AST_Node;
+ var key = computed ? to_moz(M.key) : {
+ type: "Literal",
+ value: M.key,
+ };
+ return {
+ type: "Property",
+ kind: "init",
+ computed: computed,
+ key: key,
+ value: to_moz(M.value),
+ };
+ });
+
+ def_to_moz(AST_DestructuredObject, function To_Moz_ObjectPattern(M) {
+ var props = M.properties.map(to_moz);
+ if (M.rest) props.push({
+ type: "RestElement",
+ argument: to_moz(M.rest),
+ });
+ return {
+ type: "ObjectPattern",
+ properties: props,
};
});
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
- var key = {
+ var computed = M.key instanceof AST_Node;
+ var key = computed ? to_moz(M.key) : {
type: "Literal",
- value: M.key
+ value: M.key,
};
var kind;
if (M instanceof AST_ObjectKeyVal) {
kind = "init";
- } else
- if (M instanceof AST_ObjectGetter) {
+ } else if (M instanceof AST_ObjectGetter) {
kind = "get";
- } else
- if (M instanceof AST_ObjectSetter) {
+ } else if (M instanceof AST_ObjectSetter) {
kind = "set";
}
return {
type: "Property",
kind: kind,
+ computed: computed,
+ method: M instanceof AST_ObjectMethod,
key: key,
- value: to_moz(M.value)
+ value: to_moz(M.value),
};
});
@@ -406,14 +940,32 @@
var def = M.definition();
return {
type: "Identifier",
- name: def && def.mangled_name || M.name
+ name: def && def.mangled_name || M.name,
};
});
+ def_to_moz(AST_Super, function To_Moz_Super() {
+ return { type: "Super" };
+ });
+
def_to_moz(AST_This, function To_Moz_ThisExpression() {
return { type: "ThisExpression" };
});
+ def_to_moz(AST_NewTarget, function To_Moz_MetaProperty() {
+ return {
+ type: "MetaProperty",
+ meta: {
+ type: "Identifier",
+ name: "new",
+ },
+ property: {
+ type: "Identifier",
+ name: "target",
+ },
+ };
+ });
+
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var flags = M.value.toString().match(/[gimuy]*$/)[0];
var value = "/" + M.value.raw_source + "/" + flags;
@@ -428,9 +980,18 @@
};
});
- def_to_moz(AST_Constant, function To_Moz_Literal(M) {
+ def_to_moz(AST_BigInt, function To_Moz_BigInt(M) {
var value = M.value;
- if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
+ return {
+ type: "Literal",
+ bigint: value.slice(0, -1),
+ raw: value,
+ };
+ });
+
+ function To_Moz_Literal(M) {
+ var value = M.value;
+ if (typeof value === "number" && (value < 0 || (value === 0 && 1 / value < 0))) {
return {
type: "UnaryExpression",
operator: "-",
@@ -438,33 +999,68 @@
argument: {
type: "Literal",
value: -value,
- raw: M.start.raw
- }
+ raw: M.start.raw,
+ },
};
}
return {
type: "Literal",
value: value,
- raw: M.start.raw
+ raw: M.start.raw,
};
- });
+ }
+ def_to_moz(AST_Boolean, To_Moz_Literal);
+ def_to_moz(AST_Constant, To_Moz_Literal);
+ def_to_moz(AST_Null, To_Moz_Literal);
def_to_moz(AST_Atom, function To_Moz_Atom(M) {
return {
type: "Identifier",
- name: String(M.value)
+ name: String(M.value),
};
});
- AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
- AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
- AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null });
+ def_to_moz(AST_Template, function To_Moz_TemplateLiteral_TaggedTemplateExpression(M) {
+ var last = M.strings.length - 1;
+ var tmpl = {
+ type: "TemplateLiteral",
+ expressions: M.expressions.map(to_moz),
+ quasis: M.strings.map(function(str, index) {
+ return {
+ type: "TemplateElement",
+ tail: index == last,
+ value: { raw: str },
+ };
+ }),
+ };
+ if (!M.tag) return tmpl;
+ return {
+ type: "TaggedTemplateExpression",
+ tag: to_moz(M.tag),
+ quasi: tmpl,
+ };
+ });
AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast);
- AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast);
+ AST_Hole.DEFMETHOD("to_mozilla_ast", return_null);
+ AST_Node.DEFMETHOD("to_mozilla_ast", function() {
+ throw new Error("Cannot convert AST_" + this.TYPE);
+ });
/* -----[ tools ]----- */
+ function normalize_directives(body) {
+ for (var i = 0; i < body.length; i++) {
+ var stat = body[i];
+ if (!(stat instanceof AST_SimpleStatement)) break;
+ var node = stat.body;
+ if (!(node instanceof AST_String)) break;
+ if (stat.start.pos !== node.start.pos) break;
+ body[i] = new AST_Directive(node);
+ }
+ return body;
+ }
+
function raw_token(moznode) {
if (moznode.type == "Literal") {
return moznode.raw != null ? moznode.raw : moznode.value + "";
@@ -501,6 +1097,10 @@
});
}
+ function read_name(M) {
+ return "" + M[M.type == "Identifier" ? "name" : "value"];
+ }
+
function map(moztype, mytype, propmap) {
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
moz_to_me += "return new U2." + mytype.name + "({\n" +
@@ -518,24 +1118,24 @@
moz_to_me += ",\n" + my + ": ";
me_to_moz += ",\n" + moz + ": ";
switch (how) {
- case "@":
- moz_to_me += "M." + moz + ".map(from_moz)";
- me_to_moz += "M." + my + ".map(to_moz)";
- break;
- case ">":
- moz_to_me += "from_moz(M." + moz + ")";
- me_to_moz += "to_moz(M." + my + ")";
- break;
- case "=":
- moz_to_me += "M." + moz;
- me_to_moz += "M." + my;
- break;
- case "%":
- moz_to_me += "from_moz(M." + moz + ").body";
- me_to_moz += "to_moz_block(M)";
- break;
- default:
- throw new Error("Can't understand operator in propmap: " + prop);
+ case "@":
+ moz_to_me += "M." + moz + ".map(from_moz)";
+ me_to_moz += "M." + my + ".map(to_moz)";
+ break;
+ case ">":
+ moz_to_me += "from_moz(M." + moz + ")";
+ me_to_moz += "to_moz(M." + my + ")";
+ break;
+ case "=":
+ moz_to_me += "M." + moz;
+ me_to_moz += "M." + my;
+ break;
+ case "%":
+ moz_to_me += "from_moz(M." + moz + ").body";
+ me_to_moz += "to_moz_block(M)";
+ break;
+ default:
+ throw new Error("Can't understand operator in propmap: " + prop);
}
});
@@ -588,7 +1188,7 @@
return ast;
};
- function set_moz_loc(mynode, moznode, myparent) {
+ function set_moz_loc(mynode, moznode) {
var start = mynode.start;
var end = mynode.end;
if (start.pos != null && end.endpos != null) {
@@ -597,7 +1197,7 @@
if (start.line) {
moznode.loc = {
start: {line: start.line, column: start.col},
- end: end.endline ? {line: end.endline, column: end.endcol} : null
+ end: end.endline ? {line: end.endline, column: end.endcol} : null,
};
if (start.file) {
moznode.loc.source = start.file;
@@ -619,7 +1219,7 @@
function to_moz_block(node) {
return {
type: "BlockStatement",
- body: node.body.map(to_moz)
+ body: node.body.map(to_moz),
};
}
@@ -630,7 +1230,7 @@
}
return {
type: type,
- body: body
+ body: body,
};
}
})();
diff --git a/lib/parse.js b/lib/parse.js
index cdd607a2..415a20ee 100644
--- a/lib/parse.js
+++ b/lib/parse.js
@@ -2526,7 +2526,7 @@ function parse($TEXT, options) {
while (!is("eof"))
body.push(statement());
S.input.pop_directives_stack();
- var end = prev();
+ var end = prev() || start;
var toplevel = options.toplevel;
if (toplevel) {
toplevel.body = toplevel.body.concat(body);
diff --git a/package.json b/package.json
index e9ad500e..0ce8167a 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"LICENSE"
],
"devDependencies": {
- "acorn": "~7.1.0",
+ "acorn": "~8.2.1",
"semver": "~6.3.0"
},
"scripts": {
diff --git a/test/mozilla-ast.js b/test/mozilla-ast.js
index 5f5ad541..0f6db3ac 100644
--- a/test/mozilla-ast.js
+++ b/test/mozilla-ast.js
@@ -11,7 +11,7 @@ function try_beautify(code) {
mangle: false,
output: {
beautify: true,
- braces: true
+ braces: true,
}
});
if (beautified.error) {
@@ -35,12 +35,18 @@ function validate(ast) {
return UglifyJS.minify(ast, {
compress: false,
mangle: false,
+ validate: true,
});
}
+function fuzzy(code) {
+ return code.replace(/\bimport\s*\{\s*\}\s*from\s*(['"])/g, "import$1")
+ .replace(/\b(import\b.*?)\s*,\s*\{\s*\}\s*(from\s*['"])/g, "$1 $2");
+}
+
function test(original, estree, description) {
var transformed = validate(UglifyJS.AST_Node.from_mozilla_ast(estree));
- if (transformed.error || original !== transformed.code) {
+ if (transformed.error || original !== transformed.code && fuzzy(original) !== fuzzy(transformed.code)) {
console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round);
console.log("// original code");
@@ -72,19 +78,36 @@ for (var round = 1; round <= num_iterations; round++) {
compress: false,
mangle: false,
output: {
- ast: true
- }
+ ast: true,
+ },
});
- var ok = test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
+ var ok = true;
try {
- ok = test(uglified.code, acorn.parse(input), "acorn.parse()") && ok;
+ var estree = uglified.ast.to_mozilla_ast();
} catch (e) {
+ ok = false;
console.log("//=============================================================");
- console.log("// acorn parser failed... round", round);
+ console.log("// AST_Node.to_mozilla_ast() failed... round", round);
console.log(e);
console.log("// original code");
console.log(input);
}
+ if (ok) ok = test(uglified.code, estree, "AST_Node.to_mozilla_ast()");
+ if (ok) try {
+ ok = test(uglified.code, acorn.parse(input, {
+ ecmaVersion: "latest",
+ locations: true,
+ sourceType: "module",
+ }), "acorn.parse()");
+ } catch (e) {
+ if (ufuzz.verbose) {
+ console.log("//=============================================================");
+ console.log("// acorn parser failed... round", round);
+ console.log(e);
+ console.log("// original code");
+ console.log(input);
+ }
+ }
if (!ok) process.exit(1);
});
}
diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js
index b3650fa5..8ccaefe0 100644
--- a/test/ufuzz/index.js
+++ b/test/ufuzz/index.js
@@ -2045,6 +2045,7 @@ function createVarName(maybe, dontStore) {
if (require.main !== module) {
exports.createTopLevelCode = createTopLevelCode;
exports.num_iterations = num_iterations;
+ exports.verbose = verbose;
return;
}
diff --git a/tools/node.js b/tools/node.js
index f1fb28ba..d75fe013 100644
--- a/tools/node.js
+++ b/tools/node.js
@@ -56,6 +56,9 @@ if (+process.env["UGLIFY_BUG_REPORT"]) exports.minify = function(files, options)
function describe_ast() {
var out = OutputStream({ beautify: true });
+ doitem(AST_Node);
+ return out.get() + "\n";
+
function doitem(ctor) {
out.print("AST_" + ctor.TYPE);
var props = ctor.SELF_PROPS.filter(function(prop) {
@@ -86,9 +89,7 @@ function describe_ast() {
});
});
}
- };
- doitem(AST_Node);
- return out + "\n";
+ }
}
function infer_options(options) {