some speedup and more savings from unused vars that have side effects in initialization
This commit is contained in:
132
lib/compress.js
132
lib/compress.js
@@ -75,12 +75,26 @@ merge(Compressor.prototype, {
|
|||||||
AST_Node.warn.apply(AST_Node, arguments);
|
AST_Node.warn.apply(AST_Node, arguments);
|
||||||
},
|
},
|
||||||
before: function(node, descend, in_list) {
|
before: function(node, descend, in_list) {
|
||||||
|
if (node._squeezed) return node;
|
||||||
node = node.clone();
|
node = node.clone();
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
|
node.drop_unused(this);
|
||||||
node = node.hoist_declarations(this);
|
node = node.hoist_declarations(this);
|
||||||
}
|
}
|
||||||
descend(node, this);
|
descend(node, this);
|
||||||
node = node.optimize(this);
|
node = node.optimize(this);
|
||||||
|
if (node instanceof AST_Scope) {
|
||||||
|
// dead code removal might leave further unused declarations.
|
||||||
|
// this'll usually save very few bytes, but the performance
|
||||||
|
// hit seems negligible so I'll just drop it here.
|
||||||
|
|
||||||
|
// no point to repeat warnings.
|
||||||
|
var save_warnings = this.options.warnings;
|
||||||
|
this.options.warnings = false;
|
||||||
|
node.drop_unused(this);
|
||||||
|
this.options.warnings = save_warnings;
|
||||||
|
}
|
||||||
|
node._squeezed = true;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -89,7 +103,12 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function OPT(node, optimizer) {
|
function OPT(node, optimizer) {
|
||||||
node.DEFMETHOD("optimize", function(compressor){
|
node.DEFMETHOD("optimize", function(compressor){
|
||||||
return optimizer(this, compressor);
|
var self = this;
|
||||||
|
if (self._optimized) return self;
|
||||||
|
var opt = optimizer(self, compressor);
|
||||||
|
opt._optimized = true;
|
||||||
|
if (opt === self) return opt;
|
||||||
|
return opt.transform(compressor);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -104,8 +123,10 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function make_node(ctor, orig, props) {
|
function make_node(ctor, orig, props) {
|
||||||
if (!props) props = {};
|
if (!props) props = {};
|
||||||
if (!props.start) props.start = orig.start;
|
if (orig) {
|
||||||
if (!props.end) props.end = orig.end;
|
if (!props.start) props.start = orig.start;
|
||||||
|
if (!props.end) props.end = orig.end;
|
||||||
|
}
|
||||||
return new ctor(props);
|
return new ctor(props);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -173,9 +194,6 @@ merge(Compressor.prototype, {
|
|||||||
// step. nevertheless, it's good to check.
|
// step. nevertheless, it's good to check.
|
||||||
continue loop;
|
continue loop;
|
||||||
case stat instanceof AST_If:
|
case stat instanceof AST_If:
|
||||||
// compressor.warn("Current if: {code}", {
|
|
||||||
// code: stat.condition.print_to_string()
|
|
||||||
// });
|
|
||||||
if (stat.body instanceof AST_Return) {
|
if (stat.body instanceof AST_Return) {
|
||||||
//---
|
//---
|
||||||
// pretty silly case, but:
|
// pretty silly case, but:
|
||||||
@@ -186,7 +204,7 @@ merge(Compressor.prototype, {
|
|||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
var cond = make_node(AST_SimpleStatement, stat.condition, {
|
var cond = make_node(AST_SimpleStatement, stat.condition, {
|
||||||
body: stat.condition
|
body: stat.condition
|
||||||
}).optimize(compressor);
|
});
|
||||||
ret.unshift(cond);
|
ret.unshift(cond);
|
||||||
continue loop;
|
continue loop;
|
||||||
}
|
}
|
||||||
@@ -323,7 +341,7 @@ merge(Compressor.prototype, {
|
|||||||
} else {
|
} else {
|
||||||
left = AST_Seq.cons(left, right);
|
left = AST_Seq.cons(left, right);
|
||||||
}
|
}
|
||||||
return left.optimize(compressor);
|
return left.transform(compressor);
|
||||||
};
|
};
|
||||||
var ret = [], prev = null;
|
var ret = [], prev = null;
|
||||||
statements.forEach(function(stat){
|
statements.forEach(function(stat){
|
||||||
@@ -478,7 +496,7 @@ merge(Compressor.prototype, {
|
|||||||
case "string":
|
case "string":
|
||||||
ast = make_node(AST_String, this, {
|
ast = make_node(AST_String, this, {
|
||||||
value: val
|
value: val
|
||||||
});
|
}).optimize(compressor);
|
||||||
break;
|
break;
|
||||||
case "number":
|
case "number":
|
||||||
ast = make_node(isNaN(val) ? AST_NaN : AST_Number, this, {
|
ast = make_node(isNaN(val) ? AST_NaN : AST_Number, this, {
|
||||||
@@ -493,7 +511,7 @@ merge(Compressor.prototype, {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
ast = make_node(AST_Null, this);
|
ast = make_node(AST_Null, this).optimize(compressor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
throw new Error(string_template("Can't handle constant of type: {type}", {
|
throw new Error(string_template("Can't handle constant of type: {type}", {
|
||||||
@@ -838,19 +856,43 @@ merge(Compressor.prototype, {
|
|||||||
col : def.name.start.col
|
col : def.name.start.col
|
||||||
};
|
};
|
||||||
if (def.value && def.value.has_side_effects()) {
|
if (def.value && def.value.has_side_effects()) {
|
||||||
|
def._unused_side_effects = true;
|
||||||
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
|
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
if (def.length == 0) {
|
var side_effects = [];
|
||||||
|
def = mergeSort(def, function(a, b){
|
||||||
|
if (!a.value && b.value) return -1;
|
||||||
|
if (!b.value && a.value) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
while (def.length > 0 && def[def.length - 1]._unused_side_effects) {
|
||||||
|
side_effects.unshift(def.pop().value);
|
||||||
|
}
|
||||||
|
if (side_effects.length > 0) {
|
||||||
|
side_effects = make_node(AST_BlockStatement, node, {
|
||||||
|
body: side_effects.map(function(ss){
|
||||||
|
return make_node(AST_SimpleStatement, ss, { body: ss });
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
side_effects = null;
|
||||||
|
}
|
||||||
|
if (def.length == 0 && !side_effects) {
|
||||||
return make_node(AST_EmptyStatement, node);
|
return make_node(AST_EmptyStatement, node);
|
||||||
}
|
}
|
||||||
if (def.length != node.definitions.length) {
|
if (def.length == 0) {
|
||||||
node.definitions = def;
|
return side_effects;
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
node.definitions = def;
|
||||||
|
if (side_effects) {
|
||||||
|
side_effects.body.unshift(node);
|
||||||
|
node = side_effects;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope && node !== self)
|
if (node instanceof AST_Scope && node !== self)
|
||||||
return node;
|
return node;
|
||||||
@@ -864,7 +906,6 @@ merge(Compressor.prototype, {
|
|||||||
var hoist_funs = compressor.option("hoist_funs");
|
var hoist_funs = compressor.option("hoist_funs");
|
||||||
var hoist_vars = compressor.option("hoist_vars");
|
var hoist_vars = compressor.option("hoist_vars");
|
||||||
var self = this;
|
var self = this;
|
||||||
self.drop_unused(compressor);
|
|
||||||
if (hoist_funs || hoist_vars) {
|
if (hoist_funs || hoist_vars) {
|
||||||
var dirs = [];
|
var dirs = [];
|
||||||
var hoisted = [];
|
var hoisted = [];
|
||||||
@@ -949,7 +990,7 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("dead_code")) {
|
if (compressor.option("dead_code")) {
|
||||||
var a = [];
|
var a = [];
|
||||||
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
||||||
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
|
return make_node(AST_BlockStatement, self, { body: a });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return self.body;
|
return self.body;
|
||||||
@@ -958,21 +999,6 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
// while(cond){ ... } ==> for(;cond;){ ... }
|
|
||||||
//
|
|
||||||
// not helpful, it seems (output is a bit bigger after gzip)
|
|
||||||
//
|
|
||||||
// OPT(AST_While, function(self, compressor){
|
|
||||||
// var self = AST_DWLoop.prototype.optimize.call(self, compressor);
|
|
||||||
// if (self instanceof AST_While) {
|
|
||||||
// self = make_node(AST_For, self, {
|
|
||||||
// condition: self.condition,
|
|
||||||
// body: self.body
|
|
||||||
// }).optimize(compressor);
|
|
||||||
// }
|
|
||||||
// return self;
|
|
||||||
// });
|
|
||||||
|
|
||||||
OPT(AST_For, function(self, compressor){
|
OPT(AST_For, function(self, compressor){
|
||||||
var cond = self.condition;
|
var cond = self.condition;
|
||||||
if (cond) {
|
if (cond) {
|
||||||
@@ -1030,13 +1056,13 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_empty(self.alternative)) self.alternative = null;
|
if (is_empty(self.alternative)) self.alternative = null;
|
||||||
var negated = self.condition.negate(compressor).optimize(compressor);
|
var negated = self.condition.negate(compressor);
|
||||||
var negated_is_best = best_of(self.condition, negated) === negated;
|
var negated_is_best = best_of(self.condition, negated) === negated;
|
||||||
if (self.alternative && negated_is_best) {
|
if (self.alternative && negated_is_best) {
|
||||||
negated_is_best = false; // because we already do the switch here.
|
negated_is_best = false; // because we already do the switch here.
|
||||||
self.condition = negated;
|
self.condition = negated;
|
||||||
var tmp = self.body;
|
var tmp = self.body;
|
||||||
self.body = self.alternative || new AST_EmptyStatement();
|
self.body = self.alternative || make_node(AST_EmptyStatement);
|
||||||
self.alternative = tmp;
|
self.alternative = tmp;
|
||||||
}
|
}
|
||||||
if (is_empty(self.body) && is_empty(self.alternative)) {
|
if (is_empty(self.body) && is_empty(self.alternative)) {
|
||||||
@@ -1051,7 +1077,7 @@ merge(Compressor.prototype, {
|
|||||||
condition : self.condition,
|
condition : self.condition,
|
||||||
consequent : self.body.body,
|
consequent : self.body.body,
|
||||||
alternative : self.alternative.body
|
alternative : self.alternative.body
|
||||||
}).optimize(compressor)
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
|
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
|
||||||
@@ -1060,14 +1086,14 @@ merge(Compressor.prototype, {
|
|||||||
operator : "||",
|
operator : "||",
|
||||||
left : negated,
|
left : negated,
|
||||||
right : self.body.body
|
right : self.body.body
|
||||||
}).optimize(compressor)
|
})
|
||||||
});
|
});
|
||||||
return make_node(AST_SimpleStatement, self, {
|
return make_node(AST_SimpleStatement, self, {
|
||||||
body: make_node(AST_Binary, self, {
|
body: make_node(AST_Binary, self, {
|
||||||
operator : "&&",
|
operator : "&&",
|
||||||
left : self.condition,
|
left : self.condition,
|
||||||
right : self.body.body
|
right : self.body.body
|
||||||
}).optimize(compressor)
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (self.body instanceof AST_EmptyStatement
|
if (self.body instanceof AST_EmptyStatement
|
||||||
@@ -1078,7 +1104,7 @@ merge(Compressor.prototype, {
|
|||||||
operator : "||",
|
operator : "||",
|
||||||
left : self.condition,
|
left : self.condition,
|
||||||
right : self.alternative.body
|
right : self.alternative.body
|
||||||
}).optimize(compressor)
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (self.body instanceof AST_Exit
|
if (self.body instanceof AST_Exit
|
||||||
@@ -1089,7 +1115,7 @@ merge(Compressor.prototype, {
|
|||||||
condition : self.condition,
|
condition : self.condition,
|
||||||
consequent : self.body.value,
|
consequent : self.body.value,
|
||||||
alternative : self.alternative.value || make_node(AST_Undefined, self).optimize(compressor)
|
alternative : self.alternative.value || make_node(AST_Undefined, self).optimize(compressor)
|
||||||
}).optimize(compressor)
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (self.body instanceof AST_If
|
if (self.body instanceof AST_If
|
||||||
@@ -1099,7 +1125,7 @@ merge(Compressor.prototype, {
|
|||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: self.condition,
|
left: self.condition,
|
||||||
right: self.body.condition
|
right: self.body.condition
|
||||||
}).optimize(compressor);
|
});
|
||||||
self.body = self.body.body;
|
self.body = self.body.body;
|
||||||
}
|
}
|
||||||
if (aborts(self.body)) {
|
if (aborts(self.body)) {
|
||||||
@@ -1108,17 +1134,17 @@ merge(Compressor.prototype, {
|
|||||||
self.alternative = null;
|
self.alternative = null;
|
||||||
return make_node(AST_BlockStatement, self, {
|
return make_node(AST_BlockStatement, self, {
|
||||||
body: [ self, alt ]
|
body: [ self, alt ]
|
||||||
}).optimize(compressor);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (aborts(self.alternative)) {
|
if (aborts(self.alternative)) {
|
||||||
var body = self.body;
|
var body = self.body;
|
||||||
self.body = self.alternative;
|
self.body = self.alternative;
|
||||||
self.condition = negated_is_best ? negated : self.condition.negate(compressor).optimize(compressor);
|
self.condition = negated_is_best ? negated : self.condition.negate(compressor);
|
||||||
self.alternative = null;
|
self.alternative = null;
|
||||||
return make_node(AST_BlockStatement, self, {
|
return make_node(AST_BlockStatement, self, {
|
||||||
body: [ self, body ]
|
body: [ self, body ]
|
||||||
}).optimize(compressor);
|
});
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
@@ -1199,14 +1225,14 @@ merge(Compressor.prototype, {
|
|||||||
if (self.args.length != 1) {
|
if (self.args.length != 1) {
|
||||||
return make_node(AST_Array, self, {
|
return make_node(AST_Array, self, {
|
||||||
elements: self.args
|
elements: self.args
|
||||||
}).optimize(compressor);
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "Object":
|
case "Object":
|
||||||
if (self.args.length == 0) {
|
if (self.args.length == 0) {
|
||||||
return make_node(AST_Object, self, {
|
return make_node(AST_Object, self, {
|
||||||
properties: []
|
properties: []
|
||||||
}).optimize(compressor);
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "String":
|
case "String":
|
||||||
@@ -1238,7 +1264,7 @@ merge(Compressor.prototype, {
|
|||||||
case "Function":
|
case "Function":
|
||||||
case "Error":
|
case "Error":
|
||||||
case "Array":
|
case "Array":
|
||||||
return make_node(AST_Call, self, self).optimize(compressor);
|
return make_node(AST_Call, self, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1247,8 +1273,6 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_Seq, function(self, compressor){
|
OPT(AST_Seq, function(self, compressor){
|
||||||
if (compressor.option("cascade")) {
|
if (compressor.option("cascade")) {
|
||||||
if (self.cdr instanceof AST_Seq)
|
|
||||||
self.cdr = self.cdr.optimize(compressor);
|
|
||||||
if (self.car instanceof AST_Assign
|
if (self.car instanceof AST_Assign
|
||||||
&& !self.car.left.has_side_effects()
|
&& !self.car.left.has_side_effects()
|
||||||
&& self.car.left.equivalent_to(self.cdr)) {
|
&& self.car.left.equivalent_to(self.cdr)) {
|
||||||
@@ -1277,7 +1301,7 @@ merge(Compressor.prototype, {
|
|||||||
// typeof always returns a non-empty string, thus it's
|
// typeof always returns a non-empty string, thus it's
|
||||||
// always true in booleans
|
// always true in booleans
|
||||||
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
||||||
return make_node(AST_True, self).optimize(compressor);
|
return make_node(AST_True, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e instanceof AST_Binary) {
|
if (e instanceof AST_Binary) {
|
||||||
@@ -1327,7 +1351,7 @@ merge(Compressor.prototype, {
|
|||||||
var rr = self.right.evaluate(compressor), right = rr[0];
|
var rr = self.right.evaluate(compressor), right = rr[0];
|
||||||
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
|
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
|
||||||
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
|
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
|
||||||
return make_node(AST_False, self).optimize(compressor);
|
return make_node(AST_False, self);
|
||||||
}
|
}
|
||||||
if (ll.length > 1 && ll[1]) {
|
if (ll.length > 1 && ll[1]) {
|
||||||
return rr[0];
|
return rr[0];
|
||||||
@@ -1341,7 +1365,7 @@ merge(Compressor.prototype, {
|
|||||||
var rr = self.right.evaluate(compressor), right = rr[0];
|
var rr = self.right.evaluate(compressor), right = rr[0];
|
||||||
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
|
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
|
||||||
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
|
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
|
||||||
return make_node(AST_True, self).optimize(compressor);
|
return make_node(AST_True, self);
|
||||||
}
|
}
|
||||||
if (ll.length > 1 && !ll[1]) {
|
if (ll.length > 1 && !ll[1]) {
|
||||||
return rr[0];
|
return rr[0];
|
||||||
@@ -1356,7 +1380,7 @@ merge(Compressor.prototype, {
|
|||||||
if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||
|
if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||
|
||||||
(rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {
|
(rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {
|
||||||
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
|
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
|
||||||
return make_node(AST_True, self).optimize(compressor);
|
return make_node(AST_True, self);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1390,9 +1414,9 @@ merge(Compressor.prototype, {
|
|||||||
OPT(AST_SymbolRef, function(self, compressor){
|
OPT(AST_SymbolRef, function(self, compressor){
|
||||||
if (self.undeclared()) switch (self.name) {
|
if (self.undeclared()) switch (self.name) {
|
||||||
case "undefined":
|
case "undefined":
|
||||||
return make_node(AST_Undefined, self).optimize(compressor);
|
return make_node(AST_Undefined, self);
|
||||||
case "NaN":
|
case "NaN":
|
||||||
return make_node(AST_NaN, self).optimize(compressor);
|
return make_node(AST_NaN, self);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
@@ -1433,7 +1457,7 @@ merge(Compressor.prototype, {
|
|||||||
if (self.condition instanceof AST_Seq) {
|
if (self.condition instanceof AST_Seq) {
|
||||||
var car = self.condition.car;
|
var car = self.condition.car;
|
||||||
self.condition = self.condition.cdr;
|
self.condition = self.condition.cdr;
|
||||||
return AST_Seq.cons(car, self.optimize(compressor)).optimize(compressor);
|
return AST_Seq.cons(car, self);
|
||||||
}
|
}
|
||||||
var cond = self.condition.evaluate(compressor);
|
var cond = self.condition.evaluate(compressor);
|
||||||
if (cond.length > 1) {
|
if (cond.length > 1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user