opportunistically turn assignment into declaration

fixes #27
fixes #315
This commit is contained in:
alexlamsl 2017-04-24 00:51:28 +08:00
parent 9bf72cf758
commit 65a7d92d9c
3 changed files with 223 additions and 7 deletions

View File

@ -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;
});

View File

@ -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);
}
}

View File

@ -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
}