Attempt to close #968 by implementing the mozilla tree for ES6 code

This commit is contained in:
Fábio Santos 2018-03-19 16:33:35 +00:00
parent 569757d14d
commit 9f7246305c
5 changed files with 395 additions and 11 deletions

View File

@ -643,7 +643,7 @@ var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
this.name._walk(visitor);
});
}
})
});
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
$documentation: "An `import` statement",

View File

@ -71,12 +71,29 @@
body: normalize_directives(M.body.map(from_moz))
});
},
ArrayPattern: function(M) {
return new AST_Destructuring({
start: my_start_token(M),
end: my_end_token(M),
names: M.elements.map(from_moz),
is_array: true
});
},
ObjectPattern: function(M) {
return new AST_Destructuring({
start: my_start_token(M),
end: my_end_token(M),
names: M.properties.map(from_moz),
});
},
FunctionDeclaration: function(M) {
return new AST_Defun({
start: my_start_token(M),
end: my_end_token(M),
name: from_moz(M.id),
argnames: M.params.map(from_moz),
is_generator: M.generator,
async: M.async,
body: normalize_directives(from_moz(M.body).body)
});
},
@ -86,9 +103,19 @@
end: my_end_token(M),
name: from_moz(M.id),
argnames: M.params.map(from_moz),
is_generator: M.generator,
async: M.async,
body: normalize_directives(from_moz(M.body).body)
});
},
ArrowFunctionExpression: function(M) {
return new AST_Arrow({
start: my_start_token(M),
end: my_end_token(M),
argnames: M.params.map(from_moz),
body: from_moz(M.body)
});
},
ExpressionStatement: function(M) {
return new AST_SimpleStatement({
start: my_start_token(M),
@ -117,6 +144,16 @@
key : key.type == "Identifier" ? key.name : key.value,
value : from_moz(M.value)
};
if (M.method) {
args.is_generator = M.value.generator;
args.async = M.value.async;
if (key.type == "Identifier") {
args.key = new AST_SymbolMethod({ name: args.key });
} else {
args.key = from_moz(M.key);
}
return new AST_ConciseMethod(args);
}
if (M.kind == "init") return new AST_ObjectKeyVal(args);
args.key = new AST_SymbolMethod({
name: args.key
@ -124,6 +161,30 @@
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);
if (M.kind == "method") {
args.async = M.value.async;
args.is_generator = M.value.generator;
args.quote = M.computed ? "\"" : null;
return new AST_ConciseMethod(args);
}
},
MethodDefinition: function(M) {
var args = {
start : my_start_token(M),
end : my_end_token(M),
key : from_moz(M.key),
value : from_moz(M.value),
static : M.static,
};
if (M.kind == "get") {
return new AST_ObjectGetter(args);
}
if (M.kind == "set") {
return new AST_ObjectSetter(args);
}
args.is_generator = M.value.generator;
args.async = M.value.async;
return new AST_ConciseMethod(args);
},
ArrayExpression: function(M) {
return new AST_Array({
@ -168,12 +229,81 @@
});
},
VariableDeclaration: function(M) {
return new (M.kind === "const" ? AST_Const : AST_Var)({
return new (M.kind === "const" ? AST_Const :
M.kind === "let" ? AST_Let : AST_Var)({
start : my_start_token(M),
end : my_end_token(M),
definitions : M.declarations.map(from_moz)
});
},
ImportDeclaration: function(M) {
var imported_name = null;
var imported_names = null;
M.specifiers.forEach(function (specifier) {
if (specifier.type === "ImportSpecifier") {
if (!imported_names) { imported_names = []; }
imported_names.push(new AST_NameMapping({
start: my_start_token(specifier),
end: my_end_token(specifier),
foreign_name: from_moz(specifier.imported),
name: from_moz(specifier.local)
}));
} else if (specifier.type === "ImportDefaultSpecifier") {
imported_name = from_moz(specifier.local);
} else if (specifier.type === "ImportNamespaceSpecifier") {
if (!imported_names) { imported_names = []; }
imported_names.push(new AST_NameMapping({
start: my_start_token(specifier),
end: my_end_token(specifier),
foreign_name: new AST_SymbolImportForeign({ name: "*" }),
name: from_moz(specifier.local)
}));
}
});
return new AST_Import({
start : my_start_token(M),
end : my_end_token(M),
imported_name: imported_name,
imported_names : imported_names,
module_name : from_moz(M.source)
})
},
ExportAllDeclaration: function(M) {
return new AST_Export({
start: my_start_token(M),
end: my_end_token(M),
exported_names: [
new AST_NameMapping({
name: new AST_SymbolExportForeign({ name: "*" }),
foreign_name: new AST_SymbolExportForeign({ name: "*" })
})
],
module_name: from_moz(M.source)
});
},
ExportNamedDeclaration: function(M) {
return new AST_Export({
start: my_start_token(M),
end: my_end_token(M),
exported_definition: from_moz(M.declaration),
exported_names: M.specifiers && M.specifiers.map(function (specifier) {
return new AST_NameMapping({
foreign_name: from_moz(specifier.exported),
name: from_moz(specifier.local)
})
}),
module_name: from_moz(M.source)
});
},
ExportDefaultDeclaration: function(M) {
return new AST_Export({
start: my_start_token(M),
end: my_end_token(M),
exported_value: from_moz(M.declaration),
is_default: true
});
},
Literal: function(M) {
var val = M.value, args = {
start : my_start_token(M),
@ -201,12 +331,26 @@
return new AST_RegExp(args);
}
},
MetaProperty: function(M) {
if (M.meta.name === "new" && M.property.name === "target") {
return new AST_NewTarget({
start: my_start_token(M),
end: my_end_token(M)
});
}
},
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 ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : p.kind == "let" ? AST_SymbolLet : AST_SymbolVar)
: /Import.*Specifier/.test(p.type) ? (p.local === M ? AST_SymbolImport : AST_SymbolImportForeign)
: p.type == "ExportSpecifier" ? (p.local === M ? AST_SymbolExport : AST_SymbolExportForeign)
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
: p.type == "ArrowFunctionExpression" ? (p.params.indexOf(M) !== -1) ? AST_SymbolFunarg : AST_SymbolRef
: p.type == "ClassExpression" ? (p.id === M ? AST_SymbolClass : AST_SymbolRef)
: p.type == "ClassDeclaration" ? (p.id === M ? AST_SymbolDefClass : AST_SymbolRef)
: p.type == "MethodDefinition" ? AST_SymbolMethod
: p.type == "CatchClause" ? AST_SymbolCatch
: p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef
: AST_SymbolRef)({
@ -229,6 +373,17 @@
});
};
MOZ_TO_ME.ClassDeclaration =
MOZ_TO_ME.ClassExpression = function From_Moz_Class(M) {
return new AST_Class({
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)
});
};
map("EmptyStatement", AST_EmptyStatement);
map("BlockStatement", AST_BlockStatement, "body@body");
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
@ -243,6 +398,9 @@
map("DoWhileStatement", AST_Do, "test>condition, body>body");
map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
map("ForOfStatement", AST_ForOf, "left>init, right>object, body>body");
map("AwaitExpression", AST_Await, "argument>expression");
map("RestElement", AST_Expansion, "argument>expression");
map("DebuggerStatement", AST_Debugger);
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
map("CatchClause", AST_Catch, "param>argname, body%body");
@ -264,19 +422,50 @@
type: "FunctionDeclaration",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
generator: M.is_generator,
async: M.async,
body: to_moz_scope("BlockStatement", M)
}
});
def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) {
def_to_moz(AST_Function, function To_Moz_FunctionExpression(M, parent) {
var is_generator = parent.is_generator !== undefined ?
parent.is_generator : M.is_generator
return {
type: "FunctionExpression",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
generator: is_generator,
async: M.async,
body: to_moz_scope("BlockStatement", M)
}
});
def_to_moz(AST_Arrow, function To_Moz_ArrowFunctionExpression(M) {
var body = M.body instanceof Array ? {
type: "BlockStatement",
body: M.body.map(to_moz)
} : to_moz(M.body);
return {
type: "ArrowFunctionExpression",
params: M.argnames.map(to_moz),
body: body
}
});
def_to_moz(AST_Destructuring, function To_Moz_ObjectPattern(M) {
if (M.is_array) {
return {
type: "ArrayPattern",
elements: M.names.map(to_moz)
}
}
return {
type: "ObjectPattern",
properties: M.names.map(to_moz)
};
});
def_to_moz(AST_Directive, function To_Moz_Directive(M) {
return {
type: "ExpressionStatement",
@ -324,11 +513,64 @@
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
return {
type: "VariableDeclaration",
kind: M instanceof AST_Const ? "const" : "var",
kind:
M instanceof AST_Const ? "const" :
M instanceof AST_Let ? "let" : "var",
declarations: M.definitions.map(to_moz)
};
});
def_to_moz(AST_Export, function To_Moz_ExportDeclaration(M) {
if (M.exported_names) {
if (M.exported_names[0].name.name === "*") {
return {
type: "ExportAllDeclaration",
source: to_moz(M.module_name)
};
}
return {
type: "ExportNamedDeclaration",
specifiers: M.exported_names.map(function (name_mapping) {
return {
type: "ExportSpecifier",
exported: to_moz(name_mapping.foreign_name),
local: to_moz(name_mapping.name)
};
}),
declaration: to_moz(M.exported_definition),
source: to_moz(M.module_name)
};
}
return {
type: M.is_default ? "ExportDefaultDeclaration" : "ExportNamedDeclaration",
declaration: to_moz(M.is_default ? M.exported_value : M.exported_definition)
};
});
def_to_moz(AST_Import, function To_Moz_ImportDeclaration(M) {
var specifiers = [];
if (M.imported_name) {
specifiers.push({
type: "ImportDefaultSpecifier",
local: to_moz(M.imported_name)
});
}
if (M.imported_names) {
M.imported_names.forEach(function(name_mapping) {
specifiers.push({
type: "ImportSpecifier",
local: to_moz(name_mapping.name),
imported: to_moz(name_mapping.foreign_name)
});
});
}
return {
type: "ImportDeclaration",
specifiers: specifiers,
source: to_moz(M.module_name)
}
});
def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) {
return {
type: "SequenceExpression",
@ -378,7 +620,7 @@
};
});
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M, parent) {
var key = {
type: "Literal",
value: M.key instanceof AST_SymbolMethod ? M.key.name : M.key
@ -393,6 +635,16 @@
if (M instanceof AST_ObjectSetter) {
kind = "set";
}
if (parent instanceof AST_Class) {
return {
type: "MethodDefinition",
computed: !(M.key instanceof AST_Symbol),
kind: kind,
static: M.static,
key: to_moz(M.key),
value: to_moz(M.value)
};
}
return {
type: "Property",
kind: kind,
@ -401,6 +653,55 @@
};
});
def_to_moz(AST_ConciseMethod, function To_Moz_MethodDefinition(M, parent) {
if (parent instanceof AST_Object) {
return {
type: "Property",
computed: !(M.key instanceof AST_Symbol),
kind: "init",
method: true,
shorthand: false,
key: to_moz(M.key),
value: to_moz(M.value)
};
}
return {
type: "MethodDefinition",
computed: !(M.key instanceof AST_Symbol),
kind: M.key === "constructor" ? "constructor" : "method",
static: M.static,
key: to_moz(M.key),
value: to_moz(M.value)
};
});
def_to_moz(AST_Class, function To_Moz_Class(M) {
var type = M instanceof AST_ClassExpression ? "ClassExpression" : "ClassDeclaration";
return {
type: type,
superClass: to_moz(M.extends),
id: M.name ? to_moz(M.name) : null,
body: {
type: "ClassBody",
body: M.properties.map(to_moz)
}
}
});
def_to_moz(AST_NewTarget, function To_Moz_MetaProperty(M) {
return {
type: "MetaProperty",
meta: {
type: "Identifier",
name: "new"
},
property: {
type: "Identifier",
name: "target"
}
};
});
def_to_moz(AST_Symbol, function To_Moz_Identifier(M) {
var def = M.definition();
return {
@ -586,13 +887,20 @@
};
function def_to_moz(mytype, handler) {
mytype.DEFMETHOD("to_mozilla_ast", function() {
return set_moz_loc(this, handler(this));
mytype.DEFMETHOD("to_mozilla_ast", function(parent) {
return set_moz_loc(this, handler(this, parent));
});
};
var TO_MOZ_STACK = null;
function to_moz(node) {
return node != null ? node.to_mozilla_ast() : null;
if (TO_MOZ_STACK === null) { TO_MOZ_STACK = []; }
TO_MOZ_STACK.push(node);
var ast = node != null ? node.to_mozilla_ast(TO_MOZ_STACK[TO_MOZ_STACK.length - 2]) : null;
TO_MOZ_STACK.pop();
if (TO_MOZ_STACK.length === 0) { TO_MOZ_STACK = null; }
return ast
};
function to_moz_block(node) {

View File

@ -1294,9 +1294,14 @@ function parse($TEXT, options) {
var body = _function_body(is("punc", "{"), false, is_async);
var end =
body instanceof Array && body.length ? body[body.length - 1].end :
body instanceof Array ? start :
body.end;
return new AST_Arrow({
start : start,
end : body.end,
end : end,
async : is_async,
argnames : argnames,
body : body
@ -1709,6 +1714,8 @@ function parse($TEXT, options) {
}
// the await expression is parsed as a unary expression in Babel
return new AST_Await({
start: prev(),
end: S.token,
expression : maybe_unary(true),
});
}
@ -2478,7 +2485,7 @@ function parse($TEXT, options) {
next();
if (is_import && is("name", "as")) {
next(); // The "as" word
name = as_symbol(AST_SymbolImportForeign);
name = as_symbol(is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign);
}
names = [map_nameAsterisk(is_import, name)];
}

View File

@ -0,0 +1,57 @@
import "mod-name";
import Foo from "bar";
import * as Food from "food"
import { Bar, Baz } from "lel";
import Bar, { Foo } from "lel";
import { Bar as kex, Baz as food } from "lel";
const x = 0b01;
let y = 6;
export default x;
export const z = 4;
export function fun() {}
export * from "a.js";
export {A} from "a.js";
export {A, B} from "a.js";
export {C};
(a, [b], {c:foo = 3}, ...d) => null;
() => {}
async function f() { }
function*gen() { }
class Class extends Object {
constructor(...args) {
}
foo() {}
}
x = class {
static staticMethod() {}
static get foo() {}
static set bar() {}
get x() {}
set x(value) {}
static() { /* "static" can be a method name! */ }
get() { /* "get" can be a method name! */ }
set() { /* "set" can be a method name! */ }
*bar() {}
static *baz() {}
*['constructor']() {}
static ['constructor']() {}
}
y = {
get x() {},
set x(value) {},
bar() {},
*bar() {},
*['constructor']() {}
}
console.log(new.target);
console.log([10, ...[], 20, ...[30, 40], 50]["length"]);
var { w: w1, ...V } = { w: 7, x: 1, y: 2 };
for (const x of y) {}
async function f1() { await x + y; }

View File

@ -1,4 +1,5 @@
var assert = require("assert");
var fs = require("fs");
var exec = require("child_process").exec;
var uglify = require("../node");
@ -112,4 +113,15 @@ describe("spidermonkey export/import sanity test", function() {
assert.strictEqual(counter_strings, tests[i].strings, "String count mismatch for test " + tests[i].input);
}
});
it("should output and parse ES6 code correctly", function() {
var code = fs.readFileSync("test/input/spidermonkey/input.js", "utf-8");
var uglify_ast = uglify.parse(code);
var moz_ast = uglify_ast.to_mozilla_ast();
var from_moz_ast = uglify.AST_Node.from_mozilla_ast(moz_ast);
assert.strictEqual(
from_moz_ast.print_to_string(),
uglify_ast.print_to_string()
);
});
});