support rest parameters (#4515)
This commit is contained in:
161
lib/compress.js
161
lib/compress.js
@@ -81,13 +81,14 @@ function Compressor(options, false_by_default) {
|
||||
objects : !false_by_default,
|
||||
passes : 1,
|
||||
properties : !false_by_default,
|
||||
pure_getters : !false_by_default && "strict",
|
||||
pure_funcs : null,
|
||||
pure_getters : !false_by_default && "strict",
|
||||
reduce_funcs : !false_by_default,
|
||||
reduce_vars : !false_by_default,
|
||||
rests : !false_by_default,
|
||||
sequences : !false_by_default,
|
||||
side_effects : !false_by_default,
|
||||
spread : !false_by_default,
|
||||
spreads : !false_by_default,
|
||||
strings : !false_by_default,
|
||||
switches : !false_by_default,
|
||||
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) {
|
||||
if (node instanceof AST_DefaultValue) {
|
||||
reset_flags(node);
|
||||
@@ -640,6 +641,15 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
@@ -670,6 +680,10 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
node.value.walk(scanner);
|
||||
});
|
||||
if (node.rest) {
|
||||
fixed = false;
|
||||
node.rest.walk(scanner);
|
||||
}
|
||||
fixed = save;
|
||||
return true;
|
||||
}
|
||||
@@ -719,12 +733,12 @@ merge(Compressor.prototype, {
|
||||
var safe = !fn.uses_arguments || tw.has_directive("use strict");
|
||||
fn.argnames.forEach(function(arg, i) {
|
||||
var value = iife.args[i];
|
||||
scan_declaration(tw, arg, function() {
|
||||
scan_declaration(tw, compressor, arg, function() {
|
||||
var j = fn.argnames.indexOf(arg);
|
||||
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
|
||||
}, function(node, fixed) {
|
||||
var d = node.definition();
|
||||
if (safe && d.fixed === undefined) {
|
||||
if (fixed && safe && d.fixed === undefined) {
|
||||
mark(tw, d);
|
||||
tw.loop_ids[d.id] = tw.in_loop;
|
||||
var value = iife.args[i];
|
||||
@@ -759,7 +773,7 @@ merge(Compressor.prototype, {
|
||||
return;
|
||||
} else if (node.operator == "=") {
|
||||
node.right.walk(tw);
|
||||
scan_declaration(tw, left, function() {
|
||||
scan_declaration(tw, compressor, left, function() {
|
||||
return node.right;
|
||||
}, function(sym, fixed, walk) {
|
||||
if (!(sym instanceof AST_SymbolRef)) {
|
||||
@@ -769,7 +783,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
var d = sym.definition();
|
||||
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);
|
||||
mark(tw, d);
|
||||
if (d.single_use && left instanceof AST_Destructured) d.single_use = false;
|
||||
@@ -1135,15 +1149,15 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
def(AST_VarDef, function(tw) {
|
||||
def(AST_VarDef, function(tw, descend, compressor) {
|
||||
var node = this;
|
||||
if (!node.value) return;
|
||||
node.value.walk(tw);
|
||||
scan_declaration(tw, node.name, function() {
|
||||
scan_declaration(tw, compressor, node.name, function() {
|
||||
return node.value;
|
||||
}, function(name, fixed) {
|
||||
var d = name.definition();
|
||||
if (safe_to_assign(tw, d, true)) {
|
||||
if (fixed && safe_to_assign(tw, d, true)) {
|
||||
mark(tw, d);
|
||||
tw.loop_ids[d.id] = tw.in_loop;
|
||||
d.fixed = fixed;
|
||||
@@ -5097,6 +5111,7 @@ merge(Compressor.prototype, {
|
||||
node.argnames.forEach(function(argname) {
|
||||
argname.mark_symbol(marker, scanner);
|
||||
});
|
||||
if (node.rest) node.rest.mark_symbol(marker, scanner);
|
||||
}
|
||||
if (node instanceof AST_Arrow && node.value) {
|
||||
node.value.walk(tw);
|
||||
@@ -5513,8 +5528,9 @@ merge(Compressor.prototype, {
|
||||
var fns_with_marked_args = [];
|
||||
var trimmer = new TreeTransformer(function(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) {
|
||||
var trim = true;
|
||||
var trim = !node.rest;
|
||||
for (var i = node.elements.length; --i >= 0;) {
|
||||
var element = node.elements[i].transform(trimmer);
|
||||
if (element) {
|
||||
@@ -5528,19 +5544,33 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_DestructuredKeyVal) {
|
||||
var retain = false;
|
||||
if (node.key instanceof AST_Node) {
|
||||
node.key = node.key.transform(tt);
|
||||
retain = node.key.has_side_effects(compressor);
|
||||
}
|
||||
if (retain && is_decl(node.value)) {
|
||||
node.value = node.value.transform(tt);
|
||||
} else {
|
||||
var value = node.value.transform(trimmer);
|
||||
if (!value) return List.skip;
|
||||
node.value = value;
|
||||
}
|
||||
if (node instanceof AST_DestructuredObject) {
|
||||
var properties = [];
|
||||
node.properties.forEach(function(prop) {
|
||||
var retain = false;
|
||||
if (prop.key instanceof AST_Node) {
|
||||
prop.key = prop.key.transform(tt);
|
||||
retain = prop.key.has_side_effects(compressor);
|
||||
}
|
||||
if ((retain || node.rest) && is_decl(prop.value)) {
|
||||
prop.value = prop.value.transform(tt);
|
||||
properties.push(prop);
|
||||
} 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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;) {
|
||||
var sym = a[i];
|
||||
if (!(sym instanceof AST_SymbolFunarg)) {
|
||||
@@ -5634,6 +5664,13 @@ merge(Compressor.prototype, {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -6149,8 +6186,22 @@ merge(Compressor.prototype, {
|
||||
element = element.transform(trimmer);
|
||||
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;
|
||||
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);
|
||||
node.elements = elements;
|
||||
return node;
|
||||
@@ -6181,19 +6232,36 @@ merge(Compressor.prototype, {
|
||||
value = values && values[prop.key];
|
||||
retain = false;
|
||||
}
|
||||
if (retain && is_decl(prop.value)) {
|
||||
if ((retain || node.rest) && is_decl(prop.value)) {
|
||||
prop.value = prop.value.transform(tt);
|
||||
properties.push(prop);
|
||||
} else {
|
||||
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) {
|
||||
prop.value = newValue;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
node.properties = properties;
|
||||
@@ -7704,16 +7772,26 @@ merge(Compressor.prototype, {
|
||||
if (!all(args, function(arg) {
|
||||
return !(arg instanceof AST_Spread);
|
||||
})) 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 is_iife = fn === exp && !fn.name;
|
||||
var drop_defaults = is_iife && compressor.option("default_values");
|
||||
var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
|
||||
if (!argname) return true;
|
||||
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) {
|
||||
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_false;
|
||||
@@ -8311,7 +8389,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
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 (!fn.variables.all(function(def) {
|
||||
return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
|
||||
@@ -8526,15 +8604,18 @@ merge(Compressor.prototype, {
|
||||
operator: "=",
|
||||
left: make_node(AST_DestructuredArray, self, {
|
||||
elements: fn.argnames.map(function(argname) {
|
||||
return argname.convert_symbol(AST_SymbolRef, function(ref, name) {
|
||||
var symbol = make_node(AST_SymbolVar, name, name);
|
||||
name.definition().orig.push(symbol);
|
||||
append_var(decls, expressions, symbol);
|
||||
});
|
||||
return argname.convert_symbol(AST_SymbolRef, process);
|
||||
}),
|
||||
rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
|
||||
}),
|
||||
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) {
|
||||
@@ -8568,7 +8649,7 @@ merge(Compressor.prototype, {
|
||||
function flatten_fn() {
|
||||
var decls = [];
|
||||
var expressions = [];
|
||||
if (has_default > 1 || has_destructured) {
|
||||
if (has_default > 1 || has_destructured || fn.rest) {
|
||||
flatten_destructured(decls, expressions);
|
||||
} else {
|
||||
flatten_args(decls, expressions);
|
||||
@@ -10308,7 +10389,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Spread, function(self, compressor) {
|
||||
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 node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
|
||||
}));
|
||||
@@ -10392,7 +10473,7 @@ merge(Compressor.prototype, {
|
||||
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) {
|
||||
argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
|
||||
fn.argnames.push(argname);
|
||||
@@ -10603,7 +10684,7 @@ merge(Compressor.prototype, {
|
||||
if (!(prop instanceof AST_Spread)) return process(prop);
|
||||
found = true;
|
||||
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);
|
||||
})) {
|
||||
changed = true;
|
||||
|
||||
Reference in New Issue
Block a user