Destructuring consistency fixes (#1417)

- Use AST_Destructuring for lhf assignment patterns
- Use AST_DefaultAssign for default assignments
- Add more checks for lhs expressions
- Add lots of testing
- Cleanup ast (e.g. remove default property)
- Fix #1402 based on a patch from @kzc
- Refine spread allowance in array destructring pattern
- Add destructuring AST tree checker
This commit is contained in:
Anthony Van de Gejuchte
2017-02-24 01:49:19 +01:00
committed by Alex Lam S.L
parent 85c1cba760
commit 07734b000a
16 changed files with 1253 additions and 141 deletions

View File

@@ -392,46 +392,56 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
// We don't want anything which doesn't belong in a destructuring
var root = this;
return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) {
var insert_default = function(ex, default_value) {
if (default_value) {
return new AST_DefaultAssign({
start: ex.start,
left: ex,
operator: "=",
right: default_value,
end: default_value.end
});
}
return ex;
}
if (ex instanceof AST_Object) {
return new AST_Destructuring({
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: false,
default: default_seen_above,
names: ex.properties.map(to_fun_args)
});
}), default_seen_above);
} else if (ex instanceof AST_ObjectKeyVal) {
if (ex.key instanceof AST_SymbolRef) {
ex.key = to_fun_args(ex.key, 0, [ex.key], ex.default);
ex.key = to_fun_args(ex.key, 0, [ex.key]);
}
ex.value = to_fun_args(ex.value, 0, [ex.key], ex.default);
return ex;
ex.value = to_fun_args(ex.value, 0, [ex.key]);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Hole) {
return ex;
} else if (ex instanceof AST_Destructuring) {
if (ex.names.length == 0)
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
ex.names = ex.names.map(to_fun_args);
return ex;
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_SymbolRef) {
return new AST_SymbolFunarg({
return insert_default(new AST_SymbolFunarg({
name: ex.name,
default: default_seen_above,
start: ex.start,
end: ex.end
});
}), default_seen_above);
} else if (ex instanceof AST_Expansion) {
return ex;
ex.expression = to_fun_args(ex.expression);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Array) {
return new AST_Destructuring({
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: true,
default: default_seen_above,
names: ex.elements.map(to_fun_args)
});
}), default_seen_above);
} else if (ex instanceof AST_Assign) {
return to_fun_args(ex.left, undefined, undefined, ex.right);
return insert_default(to_fun_args(ex.left, undefined, undefined, ex.right), default_seen_above);
} else {
croak("Invalid function parameter", ex.start.line, ex.start.col);
}
@@ -447,7 +457,7 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator",
$propdoc: {
is_generator: "[boolean] is generatorFn or not",
name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion*] array of function arguments, destructurings, or expanding arguments",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
},
args_as_names: function () {
@@ -489,12 +499,11 @@ var AST_Defun = DEFNODE("Defun", null, {
}, AST_Lambda);
/* -----[ DESTRUCTURING ]----- */
var AST_Destructuring = DEFNODE("Destructuring", "names is_array default", {
var AST_Destructuring = DEFNODE("Destructuring", "names is_array", {
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
$propdoc: {
"names": "[AST_Destructuring|AST_Expansion|AST_Hole|AST_ObjectKeyVal|AST_Symbol] Array of properties or elements",
"is_array": "[Boolean] Whether the destructuring represents an object or array",
"default": "[AST_Node?] Default assign value"
"names": "[AST_Node*] Array of properties or elements",
"is_array": "[Boolean] Whether the destructuring represents an object or array"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
@@ -672,7 +681,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
var AST_Catch = DEFNODE("Catch", "argname", {
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
$propdoc: {
argname: "[AST_SymbolCatch] symbol for the exception"
argname: "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
@@ -957,6 +966,10 @@ var AST_Assign = DEFNODE("Assign", null, {
$documentation: "An assignment expression — `a = b + 5`",
}, AST_Binary);
var AST_DefaultAssign = DEFNODE("DefaultAssign", null, {
$documentation: "A default assignment expression like in `(a = 3) => a`"
}, AST_Binary);
/* -----[ LITERALS ]----- */
var AST_Array = DEFNODE("Array", "elements", {
@@ -991,8 +1004,7 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties",
$propdoc: {
key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node",
value: "[AST_Node] property value. For setters and getters this is an AST_Function.",
default: "[AST_Expression] The default for this parameter, only used when nested inside a binding pattern"
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
},
_walk: function(visitor) {
return visitor._visit(this, function(){
@@ -1003,11 +1015,10 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
}
});
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote default", {
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
$documentation: "A key: value object property",
$propdoc: {
quote: "[string] the original quote character",
default: "[AST_Expression] The default parameter value, only used when nested inside a binding pattern"
quote: "[string] the original quote character"
}
}, AST_ObjectProperty);
@@ -1066,19 +1077,13 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, {
$documentation: "A class expression."
}, AST_Class);
var AST_Symbol = DEFNODE("Symbol", "scope name thedef default", {
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
$propdoc: {
name: "[string] name of this symbol",
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
thedef: "[SymbolDef/S] the definition of this symbol",
default: "[AST_Expression] The default parameter value, only used when nested inside a binding pattern"
thedef: "[SymbolDef/S] the definition of this symbol"
},
$documentation: "Base class for all symbols",
_walk: function (visitor) {
return visitor._visit(this, function() {
if (this.default) this.default._walk(visitor);
});
}
$documentation: "Base class for all symbols"
});
var AST_NewTarget = DEFNODE("NewTarget", null, {

View File

@@ -74,6 +74,7 @@ function Compressor(options, false_by_default) {
pure_funcs : null,
negate_iife : !false_by_default,
screw_ie8 : true,
ecma : 5,
drop_console : false,
angular : false,
warnings : true,
@@ -2317,7 +2318,7 @@ merge(Compressor.prototype, {
if (!fun) return self;
var args = fun.argnames.map(function(arg, i){
return make_node(AST_String, self.args[i], {
value: arg.print_to_string()
value: arg.print_to_string({ecma: compressor.option("ecma")})
});
});
var code = OutputStream();
@@ -2886,6 +2887,22 @@ merge(Compressor.prototype, {
return self;
});
OPT(AST_DefaultAssign, function(self, compressor){
if (!compressor.option("evaluate")) {
return self;
}
var evaluateRight = self.right.evaluate(compressor);
// `[x = undefined] = foo` ---> `[x] = foo`
if (evaluateRight.length > 1 && evaluateRight[1] === undefined) {
self = self.left;
} else {
self.right = evaluateRight[0];
}
return self;
});
OPT(AST_Conditional, function(self, compressor){
if (!compressor.option("conditionals")) return self;
if (self.condition instanceof AST_Seq) {

View File

@@ -641,13 +641,10 @@ function OutputStream(options) {
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| (p instanceof AST_SymbolConst && p.default === this) // const { xCover = (0, function() {}) }
|| (p instanceof AST_SymbolLet && p.default === this) // let { xCover = (0, function() {}) }
|| (p instanceof AST_SymbolVar && p.default === this) // var { xCover = (0, function() {}) }
|| (p instanceof AST_SymbolCatch && p.default === this) // } catch (xCover = (0, function() {}) ) {
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
* ==> 20 (side effect, set a := 10 and b := 20) */
|| p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
;
});
@@ -761,6 +758,9 @@ function OutputStream(options) {
// (a = foo)["prop"] —or— (a = foo).prop
if (p instanceof AST_PropAccess && p.expression === this)
return true;
// ({a, b} = {a: 1, b: 2}), a destructuring assignment
if (this instanceof AST_Assign && this.left instanceof AST_Destructuring && this.left.is_array === false)
return true;
});
/* -----[ PRINTERS ]----- */
@@ -789,12 +789,6 @@ function OutputStream(options) {
output.comma();
})
output.print(self.is_array ? "]" : "}");
if (self.default) {
output.space();
output.print('=');
output.space();
self.default.print(output)
}
});
DEFPRINT(AST_Debugger, function(self, output){
@@ -1003,7 +997,7 @@ function OutputStream(options) {
parent instanceof AST_Unary ||
(parent instanceof AST_Call && self === parent.expression);
if (needs_parens) { output.print("(") }
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol && !self.argnames[0].default) {
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
self.argnames[0].print(output);
} else {
output.with_parens(function(){
@@ -1497,15 +1491,27 @@ function OutputStream(options) {
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) {
var def = self.value.definition();
return def ? def.mangled_name || def.name : self.value.name;
var def = self.definition();
return def ? def.mangled_name || def.name : self.name;
}
if (output.option("shorthand") &&
var allowShortHand = output.option("shorthand");
if (allowShortHand &&
self.value instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self) === self.key
get_name(self.value) === self.key
) {
self.print_property_name(self.key, self.quote, output);
} else if (allowShortHand &&
self.value instanceof AST_DefaultAssign &&
self.value.left instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self.value.left) === self.key
) {
self.print_property_name(self.key, self.quote, output);
output.print("=");
self.value.right.print(output);
} else {
if (!(self.key instanceof AST_Node)) {
self.print_property_name(self.key, self.quote, output);
@@ -1517,12 +1523,6 @@ function OutputStream(options) {
output.colon();
self.value.print(output);
}
if (self.default) {
output.space();
output.print('=');
output.space();
self.default.print(output);
}
});
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) {
if (self.static) {
@@ -1573,12 +1573,6 @@ function OutputStream(options) {
});
DEFPRINT(AST_SymbolDeclaration, function(self, output){
self._do_print(output);
if (self.default) {
output.space();
output.print('=');
output.space();
self.default.print(output)
}
});
DEFPRINT(AST_Undefined, function(self, output){
output.print("void 0");

View File

@@ -1106,11 +1106,9 @@ function parse($TEXT, options) {
case "export":
return export_();
default:
unexpected();
}
}
unexpected();
});
function labeled_statement() {
@@ -1384,7 +1382,13 @@ function parse($TEXT, options) {
if (is("operator", "=") && expand === false) {
used_parameters.mark_default_assignment(S.token);
next();
param.default = expression(false);
param = new AST_DefaultAssign({
start: param.start,
left: param,
operator: "=",
right: expression(false),
end: S.token
});
}
if (expand !== false) {
@@ -1458,7 +1462,13 @@ function parse($TEXT, options) {
if (is("operator", "=") && is_expand === false) {
used_parameters.mark_default_assignment(S.token);
next();
elements[elements.length - 1].default = expression(false);
elements[elements.length - 1] = new AST_DefaultAssign({
start: elements[elements.length - 1].start,
left: elements[elements.length - 1],
operator: "=",
right: expression(false),
end: S.token
});
}
if (is_expand) {
if (!is("punc", "]")) {
@@ -1489,10 +1499,15 @@ function parse($TEXT, options) {
}
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
used_parameters.add_parameter(S.token);
elements.push(new symbol_type({
start: S.token,
name: S.token.value,
end: S.token
elements.push(new AST_ObjectKeyVal({
start: prev(),
key: S.token.value,
value: new symbol_type({
start: S.token,
name: S.token.value,
end: S.token
}),
end: prev()
}));
next();
} else if (is("punc", "}")) {
@@ -1503,9 +1518,14 @@ function parse($TEXT, options) {
if (property === null) {
unexpected(prev());
} else if (prev().type === "name" && !is("punc", ":")) {
elements.push(new AST_SymbolFunarg({
elements.push(new AST_ObjectKeyVal({
start: prev(),
name: property_token,
key: property,
value: new symbol_type({
start: prev(),
name: property,
end: prev()
}),
end: prev()
}));
} else {
@@ -1513,16 +1533,22 @@ function parse($TEXT, options) {
elements.push(new AST_ObjectKeyVal({
start: property_token,
quote: property_token.quote,
end: prev(),
key: property,
value: binding_element(used_parameters, symbol_type)
value: binding_element(used_parameters, symbol_type),
end: prev()
}));
}
}
if (is("operator", "=")) {
used_parameters.mark_default_assignment(S.token);
next();
elements[elements.length - 1].default = expression(false);
elements[elements.length - 1].value = new AST_DefaultAssign({
start: elements[elements.length - 1].value.start,
left: elements[elements.length - 1].value,
operator: "=",
right: expression(false),
end: S.token
});
}
}
expect("}");
@@ -1558,7 +1584,7 @@ function parse($TEXT, options) {
next();
a.push(new AST_Expansion({
start: prev(),
expression: binding_element(undefined, AST_SymbolFunarg),
expression: expression(false),
end: S.token,
}));
if (!is("punc", ")")) {
@@ -1943,7 +1969,7 @@ function parse($TEXT, options) {
a.push(new AST_Hole({ start: S.token, end: S.token }));
} else if (is("expand", "...")) {
next();
a.push(new AST_Expansion({start: S.token, expression: expression(),end: S.token}));
a.push(new AST_Expansion({start: prev(), expression: expression(),end: S.token}));
} else {
a.push(expression(false));
}
@@ -1970,63 +1996,51 @@ function parse($TEXT, options) {
start = S.token;
var type = start.type;
var name = as_property_name();
var value;
// Check property and fetch value
if (!is("punc", ":")) {
var concise = concise_method_or_getset(name, start);
if (concise) {
a.push(concise);
continue;
}
if (!(start.type !== name)) {
unexpected(S.token);
}
value = new AST_SymbolRef({
start: prev(),
name: name,
end: prev()
});
} else if (name === null) {
unexpected(prev());
} else {
next(); // `:` - see first condition
value = expression(false);
}
if (type == "punc" && start.value == "[") {
expect(":");
a.push(new AST_ObjectKeyVal({
start: start,
key: name,
value: expression(false),
end: prev()
}));
continue;
}
// Check for default value and alter value accordingly if necessary
if (is("operator", "=")) {
next();
a.push(new AST_Assign({
value = new AST_Assign({
start: start,
// Symbol class doesn't matter. This is only meant to carry the symbol name into .as_params() since this is not normally valid.
left: new AST_SymbolRef({
start: start,
end: start,
name: name
}),
left: value,
operator: "=",
right: expression(false),
end: prev()
}));
} else if (!is("punc", ":")) {
// It's one of those object destructurings, the value is its own name
a.push(new AST_ObjectKeyVal({
start: start,
key: name,
value: new AST_SymbolRef({
start: start,
end: prev(),
name: name
}),
end: start,
}));
} else {
expect(":");
a.push(new AST_ObjectKeyVal({
start : start,
quote : start.quote,
key : name,
value : expression(false),
end : prev()
}));
});
}
// Create property
a.push(new AST_ObjectKeyVal({
start: start,
quote: start.quote,
key: name,
value: value,
end: prev()
}));
}
next();
return new AST_Object({ properties: a })
@@ -2493,6 +2507,55 @@ function parse($TEXT, options) {
if (is("operator") && ASSIGNMENT(val)) {
if (is_assignable(left)) {
var walk = function(node) {
var newNode;
if (node instanceof AST_Object) {
newNode = new AST_Destructuring({
start: node.start,
names: node.properties.map(walk),
is_array: false,
end: node.end
});
node = newNode;
} else if (node instanceof AST_Array) {
var names = [];
for (var i = 0; i < node.elements.length; i++) {
// Only allow expansion as last element
if (node.elements[i] instanceof AST_Expansion) {
if (i + 1 !== node.elements.length) {
token_error(node.elements[i].start, "SyntaxError: Spread must the be last element in destructuring array");
}
node.elements[i].expression = walk(node.elements[i].expression);
}
names.push(walk(node.elements[i]));
}
newNode = new AST_Destructuring({
start: node.start,
names: names,
is_array: true,
end: node.end
});
node = newNode;
} else if (node instanceof AST_ObjectProperty) {
node.value = walk(node.value);
} else if (node instanceof AST_Assign) {
node = new AST_DefaultAssign({
start: node.start,
left: node.left,
operator: "=",
right: node.right,
end: node.end
});
}
return node;
}
left = walk(left);
next();
return new AST_Assign({
start : start,

View File

@@ -50,6 +50,9 @@ destructuring_objects: {
}
destructuring_objects_trailing_elision: {
beautify = {
ecma: 6
}
input: {
var {cc,} = foo;
}
@@ -57,6 +60,9 @@ destructuring_objects_trailing_elision: {
}
nested_destructuring_objects: {
beautify = {
ecma: 6
}
input: {
const [{a},b] = c;
let [{a},b] = c;
@@ -66,6 +72,9 @@ nested_destructuring_objects: {
}
destructuring_constdef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (const [x,y] in pairs);
for (const [a] = 0;;);
@@ -75,6 +84,9 @@ destructuring_constdef_in_loops: {
}
destructuring_letdef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (let [x,y] in pairs);
for (let [a] = 0;;);
@@ -84,6 +96,9 @@ destructuring_letdef_in_loops: {
}
destructuring_vardef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (var [x,y] in pairs);
for (var [a] = 0;;);
@@ -200,3 +215,39 @@ destructuring_remove_unused_2: {
}
}
}
object_destructuring_may_need_parentheses: {
beautify = {
ecma: 6
}
input: {
({a, b} = {a: 1, b: 2});
}
expect_exact: "({a,b}={a:1,b:2});"
}
destructuring_with_undefined_as_default_assignment: {
options = {
evaluate: true
}
input: {
[foo = undefined] = bar;
[foo = void 0] = bar;
}
expect: {
[foo] = bar;
[foo] = bar;
}
}
destructuring_dont_evaluate_with_undefined_as_default_assignment: {
options = {
evaluate: false
}
input: {
[foo = undefined] = bar;
}
expect: {
[foo = void 0] = bar;
}
}

View File

@@ -13,7 +13,11 @@ compress_new_function: {
compress_new_function_with_destruct: {
options = {
unsafe: true
unsafe: true,
ecma: 6
}
beautify = {
ecma: 6
}
input: {
new Function("aa, [bb]", 'return aa;');

View File

@@ -82,3 +82,19 @@ new_with_unary_prefix: {
}
expect_exact: 'var bar=(+new Date).toString(32);';
}
new_with_assignement_expression: {
options = {
evaluate: true
}
input: {
var a;
new x(a = 5 * 2, b = [1, 2, 3], c = {a: "a", b: "b", cd: "c" + "d"});
new y([a, b] = [3, 4]);
}
expect: {
var a;
new x(a = 10, b = [1, 2, 3], c = {a: "a", b: "b", cd: "cd"});
new y([a, b] = [3, 4]);
}
}

View File

@@ -170,6 +170,9 @@ shorthand_properties: {
}
concise_methods: {
beautify = {
ecma: 6
}
input: {
x = {
foo(a, b) {
@@ -299,6 +302,9 @@ concise_methods_and_mangle_props: {
}
concise_generators: {
beautify = {
ecma: 6
}
input: {
x = {
*foo(a, b) {

View File

@@ -119,6 +119,9 @@ destructuring_arguments_2: {
}
destructuring_arguments_3: {
beautify = {
ecma: 6
}
input: {
function fn3({x: {y: {z: {} = 42}}}) {}
const { cover = (function () {}), xCover = (0, function() {}) } = {};
@@ -129,6 +132,9 @@ destructuring_arguments_3: {
}
default_arguments: {
beautify = {
ecma: 6
}
input: {
function x(a = 6) { }
function x(a = (6 + 5)) { }
@@ -138,6 +144,9 @@ default_arguments: {
}
default_values_in_destructurings: {
beautify = {
ecma: 6
}
input: {
function x({a=(4), b}) {}
function x([b, c=(12)]) {}

View File

@@ -376,4 +376,4 @@ template_ending_with_newline: {
`;
};
} expect_exact: "function foo(e){return`this is a template string!\n`}"
}
}

View File

@@ -1,4 +1,7 @@
catch_destructuring_with_sequence: {
beautify = {
ecma: 6
}
input: {
try {
throw {};

View File

@@ -27,4 +27,253 @@ describe("arguments", function() {
assert.strictEqual(ast.body[0].body[0].uses_arguments, true);
assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false);
});
});
it("Should parse a function containing default assignment correctly", function() {
var ast = UglifyJS.parse("function foo(a = 123) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo(a = a) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_SymbolRef);
});
it("Should parse a function containing default assignments in destructuring correctly", function() {
var ast = UglifyJS.parse("function foo([a = 123]) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].operator, "=");
assert(ast.body[0].argnames[0].names[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a = 123}) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
// Property a of first argument
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a: a = 123}) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
// Content destructuring of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
// Property a of first argument
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
});
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
var ast = UglifyJS.parse("function foo([a, [b = 123]]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, true);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[1].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].operator, "=");
assert(ast.body[0].argnames[0].names[1].names[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo([a, {b: c = 123}]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, false);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].key, "b");
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
// Property b of second argument
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[1].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[1].names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a, b: {b = 123}}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[1].key, "b");
assert(ast.body[0].argnames[0].names[1].value instanceof UglifyJS.AST_Destructuring);
// Check content of nested destructuring in first parameter
var content = ast.body[0].argnames[0].names[1].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a: {b = 123}}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
// Check content of nested destructuring
content = ast.body[0].argnames[0].names[0].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
});
it("Should parse spread correctly", function() {
var ast = UglifyJS.parse("function foo(a, b, ...c){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 3);
// Check parameters
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[2] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[2].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo([a, b, ...c]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
// Check content first parameter
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[2].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo([a, b, [c, ...d]]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
// Check content outer destructuring array
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[2].is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].argnames[0].names[2].names.length, 2);
assert(ast.body[0].argnames[0].names[2].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2].names[1] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[2].names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo({a: [b, ...c]}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
// Check outer destructuring object
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].argnames[0].names[0].value.names.length, 2);
assert(ast.body[0].argnames[0].names[0].value.names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[0].value.names[1] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[0].value.names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
});
});

View File

@@ -80,4 +80,306 @@ describe("Arrow functions", function() {
assert.throws(test(tests[i]), error);
}
});
it("Should parse a function containing default assignment correctly", function() {
var ast = uglify.parse("var a = (a = 123) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = (a = a) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_SymbolRef);
});
it("Should parse a function containing default assignments in destructuring correctly", function() {
var ast = uglify.parse("var a = ([a = 123]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a = 123}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
// First object element in first argument
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a: a = 123}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
// Content destructuring of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectProperty);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
});
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
var ast = uglify.parse("var a = ([a, [b = 123]]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, true);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ([a, {b: c = 123}]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, false);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].key, "b");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a, b: {b = 123}}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// First argument, property 1
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_SymbolFunarg);
// First argument, property 2
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].key, "b");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].value instanceof uglify.AST_Destructuring);
// Check content of nested destructuring
var content = ast.body[0].definitions[0].value.argnames[0].names[1].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
// Content of first property in nested destructuring
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a: {b = 123}}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
// Check content of nested destructuring
content = ast.body[0].definitions[0].value.argnames[0].names[0].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
// Check first property of nested destructuring
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof uglify.AST_Number);
});
it("Should parse spread correctly", function() {
var ast = uglify.parse("var a = (a, b, ...c) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 3);
// Check parameters
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[2] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[2].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ([a, b, ...c]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
// Check content first parameter
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ([a, b, [c, ...d]]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
// Check content outer destructuring array
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].names.length, 2);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ({a: [b, ...c]}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
// Check outer destructuring object
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.names.length, 2);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1].expression instanceof uglify.AST_SymbolFunarg);
});
});

138
test/mocha/destructuring.js Normal file
View File

@@ -0,0 +1,138 @@
var assert = require("assert");
var uglify = require("../../");
describe("Destructuring", function() {
it("Should generate similar trees for destructuring in left hand side expressions, definitions, functions and arrow functions", function() {
var patterns = [
"[]",
"{}",
"[a, b, c]",
"{a: b, c: d}",
"{a}",
"{a, b}",
"{a: {}}",
"{a: []}",
"[{}]",
"[[]]",
"{a: {b}}",
// Can't do `a = 123` with lhs expression, so only test in destructuring
"[foo = bar]",
"{a = 123}",
"[{foo: abc = 123}]",
"{foo: [abc = 123]}",
"[...foo]",
"[...{}]",
"[...[]]"
// Can't do `...` because that is an invalid lhs expression, spread in array destructuring should be fine though
];
var types = [
{
name: "lhs",
symbolType: uglify.AST_SymbolRef,
tree: function (ast) {
return ast.body[0].body.left;
},
generate: function (code) {
return "(" + code + " = a)";
}
},
{
name: "var",
symbolType: uglify.AST_SymbolVar,
tree: function (ast) {
return ast.body[0].definitions[0].name;
},
generate: function (code) {
return "var " + code + " = a";
}
},
{
name: "function",
symbolType: uglify.AST_SymbolFunarg,
tree: function (ast) {
return ast.body[0].argnames[0];
},
generate: function (code) {
return "function a(" + code + ") {}";
}
},
{
name: "arrow",
symbolType: uglify.AST_SymbolFunarg,
tree: function (ast) {
return ast.body[0].definitions[0].value.argnames[0];
},
generate: function (code) {
return "var a = (" + code + ") => {}";
}
}
];
var walker = function(type, ref, code, result) {
var w = new uglify.TreeWalker(function(node) {
if (w.parent() instanceof uglify.AST_DefaultAssign &&
w.parent().right === node
) {
return true; // Don't check the content of the default assignment
} else if (node instanceof uglify.AST_Symbol) {
assert(node instanceof type.symbolType,
node.TYPE + " while " + type.symbolType.TYPE + " expected at pos " +
node.start.pos + " in `" + code + "` (" + ref + ")"
);
result.push([
new uglify.AST_Symbol({
start: node.start,
name: node.name,
end: node.end
}),
w.parent()
]);
return;
}
result.push([node, w.parent()]);
});
return w;
};
var getNodeType = function(node) {
return node[0].TYPE + (node[1] ? " " + node[1].TYPE : "");
}
for (var i = 0; i < patterns.length; i++) {
var results = [];
for (var j = 0; j < types.length; j++) {
var code = types[j].generate(patterns[i])
var ast = types[j].tree(
uglify.parse(code)
);
results.push([]);
ast.walk(walker(
types[j],
"`" + patterns[i] + "` on " + types[j].name,
code,
results[j]
));
if (j > 0) {
assert.deepEqual(
results[0].map(getNodeType),
results[j].map(getNodeType),
"AST disagree on " + patterns[i] + " with " + types[j].name
);
}
}
}
});
});

View File

@@ -47,64 +47,83 @@ describe("Function", function() {
assert.equal(destruct3.is_array, false);
assert.equal(destruct4.is_array, true);
var aAndB = [
['SymbolFunarg', 'a'],
['SymbolFunarg', 'b']
];
// destruct 1
assert.deepEqual(
[
destruct1.names[0].TYPE,
destruct1.names[0].name
destruct1.names[0].key,
destruct1.names[0].value.name
],
aAndB[0]);
['ObjectKeyVal', 'a', 'a']
);
assert.deepEqual(
[
destruct1.names[1].TYPE,
destruct1.names[1].name
destruct1.names[1].key,
destruct1.names[1].value.name
],
aAndB[1]);
['ObjectKeyVal', 'b', 'b']
);
// destruct 2
assert.deepEqual(
[
destruct2.names[0].TYPE,
destruct2.names[0].name
],
aAndB[0]);
['SymbolFunarg', 'a']
);
assert.deepEqual(
[
destruct2.names[1].names[0].TYPE,
destruct2.names[1].names[0].name
],
aAndB[1]);
['SymbolFunarg', 'b']
);
// destruct 3
assert.strictEqual(typeof destruct3.names[0].key, "string");
assert.strictEqual(destruct3.names[0].key, "a");
assert.strictEqual(destruct3.names[0].value.TYPE, "SymbolFunarg");
assert.strictEqual(destruct3.names[0].value.name, "a");
assert.strictEqual(typeof destruct3.names[1].key, "string");
assert.strictEqual(destruct3.names[1].key, "b");
assert.strictEqual(destruct3.names[1].value.TYPE, "SymbolFunarg");
assert.strictEqual(destruct3.names[1].value.name, "b");
// destruct 4
assert.deepEqual(
[
destruct4.names[0].TYPE,
destruct4.names[0].name
],
aAndB[0]);
['SymbolFunarg', 'a']
);
assert.strictEqual(destruct4.names[1].TYPE, "Destructuring");
assert.deepEqual(
[
destruct4.names[1].names[0].TYPE,
destruct4.names[1].names[0].name
],
aAndB[1]);
['SymbolFunarg', 'b']
);
assert.deepEqual(
get_args(destr_fun1.args_as_names()),
aAndB);
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun2.args_as_names()),
aAndB);
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun3.args_as_names()),
aAndB);
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun4.args_as_names()),
aAndB);
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
// Making sure we don't accidentally accept things which
// Aren't argument destructurings

View File

@@ -7,17 +7,26 @@ describe("Left-hand side expressions", function () {
assert.equal(decls.body[0].TYPE, 'Var');
assert.equal(decls.body[0].definitions.length, 2);
// Item 1
assert.equal(decls.body[0].definitions[0].name.TYPE, 'Destructuring');
assert.equal(decls.body[0].definitions[0].value.TYPE, 'SymbolRef');
// Item 2
assert.equal(decls.body[0].definitions[1].name.TYPE, 'Destructuring');
assert.equal(decls.body[0].definitions[1].value.TYPE, 'SymbolRef');
var nested_def = uglify.parse('var [{x}] = foo').body[0].definitions[0];
assert.equal(nested_def.name.names[0].names[0].TYPE, 'SymbolVar');
assert.equal(nested_def.name.names[0].names[0].name, 'x');
assert.equal(nested_def.name.names[0].names[0].TYPE, 'ObjectKeyVal');
assert.equal(nested_def.name.names[0].names[0].value.TYPE, 'SymbolVar');
assert.equal(nested_def.name.names[0].names[0].key, 'x');
assert.equal(nested_def.name.names[0].names[0].value.name, 'x');
var holey_def = uglify.parse('const [,,third] = [1,2,3]').body[0].definitions[0];
assert.equal(holey_def.name.names[0].TYPE, 'Hole');
assert.equal(holey_def.name.names[1].TYPE, 'Hole');
assert.equal(holey_def.name.names[2].TYPE, 'SymbolConst');
var expanding_def = uglify.parse('var [first, ...rest] = [1,2,3]').body[0].definitions[0];
@@ -26,4 +35,231 @@ describe("Left-hand side expressions", function () {
assert.equal(expanding_def.name.names[1].TYPE, 'Expansion');
assert.equal(expanding_def.name.names[1].expression.TYPE, 'SymbolVar');
});
it("Parser should use AST_Array for array literals", function() {
var ast = uglify.parse('["foo", "bar"]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Array);
ast = uglify.parse('a = ["foo"]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
});
it("Parser should use AST_Object for object literals", function() {
var ast = uglify.parse('({foo: "bar"})');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Object);
// This example should be fine though
ast = uglify.parse('a = {foo: "bar"}');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Object);
});
it("Parser should use AST_Destructuring for array assignment patterns", function() {
var ast = uglify.parse('[foo, bar] = [1, 2]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
});
it("Parser should use AST_Destructuring for object assignment patterns", function() {
var ast = uglify.parse('({a: b, b: c} = {b: "c", c: "d"})');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Object);
});
it("Parser should be able to handle nested destructuring", function() {
var ast = uglify.parse('[{a,b},[d, e, f, {g, h}]] = [{a: 1, b: 2}, [3, 4, 5, {g: 6, h: 7}]]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.names[0].is_array, false);
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.names[1].is_array, true);
assert(ast.body[0].body.left.names[1].names[3] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.names[1].names[3].is_array, false);
});
it("Should handle spread operator in destructuring", function() {
var ast = uglify.parse("[a, b, ...c] = [1, 2, 3, 4, 5]");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[2] instanceof uglify.AST_Expansion);
});
it("Should handle default assignments in destructuring", function() {
var ast = uglify.parse("({x: v, z = z + 5} = obj);");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[0].start.value, "x");
assert(ast.body[0].body.left.names[1].value instanceof uglify.AST_DefaultAssign);
assert.strictEqual(ast.body[0].body.left.names[1].start.value, "z");
assert(ast.body[0].body.left.names[1].value.left instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[1].value.operator, "=");
assert(ast.body[0].body.left.names[1].value.right instanceof uglify.AST_Binary);
ast = uglify.parse("({x = 123} = obj);");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_DefaultAssign);
assert.strictEqual(ast.body[0].body.left.names[0].value.operator, "=");
assert(ast.body[0].body.left.names[0].value.left instanceof uglify.AST_SymbolRef);
ast = uglify.parse("[x, y = 5] = foo");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[0].start.value, "x");
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].body.left.names[1].left instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[1].start.value, "y");
});
it("Should handle default assignments containing assignments in a destructuring", function() {
var ast = uglify.parse("[x, y = z = 2] = a;");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].body.left.names[1].left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.left.names[1].operator, "=");
assert(ast.body[0].body.left.names[1].right instanceof uglify.AST_Assign);
assert(ast.body[0].body.left.names[1].right.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.left.names[1].right.operator, "=");
assert(ast.body[0].body.left.names[1].right.right instanceof uglify.AST_Number);
ast = uglify.parse("({a: a = 123} = obj)");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_ObjectProperty);
assert.strictEqual(ast.body[0].body.left.names[0].key, "a");
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].body.left.names[0].value.left instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[0].value.operator, "=");
assert(ast.body[0].body.left.names[0].value.right instanceof uglify.AST_Number);
});
it("Should allow multiple spread in array literals", function() {
var ast = uglify.parse("var a = [1, 2, 3], b = [4, 5, 6], joined; joined = [...a, ...b]");
assert(ast.body[0] instanceof uglify.AST_Var);
assert(ast.body[1] instanceof uglify.AST_SimpleStatement);
// Check statement containing array with spreads
assert(ast.body[1].body instanceof uglify.AST_Assign);
assert(ast.body[1].body.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[1].body.operator, "=");
assert(ast.body[1].body.right instanceof uglify.AST_Array);
// Check array content
assert.strictEqual(ast.body[1].body.right.elements.length, 2);
assert(ast.body[1].body.right.elements[0] instanceof uglify.AST_Expansion);
assert(ast.body[1].body.right.elements[0].expression instanceof uglify.AST_SymbolRef);
assert(ast.body[1].body.right.elements[0].expression.name, "a");
assert(ast.body[1].body.right.elements[1] instanceof uglify.AST_Expansion);
assert(ast.body[1].body.right.elements[1].expression instanceof uglify.AST_SymbolRef);
assert(ast.body[1].body.right.elements[1].expression.name, "b");
});
it("Should not allow spread on invalid locations", function() {
var expect = function(input, expected) {
var execute = function(input) {
return function() {
uglify.parse(input);
}
}
var check = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === expected;
}
assert.throws(execute(input), check);
}
// Spreads are not allowed in destructuring array if it's not the last element
expect("[...a, ...b] = [1, 2, 3, 4]", "SyntaxError: Spread must the be last element in destructuring array");
// Multiple spreads are not allowed in destructuring array
expect("[...a, ...b] = [1, 2, 3, 4]", "SyntaxError: Spread must the be last element in destructuring array");
// Spread in obvious object pattern
expect("({...a} = foo)", "SyntaxError: Unexpected token: expand (...)");
// Spread in block should not be allowed
expect("{...a} = foo", "SyntaxError: Unexpected token: expand (...)");
// Not in standard yet
expect("let foo = {bar: 42}, bar; bar = {...foo}", "SyntaxError: Unexpected token: expand (...)");
});
});