diff options
Diffstat (limited to 'lib/compress.js')
-rw-r--r-- | lib/compress.js | 260 |
1 files changed, 195 insertions, 65 deletions
diff --git a/lib/compress.js b/lib/compress.js index 13b7306b..23ce712d 100644 --- a/lib/compress.js +++ b/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; - } - if (typeof node.key != "string") return node; - if (node.value.definition().id in in_use_ids) return node; - return List.skip; + 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)) 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") |