support rest parameters (#4515)
This commit is contained in:
26
README.md
26
README.md
@@ -627,7 +627,11 @@ to be `false` and all symbol names will be omitted.
|
|||||||
- `arguments` (default: `true`) -- replace `arguments[index]` with function
|
- `arguments` (default: `true`) -- replace `arguments[index]` with function
|
||||||
parameter name whenever possible.
|
parameter name whenever possible.
|
||||||
|
|
||||||
- `assignments` (default: `true`) -- apply optimizations to assignment expressions.
|
- `arrows` (default: `true`) -- apply optimizations to arrow functions
|
||||||
|
|
||||||
|
- `assignments` (default: `true`) -- apply optimizations to assignment expressions
|
||||||
|
|
||||||
|
- `awaits` (default: `true`) -- apply optimizations to `await` expressions
|
||||||
|
|
||||||
- `booleans` (default: `true`) -- various optimizations for boolean context,
|
- `booleans` (default: `true`) -- various optimizations for boolean context,
|
||||||
for example `!!a ? b : c → a ? b : c`
|
for example `!!a ? b : c → a ? b : c`
|
||||||
@@ -695,10 +699,6 @@ to be `false` and all symbol names will be omitted.
|
|||||||
when unsafe to do so, e.g. code which relies on `Function.prototype.length`.
|
when unsafe to do so, e.g. code which relies on `Function.prototype.length`.
|
||||||
Pass `true` to always retain function arguments.
|
Pass `true` to always retain function arguments.
|
||||||
|
|
||||||
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the
|
|
||||||
compressor from discarding function names. Useful for code relying on
|
|
||||||
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle-options).
|
|
||||||
|
|
||||||
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
|
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
|
||||||
being compressed into `1/0`, which may cause performance issues on Chrome.
|
being compressed into `1/0`, which may cause performance issues on Chrome.
|
||||||
|
|
||||||
@@ -747,6 +747,8 @@ to be `false` and all symbol names will be omitted.
|
|||||||
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
|
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
|
||||||
used as constant values.
|
used as constant values.
|
||||||
|
|
||||||
|
- `rests` (default: `true`) -- apply optimizations to rest parameters
|
||||||
|
|
||||||
- `sequences` (default: `true`) -- join consecutive simple statements using the
|
- `sequences` (default: `true`) -- join consecutive simple statements using the
|
||||||
comma operator. May be set to a positive integer to specify the maximum number
|
comma operator. May be set to a positive integer to specify the maximum number
|
||||||
of consecutive comma sequences that will be generated. If this option is set to
|
of consecutive comma sequences that will be generated. If this option is set to
|
||||||
@@ -761,20 +763,20 @@ to be `false` and all symbol names will be omitted.
|
|||||||
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
|
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
|
||||||
example: `/*@__PURE__*/foo();`
|
example: `/*@__PURE__*/foo();`
|
||||||
|
|
||||||
- `spread` (default: `true`) -- flatten spread expressions.
|
- `spreads` (default: `true`) -- flatten spread expressions.
|
||||||
|
|
||||||
- `strings` (default: `true`) -- compact string concatenations.
|
- `strings` (default: `true`) -- compact string concatenations.
|
||||||
|
|
||||||
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
||||||
|
|
||||||
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
|
|
||||||
variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
|
|
||||||
both unreferenced functions and variables)
|
|
||||||
|
|
||||||
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
|
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
|
||||||
variables from `unused` removal (can be array, comma-separated, RegExp or
|
variables from `unused` removal (can be array, comma-separated, RegExp or
|
||||||
function. Implies `toplevel`)
|
function. Implies `toplevel`)
|
||||||
|
|
||||||
|
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
|
||||||
|
variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
|
||||||
|
both unreferenced functions and variables)
|
||||||
|
|
||||||
- `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
|
- `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
|
||||||
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
|
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
|
||||||
earlier versions due to known issues.
|
earlier versions due to known issues.
|
||||||
@@ -811,10 +813,6 @@ to be `false` and all symbol names will be omitted.
|
|||||||
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
|
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
|
||||||
where `eval` or `with` are used.
|
where `eval` or `with` are used.
|
||||||
|
|
||||||
- `keep_fnames` (default `false`) -- Pass `true` to not mangle function names.
|
|
||||||
Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
|
|
||||||
[compress option](#compress-options).
|
|
||||||
|
|
||||||
- `reserved` (default `[]`) -- Pass an array of identifiers that should be
|
- `reserved` (default `[]`) -- Pass an array of identifiers that should be
|
||||||
excluded from mangling. Example: `["foo", "bar"]`.
|
excluded from mangling. Example: `["foo", "bar"]`.
|
||||||
|
|
||||||
|
|||||||
37
lib/ast.js
37
lib/ast.js
@@ -513,10 +513,11 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||||||
}
|
}
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
var AST_Lambda = DEFNODE("Lambda", "argnames length_read uses_arguments", {
|
var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", {
|
||||||
$documentation: "Base class for functions",
|
$documentation: "Base class for functions",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
argnames: "[(AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
|
argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
|
||||||
|
rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent",
|
||||||
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
|
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
|
||||||
},
|
},
|
||||||
each_argname: function(visit) {
|
each_argname: function(visit) {
|
||||||
@@ -534,6 +535,7 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read uses_arguments", {
|
|||||||
this.argnames.forEach(function(argname) {
|
this.argnames.forEach(function(argname) {
|
||||||
argname.walk(tw);
|
argname.walk(tw);
|
||||||
});
|
});
|
||||||
|
if (this.rest) this.rest.walk(tw);
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
@@ -542,6 +544,7 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read uses_arguments", {
|
|||||||
node.argnames.forEach(function(argname) {
|
node.argnames.forEach(function(argname) {
|
||||||
argname.walk(visitor);
|
argname.walk(visitor);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest.walk(visitor);
|
||||||
walk_body(node, visitor);
|
walk_body(node, visitor);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -551,6 +554,9 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read uses_arguments", {
|
|||||||
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
|
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
|
||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
|
if (this.rest != null) validate_destructured(this.rest, function(node) {
|
||||||
|
if (!(node instanceof AST_SymbolFunarg)) throw new Error("rest must be AST_SymbolFunarg");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
@@ -576,6 +582,7 @@ var AST_Arrow = DEFNODE("Arrow", "inlined value", {
|
|||||||
node.argnames.forEach(function(argname) {
|
node.argnames.forEach(function(argname) {
|
||||||
argname.walk(visitor);
|
argname.walk(visitor);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest.walk(visitor);
|
||||||
if (node.value) {
|
if (node.value) {
|
||||||
node.value.walk(visitor);
|
node.value.walk(visitor);
|
||||||
} else {
|
} else {
|
||||||
@@ -1155,25 +1162,31 @@ var AST_Array = DEFNODE("Array", "elements", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_Destructured = DEFNODE("Destructured", null, {
|
var AST_Destructured = DEFNODE("Destructured", "rest", {
|
||||||
$documentation: "Base class for destructured literal",
|
$documentation: "Base class for destructured literal",
|
||||||
|
$propdoc: {
|
||||||
|
rest: "[(AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)?] rest parameter, or null if absent",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function validate_destructured(node, check, allow_default) {
|
function validate_destructured(node, check, allow_default) {
|
||||||
if (node instanceof AST_DefaultValue && allow_default) return validate_destructured(node.name, check);
|
if (node instanceof AST_DefaultValue && allow_default) return validate_destructured(node.name, check);
|
||||||
if (node instanceof AST_DestructuredArray) return node.elements.forEach(function(node) {
|
if (node instanceof AST_Destructured) {
|
||||||
if (!(node instanceof AST_Hole)) validate_destructured(node, check, true);
|
if (node.rest != null) validate_destructured(node.rest, check);
|
||||||
});
|
if (node instanceof AST_DestructuredArray) return node.elements.forEach(function(node) {
|
||||||
if (node instanceof AST_DestructuredObject) return node.properties.forEach(function(prop) {
|
if (!(node instanceof AST_Hole)) validate_destructured(node, check, true);
|
||||||
validate_destructured(prop.value, check, true);
|
});
|
||||||
});
|
if (node instanceof AST_DestructuredObject) return node.properties.forEach(function(prop) {
|
||||||
|
validate_destructured(prop.value, check, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
check(node);
|
check(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
|
var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
|
||||||
$documentation: "A destructured array literal",
|
$documentation: "A destructured array literal",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
elements: "[AST_Node*] array of elements",
|
elements: "[(AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)*] array of elements",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
@@ -1181,6 +1194,7 @@ var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
|
|||||||
node.elements.forEach(function(element) {
|
node.elements.forEach(function(element) {
|
||||||
element.walk(visitor);
|
element.walk(visitor);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest.walk(visitor);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}, AST_Destructured);
|
}, AST_Destructured);
|
||||||
@@ -1189,7 +1203,7 @@ var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", {
|
|||||||
$documentation: "A key: value destructured property",
|
$documentation: "A key: value destructured property",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
|
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
|
||||||
value: "[AST_Node] property value",
|
value: "[AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef] property value",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
@@ -1218,6 +1232,7 @@ var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", {
|
|||||||
node.properties.forEach(function(prop) {
|
node.properties.forEach(function(prop) {
|
||||||
prop.walk(visitor);
|
prop.walk(visitor);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest.walk(visitor);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
|
|||||||
161
lib/compress.js
161
lib/compress.js
@@ -81,13 +81,14 @@ function Compressor(options, false_by_default) {
|
|||||||
objects : !false_by_default,
|
objects : !false_by_default,
|
||||||
passes : 1,
|
passes : 1,
|
||||||
properties : !false_by_default,
|
properties : !false_by_default,
|
||||||
pure_getters : !false_by_default && "strict",
|
|
||||||
pure_funcs : null,
|
pure_funcs : null,
|
||||||
|
pure_getters : !false_by_default && "strict",
|
||||||
reduce_funcs : !false_by_default,
|
reduce_funcs : !false_by_default,
|
||||||
reduce_vars : !false_by_default,
|
reduce_vars : !false_by_default,
|
||||||
|
rests : !false_by_default,
|
||||||
sequences : !false_by_default,
|
sequences : !false_by_default,
|
||||||
side_effects : !false_by_default,
|
side_effects : !false_by_default,
|
||||||
spread : !false_by_default,
|
spreads : !false_by_default,
|
||||||
strings : !false_by_default,
|
strings : !false_by_default,
|
||||||
switches : !false_by_default,
|
switches : !false_by_default,
|
||||||
top_retain : null,
|
top_retain : null,
|
||||||
@@ -609,7 +610,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function scan_declaration(tw, lhs, fixed, visit) {
|
function scan_declaration(tw, compressor, lhs, fixed, visit) {
|
||||||
var scanner = new TreeWalker(function(node) {
|
var scanner = new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_DefaultValue) {
|
if (node instanceof AST_DefaultValue) {
|
||||||
reset_flags(node);
|
reset_flags(node);
|
||||||
@@ -640,6 +641,15 @@ merge(Compressor.prototype, {
|
|||||||
};
|
};
|
||||||
node.walk(scanner);
|
node.walk(scanner);
|
||||||
});
|
});
|
||||||
|
if (node.rest) {
|
||||||
|
fixed = compressor.option("rests") && function() {
|
||||||
|
var value = save();
|
||||||
|
return value instanceof AST_Array ? make_node(AST_Array, node, {
|
||||||
|
elements: value.elements.slice(node.elements.length),
|
||||||
|
}) : node.rest;
|
||||||
|
};
|
||||||
|
node.rest.walk(scanner);
|
||||||
|
}
|
||||||
fixed = save;
|
fixed = save;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -670,6 +680,10 @@ merge(Compressor.prototype, {
|
|||||||
};
|
};
|
||||||
node.value.walk(scanner);
|
node.value.walk(scanner);
|
||||||
});
|
});
|
||||||
|
if (node.rest) {
|
||||||
|
fixed = false;
|
||||||
|
node.rest.walk(scanner);
|
||||||
|
}
|
||||||
fixed = save;
|
fixed = save;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -719,12 +733,12 @@ merge(Compressor.prototype, {
|
|||||||
var safe = !fn.uses_arguments || tw.has_directive("use strict");
|
var safe = !fn.uses_arguments || tw.has_directive("use strict");
|
||||||
fn.argnames.forEach(function(arg, i) {
|
fn.argnames.forEach(function(arg, i) {
|
||||||
var value = iife.args[i];
|
var value = iife.args[i];
|
||||||
scan_declaration(tw, arg, function() {
|
scan_declaration(tw, compressor, arg, function() {
|
||||||
var j = fn.argnames.indexOf(arg);
|
var j = fn.argnames.indexOf(arg);
|
||||||
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
|
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
|
||||||
}, function(node, fixed) {
|
}, function(node, fixed) {
|
||||||
var d = node.definition();
|
var d = node.definition();
|
||||||
if (safe && d.fixed === undefined) {
|
if (fixed && safe && d.fixed === undefined) {
|
||||||
mark(tw, d);
|
mark(tw, d);
|
||||||
tw.loop_ids[d.id] = tw.in_loop;
|
tw.loop_ids[d.id] = tw.in_loop;
|
||||||
var value = iife.args[i];
|
var value = iife.args[i];
|
||||||
@@ -759,7 +773,7 @@ merge(Compressor.prototype, {
|
|||||||
return;
|
return;
|
||||||
} else if (node.operator == "=") {
|
} else if (node.operator == "=") {
|
||||||
node.right.walk(tw);
|
node.right.walk(tw);
|
||||||
scan_declaration(tw, left, function() {
|
scan_declaration(tw, compressor, left, function() {
|
||||||
return node.right;
|
return node.right;
|
||||||
}, function(sym, fixed, walk) {
|
}, function(sym, fixed, walk) {
|
||||||
if (!(sym instanceof AST_SymbolRef)) {
|
if (!(sym instanceof AST_SymbolRef)) {
|
||||||
@@ -769,7 +783,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
var d = sym.definition();
|
var d = sym.definition();
|
||||||
d.assignments++;
|
d.assignments++;
|
||||||
if (!is_modified(compressor, tw, node, node.right, 0) && safe_to_assign(tw, d)) {
|
if (fixed && !is_modified(compressor, tw, node, node.right, 0) && safe_to_assign(tw, d)) {
|
||||||
push_ref(d, sym);
|
push_ref(d, sym);
|
||||||
mark(tw, d);
|
mark(tw, d);
|
||||||
if (d.single_use && left instanceof AST_Destructured) d.single_use = false;
|
if (d.single_use && left instanceof AST_Destructured) d.single_use = false;
|
||||||
@@ -1135,15 +1149,15 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
def(AST_VarDef, function(tw) {
|
def(AST_VarDef, function(tw, descend, compressor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
if (!node.value) return;
|
if (!node.value) return;
|
||||||
node.value.walk(tw);
|
node.value.walk(tw);
|
||||||
scan_declaration(tw, node.name, function() {
|
scan_declaration(tw, compressor, node.name, function() {
|
||||||
return node.value;
|
return node.value;
|
||||||
}, function(name, fixed) {
|
}, function(name, fixed) {
|
||||||
var d = name.definition();
|
var d = name.definition();
|
||||||
if (safe_to_assign(tw, d, true)) {
|
if (fixed && safe_to_assign(tw, d, true)) {
|
||||||
mark(tw, d);
|
mark(tw, d);
|
||||||
tw.loop_ids[d.id] = tw.in_loop;
|
tw.loop_ids[d.id] = tw.in_loop;
|
||||||
d.fixed = fixed;
|
d.fixed = fixed;
|
||||||
@@ -5097,6 +5111,7 @@ merge(Compressor.prototype, {
|
|||||||
node.argnames.forEach(function(argname) {
|
node.argnames.forEach(function(argname) {
|
||||||
argname.mark_symbol(marker, scanner);
|
argname.mark_symbol(marker, scanner);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest.mark_symbol(marker, scanner);
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Arrow && node.value) {
|
if (node instanceof AST_Arrow && node.value) {
|
||||||
node.value.walk(tw);
|
node.value.walk(tw);
|
||||||
@@ -5513,8 +5528,9 @@ merge(Compressor.prototype, {
|
|||||||
var fns_with_marked_args = [];
|
var fns_with_marked_args = [];
|
||||||
var trimmer = new TreeTransformer(function(node) {
|
var trimmer = new TreeTransformer(function(node) {
|
||||||
if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
|
if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
|
||||||
|
if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer);
|
||||||
if (node instanceof AST_DestructuredArray) {
|
if (node instanceof AST_DestructuredArray) {
|
||||||
var trim = true;
|
var trim = !node.rest;
|
||||||
for (var i = node.elements.length; --i >= 0;) {
|
for (var i = node.elements.length; --i >= 0;) {
|
||||||
var element = node.elements[i].transform(trimmer);
|
var element = node.elements[i].transform(trimmer);
|
||||||
if (element) {
|
if (element) {
|
||||||
@@ -5528,19 +5544,33 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_DestructuredKeyVal) {
|
if (node instanceof AST_DestructuredObject) {
|
||||||
var retain = false;
|
var properties = [];
|
||||||
if (node.key instanceof AST_Node) {
|
node.properties.forEach(function(prop) {
|
||||||
node.key = node.key.transform(tt);
|
var retain = false;
|
||||||
retain = node.key.has_side_effects(compressor);
|
if (prop.key instanceof AST_Node) {
|
||||||
}
|
prop.key = prop.key.transform(tt);
|
||||||
if (retain && is_decl(node.value)) {
|
retain = prop.key.has_side_effects(compressor);
|
||||||
node.value = node.value.transform(tt);
|
}
|
||||||
} else {
|
if ((retain || node.rest) && is_decl(prop.value)) {
|
||||||
var value = node.value.transform(trimmer);
|
prop.value = prop.value.transform(tt);
|
||||||
if (!value) return List.skip;
|
properties.push(prop);
|
||||||
node.value = value;
|
} else {
|
||||||
}
|
var value = prop.value.transform(trimmer);
|
||||||
|
if (!value && node.rest) {
|
||||||
|
if (prop.value instanceof AST_DestructuredArray) {
|
||||||
|
value = make_node(AST_DestructuredArray, prop.value, { elements: [] });
|
||||||
|
} else {
|
||||||
|
value = make_node(AST_DestructuredObject, prop.value, { properties: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
prop.value = value;
|
||||||
|
properties.push(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
node.properties = properties;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
|
if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
|
||||||
@@ -5607,7 +5637,7 @@ merge(Compressor.prototype, {
|
|||||||
unused_fn_names.push(node);
|
unused_fn_names.push(node);
|
||||||
}
|
}
|
||||||
if (!(node instanceof AST_Accessor)) {
|
if (!(node instanceof AST_Accessor)) {
|
||||||
var trim = compressor.drop_fargs(node, parent);
|
var trim = compressor.drop_fargs(node, parent) && !node.rest;
|
||||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||||
var sym = a[i];
|
var sym = a[i];
|
||||||
if (!(sym instanceof AST_SymbolFunarg)) {
|
if (!(sym instanceof AST_SymbolFunarg)) {
|
||||||
@@ -5634,6 +5664,13 @@ merge(Compressor.prototype, {
|
|||||||
sym.__unused = true;
|
sym.__unused = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (node.rest) {
|
||||||
|
node.rest = node.rest.transform(trimmer);
|
||||||
|
if (node.rest instanceof AST_DestructuredArray && node.rest.elements.length == 0
|
||||||
|
|| node.rest instanceof AST_DestructuredObject && node.rest.properties.length == 0) {
|
||||||
|
node.rest = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
fns_with_marked_args.push(node);
|
fns_with_marked_args.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6149,8 +6186,22 @@ merge(Compressor.prototype, {
|
|||||||
element = element.transform(trimmer);
|
element = element.transform(trimmer);
|
||||||
if (element) elements[index] = element;
|
if (element) elements[index] = element;
|
||||||
});
|
});
|
||||||
|
if (node.rest) {
|
||||||
|
if (compressor.option("rests")) {
|
||||||
|
value = values && make_node(AST_Array, save, {
|
||||||
|
elements: values.slice(node.elements.length),
|
||||||
|
});
|
||||||
|
node.rest = node.rest.transform(trimmer);
|
||||||
|
} else {
|
||||||
|
node.rest = node.rest.transform(tt);
|
||||||
|
}
|
||||||
|
}
|
||||||
value = save;
|
value = save;
|
||||||
if (values && elements.length == 0) return null;
|
if (node.rest) {
|
||||||
|
elements.length = node.elements.length;
|
||||||
|
} else if (values && elements.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
fill_holes(node, elements);
|
fill_holes(node, elements);
|
||||||
node.elements = elements;
|
node.elements = elements;
|
||||||
return node;
|
return node;
|
||||||
@@ -6181,19 +6232,36 @@ merge(Compressor.prototype, {
|
|||||||
value = values && values[prop.key];
|
value = values && values[prop.key];
|
||||||
retain = false;
|
retain = false;
|
||||||
}
|
}
|
||||||
if (retain && is_decl(prop.value)) {
|
if ((retain || node.rest) && is_decl(prop.value)) {
|
||||||
prop.value = prop.value.transform(tt);
|
prop.value = prop.value.transform(tt);
|
||||||
properties.push(prop);
|
properties.push(prop);
|
||||||
} else {
|
} else {
|
||||||
var newValue = prop.value.transform(trimmer);
|
var newValue = prop.value.transform(trimmer);
|
||||||
|
if (!newValue && node.rest) {
|
||||||
|
if (prop.value instanceof AST_DestructuredArray) {
|
||||||
|
newValue = make_node(AST_DestructuredArray, prop.value, { elements: [] });
|
||||||
|
} else {
|
||||||
|
newValue = make_node(AST_DestructuredObject, prop.value, { properties: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
prop.value = newValue;
|
prop.value = newValue;
|
||||||
properties.push(prop);
|
properties.push(prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (node.rest) {
|
||||||
|
if (compressor.option("rests")) {
|
||||||
|
value = values && make_node(AST_Object, save, {
|
||||||
|
properties: [],
|
||||||
|
});
|
||||||
|
node.rest = node.rest.transform(trimmer);
|
||||||
|
} else {
|
||||||
|
node.rest = node.rest.transform(tt);
|
||||||
|
}
|
||||||
|
}
|
||||||
value = save;
|
value = save;
|
||||||
if (properties.length == 0 && value && !value.may_throw_on_access(compressor)) {
|
if (properties.length == 0 && !node.rest && value && !value.may_throw_on_access(compressor)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
node.properties = properties;
|
node.properties = properties;
|
||||||
@@ -7704,16 +7772,26 @@ merge(Compressor.prototype, {
|
|||||||
if (!all(args, function(arg) {
|
if (!all(args, function(arg) {
|
||||||
return !(arg instanceof AST_Spread);
|
return !(arg instanceof AST_Spread);
|
||||||
})) return;
|
})) return;
|
||||||
|
if (fn.rest) {
|
||||||
|
if (!compressor.option("rests")) return;
|
||||||
|
var insert = fn.argnames.length;
|
||||||
|
for (var i = args.length; i < insert; i++) {
|
||||||
|
args[i] = make_node(AST_Undefined, call).optimize(compressor);
|
||||||
|
}
|
||||||
|
args[insert] = make_node(AST_Array, call, { elements: args.splice(insert) });
|
||||||
|
fn.argnames.push(fn.rest);
|
||||||
|
fn.rest = null;
|
||||||
|
}
|
||||||
var pos = 0, last = 0;
|
var pos = 0, last = 0;
|
||||||
var is_iife = fn === exp && !fn.name;
|
var is_iife = fn === exp && !fn.name;
|
||||||
var drop_defaults = is_iife && compressor.option("default_values");
|
var drop_defaults = is_iife && compressor.option("default_values");
|
||||||
var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
|
var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
|
||||||
if (!argname) return true;
|
if (!argname) return true;
|
||||||
if (argname instanceof AST_DestructuredArray) {
|
if (argname instanceof AST_DestructuredArray) {
|
||||||
return argname.elements.length == 0 && arg instanceof AST_Array;
|
return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
|
||||||
}
|
}
|
||||||
if (argname instanceof AST_DestructuredObject) {
|
if (argname instanceof AST_DestructuredObject) {
|
||||||
return argname.properties.length == 0 && arg && !arg.may_throw_on_access(compressor);
|
return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
|
||||||
}
|
}
|
||||||
return argname.__unused;
|
return argname.__unused;
|
||||||
} : return_false;
|
} : return_false;
|
||||||
@@ -8311,7 +8389,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function can_substitute_directly() {
|
function can_substitute_directly() {
|
||||||
if (has_default || has_destructured || var_assigned) return;
|
if (has_default || has_destructured || var_assigned || fn.rest) return;
|
||||||
if (compressor.option("inline") < 2 && fn.argnames.length) return;
|
if (compressor.option("inline") < 2 && fn.argnames.length) return;
|
||||||
if (!fn.variables.all(function(def) {
|
if (!fn.variables.all(function(def) {
|
||||||
return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
|
return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
|
||||||
@@ -8526,15 +8604,18 @@ merge(Compressor.prototype, {
|
|||||||
operator: "=",
|
operator: "=",
|
||||||
left: make_node(AST_DestructuredArray, self, {
|
left: make_node(AST_DestructuredArray, self, {
|
||||||
elements: fn.argnames.map(function(argname) {
|
elements: fn.argnames.map(function(argname) {
|
||||||
return argname.convert_symbol(AST_SymbolRef, function(ref, name) {
|
return argname.convert_symbol(AST_SymbolRef, process);
|
||||||
var symbol = make_node(AST_SymbolVar, name, name);
|
|
||||||
name.definition().orig.push(symbol);
|
|
||||||
append_var(decls, expressions, symbol);
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
|
rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
|
||||||
}),
|
}),
|
||||||
right: make_node(AST_Array, self, { elements: self.args.slice() }),
|
right: make_node(AST_Array, self, { elements: self.args.slice() }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
function process(ref, name) {
|
||||||
|
var symbol = make_node(AST_SymbolVar, name, name);
|
||||||
|
name.definition().orig.push(symbol);
|
||||||
|
append_var(decls, expressions, symbol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatten_vars(decls, expressions) {
|
function flatten_vars(decls, expressions) {
|
||||||
@@ -8568,7 +8649,7 @@ merge(Compressor.prototype, {
|
|||||||
function flatten_fn() {
|
function flatten_fn() {
|
||||||
var decls = [];
|
var decls = [];
|
||||||
var expressions = [];
|
var expressions = [];
|
||||||
if (has_default > 1 || has_destructured) {
|
if (has_default > 1 || has_destructured || fn.rest) {
|
||||||
flatten_destructured(decls, expressions);
|
flatten_destructured(decls, expressions);
|
||||||
} else {
|
} else {
|
||||||
flatten_args(decls, expressions);
|
flatten_args(decls, expressions);
|
||||||
@@ -10308,7 +10389,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_Spread, function(self, compressor) {
|
OPT(AST_Spread, function(self, compressor) {
|
||||||
var exp = self.expression;
|
var exp = self.expression;
|
||||||
if (compressor.option("spread") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
|
if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
|
||||||
return List.splice(exp.elements.map(function(node) {
|
return List.splice(exp.elements.map(function(node) {
|
||||||
return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
|
return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
|
||||||
}));
|
}));
|
||||||
@@ -10392,7 +10473,7 @@ merge(Compressor.prototype, {
|
|||||||
argname = null;
|
argname = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent)) {
|
} else if (index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent) && !fn.rest) {
|
||||||
while (index >= fn.argnames.length) {
|
while (index >= fn.argnames.length) {
|
||||||
argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
|
argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
|
||||||
fn.argnames.push(argname);
|
fn.argnames.push(argname);
|
||||||
@@ -10603,7 +10684,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!(prop instanceof AST_Spread)) return process(prop);
|
if (!(prop instanceof AST_Spread)) return process(prop);
|
||||||
found = true;
|
found = true;
|
||||||
var exp = prop.expression;
|
var exp = prop.expression;
|
||||||
if (compressor.option("spread") && exp instanceof AST_Object && all(exp.properties, function(prop) {
|
if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
|
||||||
return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
|
return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
|
||||||
})) {
|
})) {
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|||||||
@@ -992,16 +992,26 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ functions ]----- */
|
/* -----[ functions ]----- */
|
||||||
DEFPRINT(AST_Arrow, function(output) {
|
function print_funargs(self, output) {
|
||||||
var self = this;
|
output.with_parens(function() {
|
||||||
if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg) {
|
|
||||||
self.argnames[0].print(output);
|
|
||||||
} else output.with_parens(function() {
|
|
||||||
self.argnames.forEach(function(arg, i) {
|
self.argnames.forEach(function(arg, i) {
|
||||||
if (i) output.comma();
|
if (i) output.comma();
|
||||||
arg.print(output);
|
arg.print(output);
|
||||||
});
|
});
|
||||||
|
if (self.rest) {
|
||||||
|
if (self.argnames.length) output.comma();
|
||||||
|
output.print("...");
|
||||||
|
self.rest.print(output);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
DEFPRINT(AST_Arrow, function(output) {
|
||||||
|
var self = this;
|
||||||
|
if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg && !self.rest) {
|
||||||
|
self.argnames[0].print(output);
|
||||||
|
} else {
|
||||||
|
print_funargs(self, output);
|
||||||
|
}
|
||||||
output.space();
|
output.space();
|
||||||
output.print("=>");
|
output.print("=>");
|
||||||
output.space();
|
output.space();
|
||||||
@@ -1016,12 +1026,7 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
self.name.print(output);
|
self.name.print(output);
|
||||||
}
|
}
|
||||||
output.with_parens(function() {
|
print_funargs(self, output);
|
||||||
self.argnames.forEach(function(arg, i) {
|
|
||||||
if (i) output.comma();
|
|
||||||
arg.print(output);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
output.space();
|
output.space();
|
||||||
print_braced(self, output, true);
|
print_braced(self, output, true);
|
||||||
}
|
}
|
||||||
@@ -1355,25 +1360,30 @@ function OutputStream(options) {
|
|||||||
} : noop);
|
} : noop);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_DestructuredArray, function(output) {
|
DEFPRINT(AST_DestructuredArray, function(output) {
|
||||||
var a = this.elements, len = a.length;
|
var a = this.elements, len = a.length, rest = this.rest;
|
||||||
output.with_square(len > 0 ? function() {
|
output.with_square(len || rest ? function() {
|
||||||
output.space();
|
output.space();
|
||||||
a.forEach(function(exp, i) {
|
a.forEach(function(exp, i) {
|
||||||
if (i) output.comma();
|
if (i) output.comma();
|
||||||
exp.print(output);
|
exp.print(output);
|
||||||
|
});
|
||||||
|
if (rest) {
|
||||||
|
if (len) output.comma();
|
||||||
|
output.print("...");
|
||||||
|
rest.print(output);
|
||||||
|
} else if (a[len - 1] instanceof AST_Hole) {
|
||||||
// If the final element is a hole, we need to make sure it
|
// If the final element is a hole, we need to make sure it
|
||||||
// doesn't look like a trailing comma, by inserting an actual
|
// doesn't look like a trailing comma, by inserting an actual
|
||||||
// trailing comma.
|
// trailing comma.
|
||||||
if (i === len - 1 && exp instanceof AST_Hole)
|
output.comma();
|
||||||
output.comma();
|
}
|
||||||
});
|
|
||||||
output.space();
|
output.space();
|
||||||
} : noop);
|
} : noop);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_DestructuredKeyVal, print_key_value);
|
DEFPRINT(AST_DestructuredKeyVal, print_key_value);
|
||||||
DEFPRINT(AST_DestructuredObject, function(output) {
|
DEFPRINT(AST_DestructuredObject, function(output) {
|
||||||
var props = this.properties;
|
var props = this.properties, len = props.length, rest = this.rest;
|
||||||
if (props.length > 0) output.with_block(function() {
|
if (len || rest) output.with_block(function() {
|
||||||
props.forEach(function(prop, i) {
|
props.forEach(function(prop, i) {
|
||||||
if (i) {
|
if (i) {
|
||||||
output.print(",");
|
output.print(",");
|
||||||
@@ -1382,6 +1392,15 @@ function OutputStream(options) {
|
|||||||
output.indent();
|
output.indent();
|
||||||
prop.print(output);
|
prop.print(output);
|
||||||
});
|
});
|
||||||
|
if (rest) {
|
||||||
|
if (len) {
|
||||||
|
output.print(",");
|
||||||
|
output.newline();
|
||||||
|
}
|
||||||
|
output.indent();
|
||||||
|
output.print("...");
|
||||||
|
rest.print(output);
|
||||||
|
}
|
||||||
output.newline();
|
output.newline();
|
||||||
});
|
});
|
||||||
else print_braced_empty(this, output);
|
else print_braced_empty(this, output);
|
||||||
|
|||||||
109
lib/parse.js
109
lib/parse.js
@@ -1039,11 +1039,18 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function to_funarg(node) {
|
function to_funarg(node) {
|
||||||
if (node instanceof AST_Array) return new AST_DestructuredArray({
|
if (node instanceof AST_Array) {
|
||||||
start: node.start,
|
var rest = null;
|
||||||
elements: node.elements.map(to_funarg),
|
if (node.elements[node.elements.length - 1] instanceof AST_Spread) {
|
||||||
end: node.end,
|
rest = to_funarg(node.elements.pop().expression);
|
||||||
});
|
}
|
||||||
|
return new AST_DestructuredArray({
|
||||||
|
start: node.start,
|
||||||
|
elements: node.elements.map(to_funarg),
|
||||||
|
rest: rest,
|
||||||
|
end: node.end,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (node instanceof AST_Assign) return new AST_DefaultValue({
|
if (node instanceof AST_Assign) return new AST_DefaultValue({
|
||||||
start: node.start,
|
start: node.start,
|
||||||
name: to_funarg(node.left),
|
name: to_funarg(node.left),
|
||||||
@@ -1056,28 +1063,37 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_DestructuredArray) {
|
if (node instanceof AST_DestructuredArray) {
|
||||||
node.elements = node.elements.map(to_funarg);
|
node.elements = node.elements.map(to_funarg);
|
||||||
|
if (node.rest) node.rest = to_funarg(node.rest);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_DestructuredObject) {
|
if (node instanceof AST_DestructuredObject) {
|
||||||
node.properties.forEach(function(prop) {
|
node.properties.forEach(function(prop) {
|
||||||
prop.value = to_funarg(prop.value);
|
prop.value = to_funarg(prop.value);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest = to_funarg(node.rest);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Hole) return node;
|
if (node instanceof AST_Hole) return node;
|
||||||
if (node instanceof AST_Object) return new AST_DestructuredObject({
|
if (node instanceof AST_Object) {
|
||||||
start: node.start,
|
var rest = null;
|
||||||
properties: node.properties.map(function(prop) {
|
if (node.properties[node.properties.length - 1] instanceof AST_Spread) {
|
||||||
if (!(prop instanceof AST_ObjectKeyVal)) token_error(prop.start, "Invalid destructuring assignment");
|
rest = to_funarg(node.properties.pop().expression);
|
||||||
return new AST_DestructuredKeyVal({
|
}
|
||||||
start: prop.start,
|
return new AST_DestructuredObject({
|
||||||
key: prop.key,
|
start: node.start,
|
||||||
value: to_funarg(prop.value),
|
properties: node.properties.map(function(prop) {
|
||||||
end: prop.end,
|
if (!(prop instanceof AST_ObjectKeyVal)) token_error(prop.start, "Invalid destructuring assignment");
|
||||||
});
|
return new AST_DestructuredKeyVal({
|
||||||
}),
|
start: prop.start,
|
||||||
end: node.end,
|
key: prop.key,
|
||||||
});
|
value: to_funarg(prop.value),
|
||||||
|
end: prop.end,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
rest: rest,
|
||||||
|
end: node.end,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
|
if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
|
||||||
token_error(node.start, "Invalid arrow parameter");
|
token_error(node.start, "Invalid arrow parameter");
|
||||||
}
|
}
|
||||||
@@ -1116,6 +1132,7 @@ function parse($TEXT, options) {
|
|||||||
return new AST_Arrow({
|
return new AST_Arrow({
|
||||||
start: start,
|
start: start,
|
||||||
argnames: argnames,
|
argnames: argnames,
|
||||||
|
rest: exprs.rest || null,
|
||||||
body: body,
|
body: body,
|
||||||
value: value,
|
value: value,
|
||||||
end: prev(),
|
end: prev(),
|
||||||
@@ -1155,6 +1172,7 @@ function parse($TEXT, options) {
|
|||||||
if (S.input.has_directive("use strict")) {
|
if (S.input.has_directive("use strict")) {
|
||||||
if (name) strict_verify_symbol(name);
|
if (name) strict_verify_symbol(name);
|
||||||
argnames.forEach(strict_verify_symbol);
|
argnames.forEach(strict_verify_symbol);
|
||||||
|
if (argnames.rest) strict_verify_symbol(argnames.rest);
|
||||||
}
|
}
|
||||||
S.input.pop_directives_stack();
|
S.input.pop_directives_stack();
|
||||||
--S.in_function;
|
--S.in_function;
|
||||||
@@ -1164,6 +1182,7 @@ function parse($TEXT, options) {
|
|||||||
return new ctor({
|
return new ctor({
|
||||||
name: name,
|
name: name,
|
||||||
argnames: argnames,
|
argnames: argnames,
|
||||||
|
rest: argnames.rest || null,
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1445,17 +1464,22 @@ function parse($TEXT, options) {
|
|||||||
if (allow_trailing_comma && is("punc", closing)) break;
|
if (allow_trailing_comma && is("punc", closing)) break;
|
||||||
if (allow_empty && is("punc", ",")) {
|
if (allow_empty && is("punc", ",")) {
|
||||||
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
||||||
} else if (parser === maybe_assign && is("operator", "...")) {
|
} else if (!is("operator", "...")) {
|
||||||
|
a.push(parser());
|
||||||
|
} else if (parser === maybe_assign) {
|
||||||
a.push(new AST_Spread({
|
a.push(new AST_Spread({
|
||||||
start: S.token,
|
start: S.token,
|
||||||
expression: (next(), parser()),
|
expression: (next(), parser()),
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
a.push(parser());
|
next();
|
||||||
|
a.rest = parser();
|
||||||
|
if (a.rest instanceof AST_DefaultValue) token_error(a.rest.start, "Invalid rest parameter");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next();
|
expect(closing);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1634,17 +1658,19 @@ function parse($TEXT, options) {
|
|||||||
var start = S.token;
|
var start = S.token;
|
||||||
if (is("punc", "[")) {
|
if (is("punc", "[")) {
|
||||||
next();
|
next();
|
||||||
|
var elements = expr_list("]", !options.strict, true, function() {
|
||||||
|
return maybe_default(type);
|
||||||
|
});
|
||||||
return new AST_DestructuredArray({
|
return new AST_DestructuredArray({
|
||||||
start: start,
|
start: start,
|
||||||
elements: expr_list("]", !options.strict, true, function() {
|
elements: elements,
|
||||||
return maybe_default(type);
|
rest: elements.rest || null,
|
||||||
}),
|
|
||||||
end: prev(),
|
end: prev(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is("punc", "{")) {
|
if (is("punc", "{")) {
|
||||||
next();
|
next();
|
||||||
var first = true, a = [];
|
var first = true, a = [], rest = null;
|
||||||
while (!is("punc", "}")) {
|
while (!is("punc", "}")) {
|
||||||
if (first) first = false; else expect(",");
|
if (first) first = false; else expect(",");
|
||||||
// allow trailing comma
|
// allow trailing comma
|
||||||
@@ -1661,6 +1687,11 @@ function parse($TEXT, options) {
|
|||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (is("operator", "...")) {
|
||||||
|
next();
|
||||||
|
rest = maybe_destructured(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
var name = as_symbol(type);
|
var name = as_symbol(type);
|
||||||
if (is("operator", "=")) {
|
if (is("operator", "=")) {
|
||||||
next();
|
next();
|
||||||
@@ -1678,10 +1709,11 @@ function parse($TEXT, options) {
|
|||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
next();
|
expect("}");
|
||||||
return new AST_DestructuredObject({
|
return new AST_DestructuredObject({
|
||||||
start: start,
|
start: start,
|
||||||
properties: a,
|
properties: a,
|
||||||
|
rest: rest,
|
||||||
end: prev(),
|
end: prev(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1845,6 +1877,11 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function to_destructured(node) {
|
function to_destructured(node) {
|
||||||
if (node instanceof AST_Array) {
|
if (node instanceof AST_Array) {
|
||||||
|
var rest = null;
|
||||||
|
if (node.elements[node.elements.length - 1] instanceof AST_Spread) {
|
||||||
|
rest = to_destructured(node.elements.pop().expression);
|
||||||
|
if (!(rest instanceof AST_Destructured || is_assignable(rest))) return node;
|
||||||
|
}
|
||||||
var elements = node.elements.map(to_destructured);
|
var elements = node.elements.map(to_destructured);
|
||||||
return all(elements, function(node) {
|
return all(elements, function(node) {
|
||||||
return node instanceof AST_DefaultValue
|
return node instanceof AST_DefaultValue
|
||||||
@@ -1854,6 +1891,7 @@ function parse($TEXT, options) {
|
|||||||
}) ? new AST_DestructuredArray({
|
}) ? new AST_DestructuredArray({
|
||||||
start: node.start,
|
start: node.start,
|
||||||
elements: elements,
|
elements: elements,
|
||||||
|
rest: rest,
|
||||||
end: node.end,
|
end: node.end,
|
||||||
}) : node;
|
}) : node;
|
||||||
}
|
}
|
||||||
@@ -1867,6 +1905,11 @@ function parse($TEXT, options) {
|
|||||||
}) : node;
|
}) : node;
|
||||||
}
|
}
|
||||||
if (!(node instanceof AST_Object)) return node;
|
if (!(node instanceof AST_Object)) return node;
|
||||||
|
var rest = null;
|
||||||
|
if (node.properties[node.properties.length - 1] instanceof AST_Spread) {
|
||||||
|
rest = to_destructured(node.properties.pop().expression);
|
||||||
|
if (!(rest instanceof AST_Destructured || is_assignable(rest))) return node;
|
||||||
|
}
|
||||||
var props = [];
|
var props = [];
|
||||||
for (var i = 0; i < node.properties.length; i++) {
|
for (var i = 0; i < node.properties.length; i++) {
|
||||||
var prop = node.properties[i];
|
var prop = node.properties[i];
|
||||||
@@ -1885,6 +1928,7 @@ function parse($TEXT, options) {
|
|||||||
return new AST_DestructuredObject({
|
return new AST_DestructuredObject({
|
||||||
start: node.start,
|
start: node.start,
|
||||||
properties: props,
|
properties: props,
|
||||||
|
rest: rest,
|
||||||
end: node.end,
|
end: node.end,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1912,15 +1956,20 @@ function parse($TEXT, options) {
|
|||||||
var start = S.token;
|
var start = S.token;
|
||||||
var exprs = [];
|
var exprs = [];
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (maybe_arrow && is("operator", "...")) {
|
||||||
|
next();
|
||||||
|
exprs.rest = maybe_destructured(AST_SymbolFunarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
exprs.push(maybe_assign(no_in));
|
exprs.push(maybe_assign(no_in));
|
||||||
if (!is("punc", ",")) break;
|
if (!is("punc", ",")) break;
|
||||||
next();
|
next();
|
||||||
if (maybe_arrow && is("punc", ")") && is_token(peek(), "punc", "=>")) break;
|
if (maybe_arrow && is("punc", ")") && is_token(peek(), "punc", "=>")) break;
|
||||||
}
|
}
|
||||||
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
|
return exprs.length == 1 && !exprs.rest ? exprs[0] : new AST_Sequence({
|
||||||
start : start,
|
start: start,
|
||||||
expressions : exprs,
|
expressions: exprs,
|
||||||
end : prev()
|
end: prev(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
node.argnames.forEach(function(argname) {
|
node.argnames.forEach(function(argname) {
|
||||||
argname.walk(tw);
|
argname.walk(tw);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest.walk(tw);
|
||||||
walk_body(node, tw);
|
walk_body(node, tw);
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -220,6 +221,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
node.argnames.forEach(function(argname) {
|
node.argnames.forEach(function(argname) {
|
||||||
argname.walk(tw);
|
argname.walk(tw);
|
||||||
});
|
});
|
||||||
|
if (node.rest) node.rest.walk(tw);
|
||||||
in_arg.pop();
|
in_arg.pop();
|
||||||
if (node instanceof AST_Arrow && node.value) {
|
if (node instanceof AST_Arrow && node.value) {
|
||||||
node.value.walk(tw);
|
node.value.walk(tw);
|
||||||
|
|||||||
@@ -133,10 +133,12 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
DEF(AST_Lambda, function(self, tw) {
|
DEF(AST_Lambda, function(self, tw) {
|
||||||
if (self.name) self.name = self.name.transform(tw);
|
if (self.name) self.name = self.name.transform(tw);
|
||||||
self.argnames = do_list(self.argnames, tw);
|
self.argnames = do_list(self.argnames, tw);
|
||||||
|
if (self.rest) self.rest = self.rest.transform(tw);
|
||||||
self.body = do_list(self.body, tw);
|
self.body = do_list(self.body, tw);
|
||||||
});
|
});
|
||||||
DEF(AST_Arrow, function(self, tw) {
|
DEF(AST_Arrow, function(self, tw) {
|
||||||
self.argnames = do_list(self.argnames, tw);
|
self.argnames = do_list(self.argnames, tw);
|
||||||
|
if (self.rest) self.rest = self.rest.transform(tw);
|
||||||
if (self.value) {
|
if (self.value) {
|
||||||
self.value = self.value.transform(tw);
|
self.value = self.value.transform(tw);
|
||||||
} else {
|
} else {
|
||||||
@@ -180,6 +182,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
});
|
});
|
||||||
DEF(AST_DestructuredArray, function(self, tw) {
|
DEF(AST_DestructuredArray, function(self, tw) {
|
||||||
self.elements = do_list(self.elements, tw);
|
self.elements = do_list(self.elements, tw);
|
||||||
|
if (self.rest) self.rest = self.rest.transform(tw);
|
||||||
});
|
});
|
||||||
DEF(AST_DestructuredKeyVal, function(self, tw) {
|
DEF(AST_DestructuredKeyVal, function(self, tw) {
|
||||||
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
||||||
@@ -187,6 +190,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
});
|
});
|
||||||
DEF(AST_DestructuredObject, function(self, tw) {
|
DEF(AST_DestructuredObject, function(self, tw) {
|
||||||
self.properties = do_list(self.properties, tw);
|
self.properties = do_list(self.properties, tw);
|
||||||
|
if (self.rest) self.rest = self.rest.transform(tw);
|
||||||
});
|
});
|
||||||
DEF(AST_Object, function(self, tw) {
|
DEF(AST_Object, function(self, tw) {
|
||||||
self.properties = do_list(self.properties, tw);
|
self.properties = do_list(self.properties, tw);
|
||||||
|
|||||||
487
test/compress/rests.js
Normal file
487
test/compress/rests.js
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
arrow_1: {
|
||||||
|
input: {
|
||||||
|
console.log.apply(console, ((...a) => a)("PASS", 42));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log.apply(console,((...a)=>a)("PASS",42));'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_2: {
|
||||||
|
input: {
|
||||||
|
console.log.apply(console, ((a, ...b) => b)("FAIL", "PASS", 42));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log.apply(console,((a,...b)=>b)("FAIL","PASS",42));'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_destructured_array_1: {
|
||||||
|
input: {
|
||||||
|
console.log.apply(console, (([ ...a ]) => a)("PASS"));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log.apply(console,(([...a])=>a)("PASS"));'
|
||||||
|
expect_stdout: "P A S S"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_destructured_array_2: {
|
||||||
|
input: {
|
||||||
|
console.log.apply(console, (([ a, ...b ]) => b)([ "FAIL", "PASS", 42 ]));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log.apply(console,(([a,...b])=>b)(["FAIL","PASS",42]));'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_destructured_array_3: {
|
||||||
|
input: {
|
||||||
|
console.log((([ [ ...a ] = "FAIL" ]) => a)([ "PASS" ]).join("|"));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log((([[...a]="FAIL"])=>a)(["PASS"]).join("|"));'
|
||||||
|
expect_stdout: "P|A|S|S"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_destructured_object_1: {
|
||||||
|
input: {
|
||||||
|
var f = ({ ...a }) => a, o = f({ PASS: 42 });
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect_exact: "var f=({...a})=>a,o=f({PASS:42});for(var k in o)console.log(k,o[k]);"
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_destructured_object_2: {
|
||||||
|
input: {
|
||||||
|
var f = ({ FAIL: a, ...b }) => b, o = f({ PASS: 42, FAIL: null });
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect_exact: "var f=({FAIL:a,...b})=>b,o=f({PASS:42,FAIL:null});for(var k in o)console.log(k,o[k]);"
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_destructured_object_3: {
|
||||||
|
input: {
|
||||||
|
var f = ([ { ...a } = [ "FAIL" ] ]) => a;
|
||||||
|
var o = f([ "PASS" ]);
|
||||||
|
for (var k in o)
|
||||||
|
console.log(k, o[k]);
|
||||||
|
}
|
||||||
|
expect_exact: 'var f=([{...a}=["FAIL"]])=>a;var o=f(["PASS"]);for(var k in o)console.log(k,o[k]);'
|
||||||
|
expect_stdout: [
|
||||||
|
"0 P",
|
||||||
|
"1 A",
|
||||||
|
"2 S",
|
||||||
|
"3 S",
|
||||||
|
]
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
funarg_1: {
|
||||||
|
input: {
|
||||||
|
console.log.apply(console, function(...a) {
|
||||||
|
return a;
|
||||||
|
}("PASS", 42));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log.apply(console,function(...a){return a}("PASS",42));'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
funarg_2: {
|
||||||
|
input: {
|
||||||
|
console.log.apply(console, function(a, ...b) {
|
||||||
|
return b;
|
||||||
|
}("FAIL", "PASS", 42));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log.apply(console,function(a,...b){return b}("FAIL","PASS",42));'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
destructured_array_1: {
|
||||||
|
input: {
|
||||||
|
var [ ...a ] = [ "PASS", 42 ];
|
||||||
|
console.log.apply(console, a);
|
||||||
|
}
|
||||||
|
expect_exact: 'var[...a]=["PASS",42];console.log.apply(console,a);'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
destructured_array_2: {
|
||||||
|
input: {
|
||||||
|
var [ a, ...b ] = [ "FAIL", "PASS", 42 ];
|
||||||
|
console.log.apply(console, b);
|
||||||
|
}
|
||||||
|
expect_exact: 'var[a,...b]=["FAIL","PASS",42];console.log.apply(console,b);'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
destructured_object_1: {
|
||||||
|
input: {
|
||||||
|
var { ...a } = [ "FAIL", "PASS", 42 ];
|
||||||
|
console.log(a[1], a[2]);
|
||||||
|
}
|
||||||
|
expect_exact: 'var{...a}=["FAIL","PASS",42];console.log(a[1],a[2]);'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
destructured_object_2: {
|
||||||
|
input: {
|
||||||
|
var { 0: a, ...b } = [ "FAIL", "PASS", 42 ];
|
||||||
|
console.log(b[1], b[2]);
|
||||||
|
}
|
||||||
|
expect_exact: 'var{0:a,...b}=["FAIL","PASS",42];console.log(b[1],b[2]);'
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_fargs: {
|
||||||
|
options = {
|
||||||
|
keep_fargs: false,
|
||||||
|
rests: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a, ...b) {
|
||||||
|
return b[0];
|
||||||
|
}("FAIL", "PASS"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(b) {
|
||||||
|
return b[0];
|
||||||
|
}([ "PASS" ]));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
inline: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a, ...[ b, c ]) {
|
||||||
|
return c + b + a;
|
||||||
|
}("SS", "A", "P"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(([ a, ...[ b, c ] ] = [ "SS", "A", "P" ], c + b + a));
|
||||||
|
var a, b, c;
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_var: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var [ ...a ] = [ "PASS" ];
|
||||||
|
console.log(a[0]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var [ ...a ] = [ "PASS" ];
|
||||||
|
console.log(a[0]);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce_destructured_array: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
rests: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var [ ...a ] = [ "PASS" ];
|
||||||
|
console.log(a[0]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log([ "PASS" ][0]);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce_destructured_object: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var { ...a } = [ "PASS" ];
|
||||||
|
console.log(a[0]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var { ...a } = [ "PASS" ];
|
||||||
|
console.log(a[0]);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_destructured_array: {
|
||||||
|
options = {
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var [ a, ...b ] = [ "FAIL", "PASS", 42 ];
|
||||||
|
console.log.apply(console, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var [ , ...b ] = [ "FAIL", "PASS", 42 ];
|
||||||
|
console.log.apply(console, b);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS 42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_destructured_object_1: {
|
||||||
|
options = {
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var { 0: a, ...b } = [ "FAIL", "PASS", 42 ];
|
||||||
|
for (var k in b)
|
||||||
|
console.log(k, b[k]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var { 0: a, ...b } = [ "FAIL", "PASS", 42 ];
|
||||||
|
for (var k in b)
|
||||||
|
console.log(k, b[k]);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"1 PASS",
|
||||||
|
"2 42",
|
||||||
|
]
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_destructured_object_2: {
|
||||||
|
options = {
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var { foo: [ a ], ...b } = { foo: [ "FAIL" ], bar: "PASS", baz: 42 };
|
||||||
|
for (var k in b)
|
||||||
|
console.log(k, b[k]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var { foo: [], ...b } = { foo: [ "FAIL" ], bar: "PASS", baz: 42 };
|
||||||
|
for (var k in b)
|
||||||
|
console.log(k, b[k]);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"bar PASS",
|
||||||
|
"baz 42",
|
||||||
|
]
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_funarg_destructured_array_1: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
keep_fargs: false,
|
||||||
|
pure_getters: "strict",
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log((([ ...a ]) => a)([ "PASS" ])[0]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((([ ...a ]) => a)([ "PASS" ])[0]);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_funarg_destructured_array_2: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function([ a, ...b ]) {
|
||||||
|
return b;
|
||||||
|
}("bar")[1]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function([ , ...b ]) {
|
||||||
|
return b;
|
||||||
|
}("bar")[1]);
|
||||||
|
}
|
||||||
|
expect_stdout: "r"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_funarg_destructured_object_1: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
keep_fargs: false,
|
||||||
|
pure_getters: "strict",
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log((({ ...a }) => a)([ "PASS" ])[0]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((({ ...a }) => a)([ "PASS" ])[0]);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
retain_funarg_destructured_object_2: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function({ p: a, ... b }) {
|
||||||
|
return b;
|
||||||
|
}({ p: "FAIL" }).p || "PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function({ p: a, ... b }) {
|
||||||
|
return b;
|
||||||
|
}({ p: "FAIL" }).p || "PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_unused_call_args_1: {
|
||||||
|
options = {
|
||||||
|
rests: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function(...a) {
|
||||||
|
console.log(a[0]);
|
||||||
|
})(42, console.log("PASS"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(a) {
|
||||||
|
console.log(a[0]);
|
||||||
|
})([ 42, console.log("PASS") ]);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"PASS",
|
||||||
|
"42",
|
||||||
|
]
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_unused_call_args_2: {
|
||||||
|
options = {
|
||||||
|
keep_fargs: false,
|
||||||
|
rests: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a, ...b) {
|
||||||
|
return b;
|
||||||
|
}(console).length);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(b) {
|
||||||
|
return b;
|
||||||
|
}((console, [])).length);
|
||||||
|
}
|
||||||
|
expect_stdout: "0"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_funarg: {
|
||||||
|
options = {
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function(...a) {
|
||||||
|
var b = a.length;
|
||||||
|
console.log(b);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(...b) {
|
||||||
|
var b = b.length;
|
||||||
|
console.log(b);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "0"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_funarg_destructured_array: {
|
||||||
|
options = {
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function([ ...a ]) {
|
||||||
|
var b = a.length;
|
||||||
|
console.log(b);
|
||||||
|
})([]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function([ ...b ]) {
|
||||||
|
var b = b.length;
|
||||||
|
console.log(b);
|
||||||
|
})([]);
|
||||||
|
}
|
||||||
|
expect_stdout: "0"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_funarg_destructured_object: {
|
||||||
|
options = {
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function({ ...a }) {
|
||||||
|
var b = a[0];
|
||||||
|
console.log(b);
|
||||||
|
})([ "PASS" ]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function({ ...b }) {
|
||||||
|
var b = b[0];
|
||||||
|
console.log(b);
|
||||||
|
})([ "PASS" ]);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_arguments: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
keep_fargs: false,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function(...[ {} ]) {
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})("PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(...[ {} ]) {
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
@@ -128,7 +128,7 @@ dont_inline: {
|
|||||||
do_inline: {
|
do_inline: {
|
||||||
options = {
|
options = {
|
||||||
inline: true,
|
inline: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log(function(a) {
|
console.log(function(a) {
|
||||||
@@ -167,7 +167,7 @@ drop_empty_call_1: {
|
|||||||
drop_empty_call_2: {
|
drop_empty_call_2: {
|
||||||
options = {
|
options = {
|
||||||
side_effects: true,
|
side_effects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
(function() {})(...[ console.log("PASS") ]);
|
(function() {})(...[ console.log("PASS") ]);
|
||||||
@@ -181,7 +181,7 @@ drop_empty_call_2: {
|
|||||||
|
|
||||||
convert_hole: {
|
convert_hole: {
|
||||||
options = {
|
options = {
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log(...[ "PASS", , 42 ]);
|
console.log(...[ "PASS", , 42 ]);
|
||||||
@@ -275,7 +275,7 @@ reduce_vars_2: {
|
|||||||
convert_setter: {
|
convert_setter: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -419,7 +419,7 @@ keep_getter_4: {
|
|||||||
keep_accessor: {
|
keep_accessor: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -467,7 +467,7 @@ keep_accessor: {
|
|||||||
object_key_order_1: {
|
object_key_order_1: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -497,7 +497,7 @@ object_key_order_1: {
|
|||||||
object_key_order_2: {
|
object_key_order_2: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -527,7 +527,7 @@ object_key_order_2: {
|
|||||||
object_key_order_3: {
|
object_key_order_3: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -557,7 +557,7 @@ object_key_order_3: {
|
|||||||
object_key_order_4: {
|
object_key_order_4: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -587,7 +587,7 @@ object_key_order_4: {
|
|||||||
object_spread_array: {
|
object_spread_array: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -613,7 +613,7 @@ object_spread_array: {
|
|||||||
object_spread_string: {
|
object_spread_string: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -670,7 +670,7 @@ unused_var_side_effects: {
|
|||||||
issue_4329: {
|
issue_4329: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log({
|
console.log({
|
||||||
@@ -749,7 +749,7 @@ issue_4342: {
|
|||||||
issue_4345: {
|
issue_4345: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log({
|
console.log({
|
||||||
@@ -809,7 +809,7 @@ issue_4361: {
|
|||||||
issue_4363: {
|
issue_4363: {
|
||||||
options = {
|
options = {
|
||||||
objects: true,
|
objects: true,
|
||||||
spread: true,
|
spreads: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
({
|
({
|
||||||
@@ -452,6 +452,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
return List.skip;
|
return List.skip;
|
||||||
}
|
}
|
||||||
|
} else if (parent.rest === node) {
|
||||||
|
node.start._permute++;
|
||||||
|
CHANGED = true;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace this node
|
// replace this node
|
||||||
|
|||||||
@@ -140,6 +140,8 @@ var SUPPORT = function(matrix) {
|
|||||||
default_value: "[ a = 0 ] = [];",
|
default_value: "[ a = 0 ] = [];",
|
||||||
destructuring: "[] = [];",
|
destructuring: "[] = [];",
|
||||||
let: "let a;",
|
let: "let a;",
|
||||||
|
rest: "var [...a] = [];",
|
||||||
|
rest_object: "var {...a} = {};",
|
||||||
spread: "[...[]];",
|
spread: "[...[]];",
|
||||||
spread_object: "({...0});",
|
spread_object: "({...0});",
|
||||||
trailing_comma: "function f(a,) {}",
|
trailing_comma: "function f(a,) {}",
|
||||||
@@ -427,9 +429,22 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
|
|||||||
var avoid = [];
|
var avoid = [];
|
||||||
var len = unique_vars.length;
|
var len = unique_vars.length;
|
||||||
var pairs = createPairs(recurmax, !nameLenBefore);
|
var pairs = createPairs(recurmax, !nameLenBefore);
|
||||||
|
pairs.has_rest = nameLenBefore && convertToRest(pairs.names);
|
||||||
unique_vars.length = len;
|
unique_vars.length = len;
|
||||||
return pairs;
|
return pairs;
|
||||||
|
|
||||||
|
function convertToRest(names) {
|
||||||
|
var last = names.length - 1;
|
||||||
|
if (last >= 0 && SUPPORT.rest && rng(20) == 0) {
|
||||||
|
var name = names[last];
|
||||||
|
if (name && name.indexOf("=") < 0) {
|
||||||
|
if (/^[[{]/.test(name)) name = "[ " + name + " ]";
|
||||||
|
names[last] = "..." + name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fill(nameFn, valueFn) {
|
function fill(nameFn, valueFn) {
|
||||||
var save_async = async;
|
var save_async = async;
|
||||||
if (was_async != null) {
|
if (was_async != null) {
|
||||||
@@ -519,12 +534,13 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
|
|||||||
if (index < pairs.values.length) {
|
if (index < pairs.values.length) {
|
||||||
pairs.values.splice(index, 0, rng(2) ? createAssignmentValue(recurmax) : "");
|
pairs.values.splice(index, 0, rng(2) ? createAssignmentValue(recurmax) : "");
|
||||||
} else switch (rng(5)) {
|
} else switch (rng(5)) {
|
||||||
case 0:
|
case 0:
|
||||||
pairs.values[index] = createAssignmentValue(recurmax);
|
pairs.values[index] = createAssignmentValue(recurmax);
|
||||||
case 1:
|
case 1:
|
||||||
pairs.values.length = index + 1;
|
pairs.values.length = index + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
convertToRest(pairs.names);
|
||||||
names.unshift("[ " + pairs.names.join(", ") + " ]" + default_value);
|
names.unshift("[ " + pairs.names.join(", ") + " ]" + default_value);
|
||||||
values.unshift("[ " + pairs.values.join(", ") + " ]");
|
values.unshift("[ " + pairs.values.join(", ") + " ]");
|
||||||
});
|
});
|
||||||
@@ -547,10 +563,17 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
fill(function() {
|
fill(function() {
|
||||||
names.unshift("{ " + addTrailingComma(pairs.names.map(function(name, index) {
|
var last = pairs.names.length - 1, has_rest = false;
|
||||||
var key = index in keys ? keys[index] : rng(10) && createKey(recurmax, keys);
|
var s = pairs.names.map(function(name, index) {
|
||||||
return key ? key + ": " + name : name;
|
if (index in keys) return keys[index] + ": " + name;
|
||||||
}).join(", ")) + " }" + createDefaultValue(recurmax, noDefault));
|
if (index == last && SUPPORT.rest_object && rng(20) == 0 && name.indexOf("=") < 0) {
|
||||||
|
has_rest = true;
|
||||||
|
return "..." + name;
|
||||||
|
}
|
||||||
|
return rng(10) == 0 ? name : createKey(recurmax, keys) + ": " + name;
|
||||||
|
}).join(", ");
|
||||||
|
if (!has_rest) s = addTrailingComma(s);
|
||||||
|
names.unshift("{ " + s + " }" + createDefaultValue(recurmax, noDefault));
|
||||||
}, function() {
|
}, function() {
|
||||||
values.unshift("{ " + addTrailingComma(pairs.values.map(function(value, index) {
|
values.unshift("{ " + addTrailingComma(pairs.values.map(function(value, index) {
|
||||||
var key = index in keys ? keys[index] : createKey(recurmax, keys);
|
var key = index in keys ? keys[index] : createKey(recurmax, keys);
|
||||||
@@ -677,7 +700,8 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
|
if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
|
||||||
called[name] = false;
|
called[name] = false;
|
||||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||||
params = addTrailingComma(pairs.names.join(", "));
|
params = pairs.names.join(", ");
|
||||||
|
if (!pairs.has_rest) params = addTrailingComma(params);
|
||||||
args = addTrailingComma(pairs.values.join(", "));
|
args = addTrailingComma(pairs.values.join(", "));
|
||||||
} else {
|
} else {
|
||||||
params = createParams(save_async);
|
params = createParams(save_async);
|
||||||
@@ -1037,7 +1061,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
var params;
|
var params;
|
||||||
if (SUPPORT.destructuring && rng(2)) {
|
if (SUPPORT.destructuring && rng(2)) {
|
||||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||||
params = addTrailingComma(pairs.names.join(", "));
|
params = pairs.names.join(", ");
|
||||||
|
if (!pairs.has_rest) params = addTrailingComma(params);
|
||||||
args = addTrailingComma(pairs.values.join(", "));
|
args = addTrailingComma(pairs.values.join(", "));
|
||||||
} else {
|
} else {
|
||||||
params = createParams(save_async, NO_DUPLICATE);
|
params = createParams(save_async, NO_DUPLICATE);
|
||||||
|
|||||||
Reference in New Issue
Block a user