From 0c924fca64419801298b8abfe89a8df7c3b92fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Santos?= Date: Tue, 20 Mar 2018 20:27:06 +0000 Subject: [PATCH] further AST input/output --- lib/mozilla-ast.js | 94 ++++++++++++++++++++++++++----- test/input/spidermonkey/input.js | 97 +++++++++++++++++++++++++++++--- test/mocha/spidermonkey.js | 12 ++++ 3 files changed, 180 insertions(+), 23 deletions(-) diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js index 40423ddf..173c6a15 100644 --- a/lib/mozilla-ast.js +++ b/lib/mozilla-ast.js @@ -75,7 +75,12 @@ return new AST_Destructuring({ start: my_start_token(M), end: my_end_token(M), - names: M.elements.map(from_moz), + names: M.elements.map(function(elm) { + if (elm === null) { + return new AST_Hole(); + } + return from_moz(elm); + }), is_array: true }); }, @@ -86,6 +91,29 @@ names: M.properties.map(from_moz), }); }, + AssignmentPattern: function(M) { + return new AST_Binary({ + start: my_start_token(M), + end: my_end_token(M), + left: from_moz(M.left), + operator: "=", + right: from_moz(M.right) + }); + }, + SpreadElement: function(M) { + return new AST_Expansion({ + start: my_start_token(M), + end: my_end_token(M), + expression: from_moz(M.argument) + }); + }, + RestElement: function(M) { + return new AST_Expansion({ + start: my_start_token(M), + end: my_end_token(M), + expression: from_moz(M.argument) + }); + }, FunctionDeclaration: function(M) { return new AST_Defun({ start: my_start_token(M), @@ -113,7 +141,8 @@ start: my_start_token(M), end: my_end_token(M), argnames: M.params.map(from_moz), - body: from_moz(M.body) + body: from_moz(M.body), + async: M.async, }); }, ExpressionStatement: function(M) { @@ -147,14 +176,19 @@ if (M.method) { args.is_generator = M.value.generator; args.async = M.value.async; - if (key.type == "Identifier") { + if (!M.computed) { 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); + if (M.kind == "init") { + if (key.type != "Identifier") { + args.key = from_moz(key); + } + return new AST_ObjectKeyVal(args); + } args.key = new AST_SymbolMethod({ name: args.key }); @@ -172,7 +206,7 @@ var args = { start : my_start_token(M), end : my_end_token(M), - key : from_moz(M.key), + key : M.computed ? from_moz(M.key) : new AST_SymbolMethod({ name: M.key.name || M.key.value }), value : from_moz(M.value), static : M.static, }; @@ -287,12 +321,12 @@ 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) { + exported_names: M.specifiers && M.specifiers.length ? M.specifiers.map(function (specifier) { return new AST_NameMapping({ foreign_name: from_moz(specifier.exported), name: from_moz(specifier.local) }) - }), + }) : null, module_name: from_moz(M.source) }); }, @@ -350,7 +384,7 @@ : 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 == "MethodDefinition" ? (p.computed ? AST_SymbolRef : AST_SymbolMethod) : p.type == "CatchClause" ? AST_SymbolCatch : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef : AST_SymbolRef)({ @@ -400,7 +434,6 @@ 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"); @@ -417,6 +450,13 @@ return to_moz_scope("Program", M); }); + def_to_moz(AST_Expansion, function To_Moz_Spread(M, parent) { + return { + type: to_moz_in_destructuring() ? "RestElement" : "SpreadElement", + argument: to_moz(M.expression) + }; + }); + def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { return { type: "FunctionDeclaration", @@ -449,6 +489,7 @@ return { type: "ArrowFunctionExpression", params: M.argnames.map(to_moz), + async: M.async, body: body } }); @@ -598,6 +639,13 @@ }); def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) { + if (to_moz_in_destructuring()) { + return { + type: "AssignmentPattern", + left: to_moz(M.left), + right: to_moz(M.right) + }; + } return { type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression", left: to_moz(M.left), @@ -621,10 +669,16 @@ }); 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 + var key = M.key instanceof AST_Node ? to_moz(M.key) : { + type: "Identifier", + value: M.key }; + if (typeof M.key === "string") { + key = { + type: "Identifier", + name: M.key + }; + } var kind; if (M instanceof AST_ObjectKeyVal) { kind = "init"; @@ -638,7 +692,7 @@ if (parent instanceof AST_Class) { return { type: "MethodDefinition", - computed: !(M.key instanceof AST_Symbol), + computed: !(M.key instanceof AST_Symbol) || M.key instanceof AST_SymbolRef, kind: kind, static: M.static, key: to_moz(M.key), @@ -657,7 +711,7 @@ if (parent instanceof AST_Object) { return { type: "Property", - computed: !(M.key instanceof AST_Symbol), + computed: !(M.key instanceof AST_Symbol) || M.key instanceof AST_SymbolRef, kind: "init", method: true, shorthand: false, @@ -667,7 +721,7 @@ } return { type: "MethodDefinition", - computed: !(M.key instanceof AST_Symbol), + computed: !(M.key instanceof AST_Symbol) || M.key instanceof AST_SymbolRef, kind: M.key === "constructor" ? "constructor" : "method", static: M.static, key: to_moz(M.key), @@ -903,6 +957,16 @@ return ast }; + function to_moz_in_destructuring() { + var i = TO_MOZ_STACK.length; + while (i--) { + if (TO_MOZ_STACK[i] instanceof AST_Destructuring) { + return true; + } + } + return false; + }; + function to_moz_block(node) { return { type: "BlockStatement", diff --git a/test/input/spidermonkey/input.js b/test/input/spidermonkey/input.js index 3cf63fdc..9108aa0d 100644 --- a/test/input/spidermonkey/input.js +++ b/test/input/spidermonkey/input.js @@ -2,8 +2,8 @@ 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"; +import Bar1, { Foo2 } from "lel"; +import { Bar2 as kex, Baz as food } from "lel"; const x = 0b01; let y = 6; @@ -13,7 +13,7 @@ export const z = 4; export function fun() {} export * from "a.js"; export {A} from "a.js"; -export {A, B} from "a.js"; +export {A1, B1} from "a.js"; export {C}; (a, [b], {c:foo = 3}, ...d) => null; @@ -31,16 +31,24 @@ class Class extends Object { x = class { static staticMethod() {} static get foo() {} - static set bar() {} + static set bar(value) {} 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! */ } + static() { + // "static" can be a method name! + } + get() { + // "get" can be a method name! + } + async set() { + // "set" can be a method name! + } *bar() {} static *baz() {} *['constructor']() {} static ['constructor']() {} + [a]() {} + "%"(){} } y = { @@ -50,8 +58,81 @@ y = { *bar() {}, *['constructor']() {} } -console.log(new.target); +function f () { + 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; } + +// arrow.js + +var foo = ([]) => "foo"; +var bar = ({}) => "bar"; +var with_default = (foo = "default") => foo; +var object_with_default = ({foo = "default", bar: baz = "default"}) => foo; +var array_after_spread = (...[foo]) => foo; +var array_after_spread = (...{foo}) => foo; +var computed = ({ [compute()]: x }) => {}; +var array_hole = ([, , ...x] = [1, 2]) => {}; +var object_trailing_elision = ({foo,}) => {}; +var spread_empty_array = (...[]) => "foo"; +var spread_empty_object = (...{}) => "foo"; + +// async.js + +async (x) => await x + +// destructuring.js + +var [aa, bb] = cc; +var [aa, [bb, cc]] = dd; +var [,[,,,,,],,,zz,] = xx; // Trailing comma +var [,,zzz,,] = xxx; // Trailing comma after hole + +var {aa, bb} = {aa:1, bb:2}; +var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}}; + +for (const [x,y] in pairs); +for (const [a] = 0;;); +for (const {c} of cees); + +// object.js + +var a = { + get, + set: "foo", + get bar() { + return this.get; + }, + get 5() { + return "five"; + }, + get 0xf55() { + return "f five five"; + }, + get "five"() { + return 5; + }, + set one(value) { + this._one = value; + }, + set 9(value) { + this._nine = value; + }, + set 0b1010(value) { + this._ten = value; + }, + set "eleven"(value) { + this._eleven = value; + }, + *"%"() { + return 2; + }, + *["%"]() { + return 2; + }, + [a]() {} +}; + diff --git a/test/mocha/spidermonkey.js b/test/mocha/spidermonkey.js index 381b7dd2..76c5b674 100644 --- a/test/mocha/spidermonkey.js +++ b/test/mocha/spidermonkey.js @@ -1,6 +1,7 @@ var assert = require("assert"); var fs = require("fs"); var exec = require("child_process").exec; +var acorn = require("acorn"); var uglify = require("../node"); describe("spidermonkey export/import sanity test", function() { @@ -124,4 +125,15 @@ describe("spidermonkey export/import sanity test", function() { uglify_ast.print_to_string() ); }); + + it("should be capable of importing from acorn", function() { + var code = fs.readFileSync("test/input/spidermonkey/input.js", "utf-8"); + var uglify_ast = uglify.parse(code); + var moz_ast = acorn.parse(code, {sourceType: 'module', ecmaVersion: 9}); + 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() + ); + }); });