diff --git a/lib/compress.js b/lib/compress.js index 0f4dd255..45da1e9e 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -48,6 +48,7 @@ function Compressor(options, false_by_default) { return new Compressor(options, false_by_default); TreeTransformer.call(this, this.before, this.after); this.options = defaults(options, { + assignments : false, booleans : !false_by_default, cascade : !false_by_default, collapse_vars : !false_by_default, @@ -484,9 +485,10 @@ merge(Compressor.prototype, { function is_modified(node, level, immutable) { var parent = tw.parent(level); - if (is_lhs(node, parent) - || !immutable && parent instanceof AST_Call && parent.expression === node) { + if (is_lhs(node, parent)) { return true; + } else if (parent instanceof AST_Call && parent.expression === node) { + return !immutable && level > 0; } else if (parent instanceof AST_PropAccess && parent.expression === node) { return !immutable && is_modified(parent, level + 1); } @@ -1144,7 +1146,68 @@ merge(Compressor.prototype, { return true; } })); - }; + } + + function assign_to_decl(compressor, expr) { + if (!(expr instanceof AST_Assign)) return; + var def; + if (expr.operator == "=" + && expr.left instanceof AST_SymbolRef + && (def = expr.left.definition()).scope === expr.left.scope + && (!def.global || compressor.option("toplevel")) + && all(def.orig, function(sym) { + return sym instanceof AST_SymbolVar; + })) { + def.references.splice(def.references.indexOf(expr.left), 1); + return [ make_node(AST_Var, expr, { + definitions: [ make_node(AST_VarDef, expr, { + name: make_node(AST_SymbolVar, expr.left, expr.left), + value: expr.right + }) ] + }) ]; + } + var body = assign_to_decl(compressor, expr.right); + if (body) { + if (body.length == 1) { + body.push(make_node(AST_SimpleStatement, expr, { + body: make_node(AST_Assign, expr, { + operator: expr.operator, + left: expr.left, + right: expr.right.left + }) + })); + } else { + body[1].body = make_node(AST_Assign, expr, { + operator: expr.operator, + left: expr.left, + right: body[1].body + }); + } + } + return body; + } + + function transform_assignments(compressor, expr) { + if (!compressor.option("assignments")) return; + if (expr instanceof AST_Sequence) { + var transformed = false; + var list = MAP(expr.expressions, function(e) { + var body = assign_to_decl(compressor, e); + if (body) { + transformed = true; + return MAP.splice(body); + } + return e; + }); + if (transformed) { + return list.map(function(e) { + return e instanceof AST_Statement ? e : make_node(AST_SimpleStatement, e, { + body: e + }); + }); + } + } else return assign_to_decl(compressor, expr); + } function is_undefined(node, compressor) { return node.is_undefined @@ -2372,8 +2435,8 @@ merge(Compressor.prototype, { }); OPT(AST_SimpleStatement, function(self, compressor){ + var body = self.body; if (compressor.option("side_effects")) { - var body = self.body; var node = body.drop_side_effect_free(compressor, true); if (!node) { compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start); @@ -2383,7 +2446,10 @@ merge(Compressor.prototype, { return make_node(AST_SimpleStatement, self, { body: node }); } } - return self; + body = transform_assignments(compressor, self.body); + return body ? make_node(AST_BlockStatement, self, { + body: body + }).optimize(compressor) : self; }); OPT(AST_DWLoop, function(self, compressor){ @@ -2486,6 +2552,17 @@ merge(Compressor.prototype, { } } if_break_in_loop(self, compressor); + if (self.init && !(self.init instanceof AST_Statement)) { + var body = transform_assignments(compressor, self.init); + if (body) { + var node = self.clone(); + node.init = null; + body.push(node); + return make_node(AST_BlockStatement, self, { + body: body + }).optimize(compressor); + } + } return self; }); diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index a4c1f9e6..d15a8259 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -505,7 +505,28 @@ collapse_vars_seq: { expect: { var f1 = function(x, y) { var a, b, r = x + y; - return a = r * r - r, b = 7, a + b + return a = r * r - r, b = 7, a + b; + }; + } +} + +collapse_vars_seq_assignments: { + options = { + collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, + comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, + keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, assignments:true + } + input: { + var f1 = function(x, y) { + var a, b, r = x + y, q = r * r, z = q - r; + a = z, b = 7; + return a + b; + }; + } + expect: { + var f1 = function(x, y) { + var r = x + y, b = 7; + return r * r - r + b; }; } } @@ -526,7 +547,28 @@ collapse_vars_throw: { expect: { var f1 = function(x, y) { var a, b, r = x + y; - throw a = r * r - r, b = 7, a + b + throw a = r * r - r, b = 7, a + b; + }; + } +} + +collapse_vars_throw_assignments: { + options = { + collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, + comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, + keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, assignments:true + } + input: { + var f1 = function(x, y) { + var a, b, r = x + y, q = r * r, z = q - r; + a = z, b = 7; + throw a + b; + }; + } + expect: { + var f1 = function(x,y) { + var r = x + y, b = 7; + throw r * r - r + b; }; } } @@ -1677,3 +1719,45 @@ var_defs: { } expect_stdout: "97" } + +assign_to_var: { + options = { + assignments: true, + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + var a; + a = x; + return a; + } + } + expect: { + function f() { + return x; + } + } +} + +issue_27: { + options = { + assignments: true, + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + (function(jQuery) { + var $; + $ = jQuery; + $("body").addClass("foo"); + })(jQuery); + } + expect: { + (function(jQuery) { + jQuery("body").addClass("foo"); + })(jQuery); + } +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 57e23891..622157c4 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2327,3 +2327,58 @@ iife_assign: { } expect_stdout: "1" } + +assign_to_var: { + options = { + assignments: true, + join_vars: true, + loops: true, + reduce_vars: true, + unused: true, + } + input: { + function f(b) { + var a; + for (a = x; b;) return a; + } + } + expect: { + function f(b) { + for (var a = x; b;) return a; + } + } +} + +issue_315: { + options = { + assignments: true, + evaluate: true, + join_vars: true, + keep_fargs: false, + loops: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(s) { + var w, _i, _len, _ref, _results; + _ref = s.trim().split(" "); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + w = _ref[_i]; + _results.push(w.toLowerCase()); + } + return _results; + }("test")); + } + expect: { + console.log(function() { + for (var _ref = "test".trim().split(" "), _results = [], _i = 0, _len = _ref.length; _i < _len; _i++) { + var w = _ref[_i]; + _results.push(w.toLowerCase()); + } + return _results; + }()); + } + expect_stdout: true +}