diff --git a/lib/compress.js b/lib/compress.js index 32212688..20928db3 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4030,7 +4030,7 @@ merge(Compressor.prototype, { if (compressor.option("inline") && !fn.uses_arguments && !fn.uses_eval - && fn.body.length == 1 + && can_flatten_body() && (exp === fn ? !fn.name : compressor.option("unused") && (def = exp.definition()).references.length == 1 @@ -4038,11 +4038,9 @@ merge(Compressor.prototype, { && fn.is_constant_expression(exp.scope)) && !self.pure && !fn.contains_this() - && can_flatten_args(fn) - && (value = flatten_body(stat))) { - var expressions = flatten_args(fn); - expressions.push(value.clone(true)); - return make_sequence(self, expressions).optimize(compressor); + && can_inject_symbols() + && (value = return_value(stat))) { + return make_sequence(self, flatten_fn()).optimize(compressor); } if (compressor.option("side_effects") && all(fn.body, is_empty)) { var args = self.args.concat(make_node(AST_Undefined, self)); @@ -4072,7 +4070,64 @@ merge(Compressor.prototype, { } return self; - function can_flatten_args(fn) { + function can_flatten_body() { + var len = fn.body.length; + if (len == 1) return true; + stat = null; + for (var i = 0; i < len; i++) { + var line = fn.body[i]; + if (line instanceof AST_Definitions) { + if (stat && !all(line.definitions, function(var_def) { + return !var_def.value; + })) { + return false; + } + } else if (stat) { + return false; + } else { + stat = line; + } + } + return stat; + } + + function can_inject_args(catches, defs, safe_to_inject) { + for (var i = 0, len = fn.argnames.length; i < len; i++) { + var arg = fn.argnames[i]; + if (arg.__unused) continue; + if (!safe_to_inject + || catches[arg.name] + || identifier_atom(arg.name) + || scope.var_names()[arg.name]) { + return false; + } + if (in_loop) in_loop.push(arg.definition()); + } + return true; + } + + function can_inject_vars(catches, defs, safe_to_inject) { + var len = fn.body.length; + if (len == 1) return true; + if (!safe_to_inject) return false; + for (var i = 0; i < len; i++) { + var stat = fn.body[i]; + if (stat instanceof AST_Definitions) { + for (var j = stat.definitions.length; --j >= 0;) { + var name = stat.definitions[j].name; + if (catches[name.name] + || identifier_atom(name.name) + || scope.var_names()[name.name]) { + return false; + } + if (defs) defs.push(name.definition()); + } + } + } + return true; + } + + function can_inject_symbols() { var catches = Object.create(null); do { scope = compressor.parent(++level); @@ -4085,54 +4140,67 @@ merge(Compressor.prototype, { } } while (!(scope instanceof AST_Scope)); var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel); - for (var i = 0, len = fn.argnames.length; i < len; i++) { - var arg = fn.argnames[i]; - if (arg.__unused) continue; - if (!safe_to_inject - || catches[arg.name] - || identifier_atom(arg.name) - || scope.var_names()[arg.name]) { - return false; - } - if (in_loop) in_loop.push(arg.definition()); - } - return !in_loop || in_loop.length == 0 || !is_reachable(stat, in_loop); + if (!can_inject_vars(catches, in_loop, safe_to_inject)) return false; + if (!can_inject_args(catches, in_loop, safe_to_inject)) return false; + return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop); } - function flatten_args(fn) { - var decls = []; - var expressions = []; + function append_var(decls, expressions, name, value) { + var def = name.definition(); + scope.var_names()[name.name] = true; + scope.variables.set(name.name, def); + scope.enclosed.push(def); + decls.push(make_node(AST_VarDef, name, { + name: name, + value: null + })); + var sym = make_node(AST_SymbolRef, name, name); + def.references.push(sym); + if (value) expressions.push(make_node(AST_Assign, self, { + operator: "=", + left: sym, + right: value + })); + } + + function flatten_args(decls, expressions) { for (var len = fn.argnames.length, i = len; --i >= 0;) { var name = fn.argnames[i]; var value = self.args[i]; if (name.__unused || scope.var_names()[name.name]) { - if (value) { - expressions.unshift(value); - } + if (value) expressions.push(value); } else { - var def = name.definition(); - scope.var_names()[name.name] = true; - scope.variables.set(name.name, def); - scope.enclosed.push(def); var symbol = make_node(AST_SymbolVar, name, name); - def.orig.push(symbol); - decls.unshift(make_node(AST_VarDef, name, { - name: symbol, - value: null - })); - var sym = make_node(AST_SymbolRef, name, name); - def.references.push(sym); + name.definition().orig.push(symbol); if (!value && in_loop) value = make_node(AST_Undefined, self); - if (value) expressions.unshift(make_node(AST_Assign, self, { - operator: "=", - left: sym, - right: value - })); + append_var(decls, expressions, symbol, value); } } + decls.reverse(); + expressions.reverse(); for (i = len, len = self.args.length; i < len; i++) { expressions.push(self.args[i]); } + } + + function flatten_body(decls, expressions) { + for (i = 0, len = fn.body.length; i < len; i++) { + var stat = fn.body[i]; + if (stat instanceof AST_Definitions) { + stat.definitions.forEach(function(var_def) { + append_var(decls, expressions, var_def.name, var_def.value); + }); + } else { + expressions.push(value.clone(true)); + } + } + } + + function flatten_fn() { + var decls = []; + var expressions = []; + flatten_args(decls, expressions); + flatten_body(decls, expressions); if (decls.length) { i = scope.body.indexOf(compressor.parent(level - 1)) + 1; scope.body.splice(i, 0, make_node(AST_Var, fn, { @@ -4142,7 +4210,7 @@ merge(Compressor.prototype, { return expressions; } - function flatten_body(stat) { + function return_value(stat) { if (stat instanceof AST_Return) { return stat.value; } else if (stat instanceof AST_SimpleStatement) {