aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/compress.js132
1 files changed, 78 insertions, 54 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 5c47e13d..6870fa6e 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -75,12 +75,26 @@ merge(Compressor.prototype, {
AST_Node.warn.apply(AST_Node, arguments);
},
before: function(node, descend, in_list) {
+ if (node._squeezed) return node;
node = node.clone();
if (node instanceof AST_Scope) {
+ node.drop_unused(this);
node = node.hoist_declarations(this);
}
descend(node, 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;
}
});
@@ -89,7 +103,12 @@ merge(Compressor.prototype, {
function OPT(node, optimizer) {
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) {
if (!props) props = {};
- if (!props.start) props.start = orig.start;
- if (!props.end) props.end = orig.end;
+ if (orig) {
+ if (!props.start) props.start = orig.start;
+ if (!props.end) props.end = orig.end;
+ }
return new ctor(props);
};
@@ -173,9 +194,6 @@ merge(Compressor.prototype, {
// step. nevertheless, it's good to check.
continue loop;
case stat instanceof AST_If:
- // compressor.warn("Current if: {code}", {
- // code: stat.condition.print_to_string()
- // });
if (stat.body instanceof AST_Return) {
//---
// pretty silly case, but:
@@ -186,7 +204,7 @@ merge(Compressor.prototype, {
CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
- }).optimize(compressor);
+ });
ret.unshift(cond);
continue loop;
}
@@ -323,7 +341,7 @@ merge(Compressor.prototype, {
} else {
left = AST_Seq.cons(left, right);
}
- return left.optimize(compressor);
+ return left.transform(compressor);
};
var ret = [], prev = null;
statements.forEach(function(stat){
@@ -478,7 +496,7 @@ merge(Compressor.prototype, {
case "string":
ast = make_node(AST_String, this, {
value: val
- });
+ }).optimize(compressor);
break;
case "number":
ast = make_node(isNaN(val) ? AST_NaN : AST_Number, this, {
@@ -493,7 +511,7 @@ merge(Compressor.prototype, {
break;
default:
if (val === null) {
- ast = make_node(AST_Null, this);
+ ast = make_node(AST_Null, this).optimize(compressor);
break;
}
throw new Error(string_template("Can't handle constant of type: {type}", {
@@ -838,19 +856,43 @@ merge(Compressor.prototype, {
col : def.name.start.col
};
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);
return true;
}
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
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);
}
- if (def.length != node.definitions.length) {
- node.definitions = def;
- return node;
+ if (def.length == 0) {
+ return side_effects;
+ }
+ node.definitions = def;
+ if (side_effects) {
+ side_effects.body.unshift(node);
+ node = side_effects;
}
+ return node;
}
if (node instanceof AST_Scope && node !== self)
return node;
@@ -864,7 +906,6 @@ merge(Compressor.prototype, {
var hoist_funs = compressor.option("hoist_funs");
var hoist_vars = compressor.option("hoist_vars");
var self = this;
- self.drop_unused(compressor);
if (hoist_funs || hoist_vars) {
var dirs = [];
var hoisted = [];
@@ -949,7 +990,7 @@ merge(Compressor.prototype, {
if (compressor.option("dead_code")) {
var 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 {
return self.body;
@@ -958,21 +999,6 @@ merge(Compressor.prototype, {
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){
var cond = self.condition;
if (cond) {
@@ -1030,13 +1056,13 @@ merge(Compressor.prototype, {
}
}
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;
if (self.alternative && negated_is_best) {
negated_is_best = false; // because we already do the switch here.
self.condition = negated;
var tmp = self.body;
- self.body = self.alternative || new AST_EmptyStatement();
+ self.body = self.alternative || make_node(AST_EmptyStatement);
self.alternative = tmp;
}
if (is_empty(self.body) && is_empty(self.alternative)) {
@@ -1051,7 +1077,7 @@ merge(Compressor.prototype, {
condition : self.condition,
consequent : self.body.body,
alternative : self.alternative.body
- }).optimize(compressor)
+ })
});
}
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
@@ -1060,14 +1086,14 @@ merge(Compressor.prototype, {
operator : "||",
left : negated,
right : self.body.body
- }).optimize(compressor)
+ })
});
return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, {
operator : "&&",
left : self.condition,
right : self.body.body
- }).optimize(compressor)
+ })
});
}
if (self.body instanceof AST_EmptyStatement
@@ -1078,7 +1104,7 @@ merge(Compressor.prototype, {
operator : "||",
left : self.condition,
right : self.alternative.body
- }).optimize(compressor)
+ })
});
}
if (self.body instanceof AST_Exit
@@ -1089,7 +1115,7 @@ merge(Compressor.prototype, {
condition : self.condition,
consequent : self.body.value,
alternative : self.alternative.value || make_node(AST_Undefined, self).optimize(compressor)
- }).optimize(compressor)
+ })
});
}
if (self.body instanceof AST_If
@@ -1099,7 +1125,7 @@ merge(Compressor.prototype, {
operator: "&&",
left: self.condition,
right: self.body.condition
- }).optimize(compressor);
+ });
self.body = self.body.body;
}
if (aborts(self.body)) {
@@ -1108,17 +1134,17 @@ merge(Compressor.prototype, {
self.alternative = null;
return make_node(AST_BlockStatement, self, {
body: [ self, alt ]
- }).optimize(compressor);
+ });
}
}
if (aborts(self.alternative)) {
var body = self.body;
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;
return make_node(AST_BlockStatement, self, {
body: [ self, body ]
- }).optimize(compressor);
+ });
}
return self;
});
@@ -1199,14 +1225,14 @@ merge(Compressor.prototype, {
if (self.args.length != 1) {
return make_node(AST_Array, self, {
elements: self.args
- }).optimize(compressor);
+ });
}
break;
case "Object":
if (self.args.length == 0) {
return make_node(AST_Object, self, {
properties: []
- }).optimize(compressor);
+ });
}
break;
case "String":
@@ -1238,7 +1264,7 @@ merge(Compressor.prototype, {
case "Function":
case "Error":
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){
if (compressor.option("cascade")) {
- if (self.cdr instanceof AST_Seq)
- self.cdr = self.cdr.optimize(compressor);
if (self.car instanceof AST_Assign
&& !self.car.left.has_side_effects()
&& self.car.left.equivalent_to(self.cdr)) {
@@ -1277,7 +1301,7 @@ merge(Compressor.prototype, {
// typeof always returns a non-empty string, thus it's
// always true in booleans
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) {
@@ -1327,7 +1351,7 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor), right = rr[0];
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
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]) {
return rr[0];
@@ -1341,7 +1365,7 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor), right = rr[0];
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
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]) {
return rr[0];
@@ -1356,7 +1380,7 @@ merge(Compressor.prototype, {
if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||
(rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {
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;
}
@@ -1390,9 +1414,9 @@ merge(Compressor.prototype, {
OPT(AST_SymbolRef, function(self, compressor){
if (self.undeclared()) switch (self.name) {
case "undefined":
- return make_node(AST_Undefined, self).optimize(compressor);
+ return make_node(AST_Undefined, self);
case "NaN":
- return make_node(AST_NaN, self).optimize(compressor);
+ return make_node(AST_NaN, self);
}
return self;
});
@@ -1433,7 +1457,7 @@ merge(Compressor.prototype, {
if (self.condition instanceof AST_Seq) {
var car = self.condition.car;
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);
if (cond.length > 1) {