support default values (#4442)
This commit is contained in:
258
lib/compress.js
258
lib/compress.js
@@ -56,6 +56,7 @@ function Compressor(options, false_by_default) {
|
||||
comparisons : !false_by_default,
|
||||
conditionals : !false_by_default,
|
||||
dead_code : !false_by_default,
|
||||
default_values : !false_by_default,
|
||||
directives : !false_by_default,
|
||||
drop_console : false,
|
||||
drop_debugger : !false_by_default,
|
||||
@@ -607,6 +608,20 @@ merge(Compressor.prototype, {
|
||||
|
||||
function scan_declaration(tw, lhs, fixed, visit) {
|
||||
var scanner = new TreeWalker(function(node) {
|
||||
if (node instanceof AST_DefaultValue) {
|
||||
reset_flags(node);
|
||||
push(tw);
|
||||
node.value.walk(tw);
|
||||
pop(tw);
|
||||
var save = fixed;
|
||||
fixed = function() {
|
||||
var value = save();
|
||||
return is_undefined(value) ? make_sequence(node, [ value, node.value ]) : node.name;
|
||||
};
|
||||
node.name.walk(scanner);
|
||||
fixed = save;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_DestructuredArray) {
|
||||
reset_flags(node);
|
||||
var save = fixed;
|
||||
@@ -1184,6 +1199,11 @@ merge(Compressor.prototype, {
|
||||
AST_Node.DEFMETHOD("convert_symbol", noop);
|
||||
AST_Destructured.DEFMETHOD("convert_symbol", function(type, process) {
|
||||
return this.transform(new TreeTransformer(function(node, descend) {
|
||||
if (node instanceof AST_DefaultValue) {
|
||||
node = node.clone();
|
||||
node.name = node.name.transform(this);
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_Destructured) {
|
||||
node = node.clone();
|
||||
descend(node, this);
|
||||
@@ -1205,8 +1225,13 @@ merge(Compressor.prototype, {
|
||||
AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
|
||||
AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
|
||||
|
||||
AST_Destructured.DEFMETHOD("mark_symbol", function(process, tw) {
|
||||
function mark_destructured(process, tw) {
|
||||
var marker = new TreeWalker(function(node) {
|
||||
if (node instanceof AST_DefaultValue) {
|
||||
node.value.walk(tw);
|
||||
node.name.walk(marker);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_DestructuredKeyVal) {
|
||||
if (node.key instanceof AST_Node) node.key.walk(tw);
|
||||
node.value.walk(marker);
|
||||
@@ -1215,7 +1240,9 @@ merge(Compressor.prototype, {
|
||||
return process(node);
|
||||
});
|
||||
this.walk(marker);
|
||||
});
|
||||
}
|
||||
AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
|
||||
AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
|
||||
function mark_symbol(process) {
|
||||
return process(this);
|
||||
}
|
||||
@@ -1229,6 +1256,10 @@ merge(Compressor.prototype, {
|
||||
var found = false;
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (found) return true;
|
||||
if (node instanceof AST_DefaultValue) {
|
||||
node.name.walk(tw);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_DestructuredKeyVal) {
|
||||
if (!allow_computed_keys && node.key instanceof AST_Node) return found = true;
|
||||
node.value.walk(tw);
|
||||
@@ -1658,7 +1689,7 @@ merge(Compressor.prototype, {
|
||||
var assign_used = false;
|
||||
var can_replace = !args || !hit;
|
||||
if (!can_replace) {
|
||||
for (var j = scope.argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
|
||||
for (var j = candidate.index + 1; !abort && j < args.length; j++) {
|
||||
args[j].transform(scanner);
|
||||
}
|
||||
can_replace = true;
|
||||
@@ -1895,9 +1926,14 @@ merge(Compressor.prototype, {
|
||||
for (var i = len; --i >= 0;) {
|
||||
var sym = fn.argnames[i];
|
||||
var arg = iife.args[i];
|
||||
var value;
|
||||
if (sym instanceof AST_DefaultValue) {
|
||||
value = sym.value;
|
||||
sym = sym.name;
|
||||
}
|
||||
args.unshift(make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
value: arg
|
||||
value: value ? arg ? make_sequence(iife, [ arg, value ]) : value : arg,
|
||||
}));
|
||||
if (sym instanceof AST_Destructured) {
|
||||
if (!sym.match_symbol(return_false)) continue;
|
||||
@@ -1906,17 +1942,21 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (sym.name in names) continue;
|
||||
names[sym.name] = true;
|
||||
if (!arg) {
|
||||
if (value) arg = !arg || is_undefined(arg) ? value : null;
|
||||
if (!arg && !value) {
|
||||
arg = make_node(AST_Undefined, sym).transform(compressor);
|
||||
} else if (arg instanceof AST_Lambda && arg.pinned()) {
|
||||
arg = null;
|
||||
} else {
|
||||
} else if (arg) {
|
||||
arg.walk(tw);
|
||||
}
|
||||
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
|
||||
if (!arg) continue;
|
||||
var candidate = make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
value: arg
|
||||
}) ]);
|
||||
});
|
||||
candidate.index = i;
|
||||
candidates.unshift([ candidate ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2310,14 +2350,22 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function remove_candidate(expr) {
|
||||
if (expr.name instanceof AST_SymbolFunarg) {
|
||||
var index = compressor.self().argnames.indexOf(expr.name);
|
||||
var args = compressor.parent().args;
|
||||
if (args[index]) {
|
||||
args[index] = make_node(AST_Number, args[index], {
|
||||
var index = expr.index;
|
||||
if (index >= 0) {
|
||||
var argname = scope.argnames[index];
|
||||
if (argname instanceof AST_DefaultValue) {
|
||||
argname.value = make_node(AST_Number, argname, {
|
||||
value: 0
|
||||
});
|
||||
expr.name.definition().fixed = false;
|
||||
argname.name.definition().fixed = false;
|
||||
} else {
|
||||
var args = compressor.parent().args;
|
||||
if (args[index]) {
|
||||
args[index] = make_node(AST_Number, args[index], {
|
||||
value: 0
|
||||
});
|
||||
argname.definition().fixed = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -3097,7 +3145,7 @@ merge(Compressor.prototype, {
|
||||
|| node instanceof AST_Undefined
|
||||
|| node instanceof AST_UnaryPrefix
|
||||
&& node.operator == "void"
|
||||
&& !node.expression.has_side_effects(compressor);
|
||||
&& !(compressor && node.expression.has_side_effects(compressor));
|
||||
}
|
||||
|
||||
// is_truthy()
|
||||
@@ -4077,10 +4125,18 @@ merge(Compressor.prototype, {
|
||||
if (fn.evaluating) return this;
|
||||
if (fn.name && fn.name.definition().recursive_refs > 0) return this;
|
||||
if (this.is_expr_pure(compressor)) return this;
|
||||
if (!all(fn.argnames, function(sym) {
|
||||
var args = eval_args(this.args);
|
||||
if (!all(fn.argnames, function(sym, index) {
|
||||
if (sym instanceof AST_DefaultValue) {
|
||||
if (!args) return false;
|
||||
if (args[index] !== undefined) return false;
|
||||
var value = sym.value._eval(compressor, ignore_side_effects, cached, depth);
|
||||
if (value === sym.value) return false;
|
||||
args[index] = value;
|
||||
sym = sym.name;
|
||||
}
|
||||
return !(sym instanceof AST_Destructured);
|
||||
})) return this;
|
||||
var args = eval_args(this.args);
|
||||
if (!args && !ignore_side_effects) return this;
|
||||
var stat = fn.first_statement();
|
||||
if (!(stat instanceof AST_Return)) {
|
||||
@@ -4104,9 +4160,10 @@ merge(Compressor.prototype, {
|
||||
if (!val) return;
|
||||
var cached_args = [];
|
||||
if (!args || all(fn.argnames, function(sym, i) {
|
||||
var value = args[i];
|
||||
if (sym instanceof AST_DefaultValue) sym = sym.name;
|
||||
var def = sym.definition();
|
||||
if (def.orig[def.orig.length - 1] !== sym) return false;
|
||||
var value = args[i];
|
||||
def.references.forEach(function(node) {
|
||||
node._eval = function() {
|
||||
return value;
|
||||
@@ -5340,32 +5397,35 @@ merge(Compressor.prototype, {
|
||||
var calls_to_drop_args = [];
|
||||
var fns_with_marked_args = [];
|
||||
var trimmer = new TreeTransformer(function(node) {
|
||||
if (node instanceof AST_DefaultValue) return trim_default(tt, trimmer, node);
|
||||
if (node instanceof AST_DestructuredArray) {
|
||||
var trim = true;
|
||||
for (var i = node.elements.length; --i >= 0;) {
|
||||
var sym = node.elements[i];
|
||||
if (!(sym instanceof AST_SymbolDeclaration)) {
|
||||
node.elements[i] = sym.transform(trimmer);
|
||||
trim = false;
|
||||
} else if (sym.definition().id in in_use_ids) {
|
||||
var element = node.elements[i].transform(trimmer);
|
||||
if (element) {
|
||||
node.elements[i] = element;
|
||||
trim = false;
|
||||
} else if (trim) {
|
||||
node.elements.pop();
|
||||
} else {
|
||||
node.elements[i] = make_node(AST_Hole, sym);
|
||||
node.elements[i] = make_node(AST_Hole, node.elements[i]);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_DestructuredKeyVal) {
|
||||
if (!(node.value instanceof AST_SymbolDeclaration)) {
|
||||
node.value = node.value.transform(trimmer);
|
||||
return node;
|
||||
var retain = false;
|
||||
if (node.key instanceof AST_Node) {
|
||||
node.key = node.key.transform(tt);
|
||||
retain = node.key.has_side_effects(compressor);
|
||||
}
|
||||
if (typeof node.key != "string") return node;
|
||||
if (node.value.definition().id in in_use_ids) return node;
|
||||
return List.skip;
|
||||
if (retain && is_decl(node.value)) return node;
|
||||
var value = node.value.transform(trimmer);
|
||||
if (!value) return List.skip;
|
||||
node.value = value;
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
|
||||
});
|
||||
var tt = new TreeTransformer(function(node, descend, in_list) {
|
||||
var parent = tt.parent();
|
||||
@@ -5432,21 +5492,28 @@ merge(Compressor.prototype, {
|
||||
var trim = compressor.drop_fargs(node, parent);
|
||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||
var sym = a[i];
|
||||
if (sym instanceof AST_Destructured) {
|
||||
sym.transform(trimmer);
|
||||
trim = false;
|
||||
if (!(sym instanceof AST_SymbolFunarg)) {
|
||||
var arg = sym.transform(trimmer);
|
||||
if (arg) {
|
||||
trim = false;
|
||||
} else if (trim) {
|
||||
log(sym.name, "Dropping unused function argument {name}");
|
||||
a.pop();
|
||||
} else {
|
||||
sym.name.__unused = true;
|
||||
a[i] = sym.name;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
var def = sym.definition();
|
||||
if (def.id in in_use_ids) {
|
||||
trim = false;
|
||||
if (indexOf_assign(def, sym) < 0) sym.__unused = null;
|
||||
} else if (trim) {
|
||||
log(sym, "Dropping unused function argument {name}");
|
||||
a.pop();
|
||||
} else {
|
||||
sym.__unused = true;
|
||||
if (trim) {
|
||||
log(sym, "Dropping unused function argument {name}");
|
||||
a.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
fns_with_marked_args.push(node);
|
||||
@@ -5469,6 +5536,7 @@ merge(Compressor.prototype, {
|
||||
if (def.name instanceof AST_Destructured) {
|
||||
var value = def.value;
|
||||
var trimmer = new TreeTransformer(function(node) {
|
||||
if (node instanceof AST_DefaultValue) return trim_default(tt, trimmer, node);
|
||||
if (node instanceof AST_DestructuredArray) {
|
||||
var save = value;
|
||||
if (value instanceof AST_SymbolRef) value = value.fixed_value();
|
||||
@@ -5514,7 +5582,7 @@ merge(Compressor.prototype, {
|
||||
value = values && values[prop.key];
|
||||
retain = false;
|
||||
}
|
||||
if (retain && prop.value instanceof AST_SymbolDeclaration) {
|
||||
if (retain && is_decl(prop.value)) {
|
||||
properties.push(prop);
|
||||
} else {
|
||||
var newValue = prop.value.transform(trimmer);
|
||||
@@ -5962,6 +6030,38 @@ merge(Compressor.prototype, {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function is_decl(node) {
|
||||
return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
|
||||
}
|
||||
|
||||
function trim_default(tt, trimmer, node) {
|
||||
node.value = node.value.transform(tt);
|
||||
var name = node.name.transform(trimmer);
|
||||
if (!name) {
|
||||
var value = node.value.drop_side_effect_free(compressor);
|
||||
if (!value) return null;
|
||||
name = node.name;
|
||||
if (name instanceof AST_Destructured) {
|
||||
name = name.clone();
|
||||
name[name instanceof AST_DestructuredArray ? "elements" : "properties"] = [];
|
||||
if (!(value instanceof AST_Array || value.is_string(compressor)
|
||||
|| name instanceof AST_DestructuredObject
|
||||
&& (value instanceof AST_Object
|
||||
|| value.is_boolean(compressor)
|
||||
|| value.is_number(compressor)))) {
|
||||
value = make_node(AST_Array, value, {
|
||||
elements: [ value ],
|
||||
});
|
||||
}
|
||||
node.name = name;
|
||||
} else {
|
||||
log(name, "Side effects in default value of unused variable {name}");
|
||||
}
|
||||
node.value = value;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
|
||||
@@ -6168,7 +6268,8 @@ merge(Compressor.prototype, {
|
||||
if (!(exp instanceof AST_Lambda)) return;
|
||||
if (exp.uses_arguments || exp.pinned()) return;
|
||||
var sym = exp.argnames[parent.args.indexOf(this)];
|
||||
if (sym && !all_bool(sym.definition(), bool_returns, compressor)) return;
|
||||
if (sym instanceof AST_DefaultValue) sym = sym.name;
|
||||
if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
|
||||
} else if (parent.TYPE == "Call") {
|
||||
compressor.pop();
|
||||
var in_bool = compressor.in_boolean_context();
|
||||
@@ -7447,6 +7548,11 @@ merge(Compressor.prototype, {
|
||||
var side_effects = [];
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var argname = fn.argnames[i];
|
||||
if (compressor.option("default_values")
|
||||
&& argname instanceof AST_DefaultValue
|
||||
&& args[i].is_defined(compressor)) {
|
||||
fn.argnames[i] = argname = argname.name;
|
||||
}
|
||||
if (!argname || "__unused" in argname) {
|
||||
var node = args[i].drop_side_effect_free(compressor);
|
||||
if (drop_fargs(argname)) {
|
||||
@@ -7779,22 +7885,31 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
||||
var is_func = fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function;
|
||||
var is_func = fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function;
|
||||
var stat = is_func && fn.first_statement();
|
||||
var can_inline = is_func
|
||||
&& compressor.option("inline")
|
||||
&& !self.is_expr_pure(compressor)
|
||||
&& all(fn.argnames, function(argname) {
|
||||
return !(argname instanceof AST_Destructured);
|
||||
})
|
||||
&& all(self.args, function(arg) {
|
||||
return !(arg instanceof AST_Spread);
|
||||
});
|
||||
var has_default = false;
|
||||
var can_drop = is_func && all(fn.argnames, function(argname, index) {
|
||||
if (argname instanceof AST_DefaultValue) {
|
||||
has_default = true;
|
||||
var arg = self.args[index];
|
||||
if (arg && !is_undefined(arg)) return false;
|
||||
var abort = false;
|
||||
argname.value.walk(new TreeWalker(function(node) {
|
||||
if (abort) return true;
|
||||
if (node instanceof AST_SymbolRef && fn.find_variable(node.name) === node.definition()) {
|
||||
return abort = true;
|
||||
}
|
||||
}));
|
||||
if (abort) return false;
|
||||
argname = argname.name;
|
||||
}
|
||||
return !(argname instanceof AST_Destructured);
|
||||
});
|
||||
var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
|
||||
if (can_inline && stat instanceof AST_Return) {
|
||||
var value = stat.value;
|
||||
if (exp === fn && (!value || value.is_constant_expression() && safe_from_await(value))) {
|
||||
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
return make_sequence(self, convert_args(value)).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (is_func) {
|
||||
@@ -7805,6 +7920,9 @@ merge(Compressor.prototype, {
|
||||
&& !(fn.name && fn instanceof AST_Function)
|
||||
&& (exp === fn || !recursive_ref(compressor, def = exp.definition())
|
||||
&& fn.is_constant_expression(find_scope(compressor)))
|
||||
&& all(self.args, function(arg) {
|
||||
return !(arg instanceof AST_Spread);
|
||||
})
|
||||
&& (value = can_flatten_body(stat))
|
||||
&& !fn.contains_this()) {
|
||||
var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
|
||||
@@ -7848,19 +7966,11 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
if (compressor.option("side_effects")
|
||||
&& can_drop
|
||||
&& all(fn.body, is_empty)
|
||||
&& (fn !== exp || fn_name_unused(fn, compressor))
|
||||
&& !(fn instanceof AST_Arrow && fn.value)
|
||||
&& all(fn.argnames, function(argname) {
|
||||
return !(argname instanceof AST_Destructured);
|
||||
})) {
|
||||
var args = self.args.map(function(arg) {
|
||||
return arg instanceof AST_Spread ? make_node(AST_Array, arg, {
|
||||
elements: [ arg ],
|
||||
}) : arg;
|
||||
});
|
||||
args.push(make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
&& !(fn instanceof AST_Arrow && fn.value)) {
|
||||
return make_sequence(self, convert_args()).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("drop_console")) {
|
||||
@@ -7881,6 +7991,19 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return try_evaluate(compressor, self);
|
||||
|
||||
function convert_args(value) {
|
||||
var args = self.args.map(function(arg) {
|
||||
return arg instanceof AST_Spread ? make_node(AST_Array, arg, {
|
||||
elements: [ arg ],
|
||||
}) : arg;
|
||||
});
|
||||
fn.argnames.forEach(function(argname, index) {
|
||||
if (argname instanceof AST_DefaultValue) args.push(argname.value);
|
||||
});
|
||||
args.push(value || make_node(AST_Undefined, self));
|
||||
return args;
|
||||
}
|
||||
|
||||
function safe_from_await(node) {
|
||||
if (!is_async(scope || compressor.find_parent(AST_Scope))) return true;
|
||||
var safe = true;
|
||||
@@ -7948,6 +8071,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function can_substitute_directly() {
|
||||
if (has_default) return;
|
||||
if (var_assigned) return;
|
||||
if (compressor.option("inline") < 2 && fn.argnames.length) return;
|
||||
if (!fn.variables.all(function(def) {
|
||||
@@ -8017,6 +8141,7 @@ merge(Compressor.prototype, {
|
||||
for (var i = 0; i < fn.argnames.length; i++) {
|
||||
var arg = fn.argnames[i];
|
||||
if (arg.__unused) continue;
|
||||
if (arg instanceof AST_DefaultValue) arg = arg.name;
|
||||
if (!safe_to_inject || var_exists(defined, arg.name)) return false;
|
||||
used[arg.name] = true;
|
||||
if (in_loop) in_loop.push(arg.definition());
|
||||
@@ -8115,6 +8240,10 @@ merge(Compressor.prototype, {
|
||||
for (i = len; --i >= 0;) {
|
||||
var name = fn.argnames[i];
|
||||
var value = self.args[i];
|
||||
if (name instanceof AST_DefaultValue) {
|
||||
value = value ? make_sequence(self, [ value, name.value ]) : name.value;
|
||||
name = name.name;
|
||||
}
|
||||
if (name.__unused || scope.var_names()[name.name]) {
|
||||
if (value) expressions.push(value);
|
||||
} else {
|
||||
@@ -8148,6 +8277,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
append_var(decls, expressions, name, var_def.value);
|
||||
if (in_loop && all(fn.argnames, function(argname) {
|
||||
if (argname instanceof AST_DefaultValue) argname = argname.name;
|
||||
return argname.name != name.name;
|
||||
})) {
|
||||
var def = fn.variables.get(name.name);
|
||||
@@ -9936,13 +10066,13 @@ merge(Compressor.prototype, {
|
||||
var argname = fn.argnames[index];
|
||||
if (def.deleted && def.deleted[index]) {
|
||||
argname = null;
|
||||
} else if (argname instanceof AST_Destructured) {
|
||||
} else if (argname && !(argname instanceof AST_SymbolFunarg)) {
|
||||
argname = null;
|
||||
} else if (argname && (compressor.has_directive("use strict")
|
||||
|| fn.name
|
||||
|| !(fn_parent instanceof AST_Call && index < fn_parent.args.length)
|
||||
|| !all(fn.argnames, function(argname) {
|
||||
return !(argname instanceof AST_Destructured);
|
||||
return argname instanceof AST_SymbolFunarg;
|
||||
}))) {
|
||||
var arg_def = argname.definition();
|
||||
if (!compressor.option("reduce_vars")
|
||||
|
||||
Reference in New Issue
Block a user