aboutsummaryrefslogtreecommitdiff
path: root/lib/compress.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compress.js')
-rw-r--r--lib/compress.js375
1 files changed, 206 insertions, 169 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 1d9258cf..8c254573 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -111,7 +111,7 @@ function Compressor(options, false_by_default) {
};
}
var sequences = this.options["sequences"];
- this.sequences_limit = sequences == 1 ? 200 : sequences | 0;
+ this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
this.warnings_produced = {};
};
@@ -440,6 +440,13 @@ merge(Compressor.prototype, {
return new ctor(props);
};
+ function make_sequence(orig, expressions) {
+ if (expressions.length == 1) return expressions[0];
+ return make_node(AST_Sequence, orig, {
+ expressions: expressions
+ });
+ }
+
function make_node_from_constant(val, orig) {
switch (typeof val) {
case "string":
@@ -482,16 +489,19 @@ merge(Compressor.prototype, {
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|| parent instanceof AST_Call && parent.expression === orig
&& (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
- return make_node(AST_Seq, orig, {
- car: make_node(AST_Number, orig, {
- value: 0
- }),
- cdr: val
- });
+ return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
}
return val;
}
+ function merge_sequence(array, node) {
+ if (node instanceof AST_Sequence) {
+ array.push.apply(array, node.expressions);
+ } else {
+ array.push(node);
+ }
+ }
+
function as_statement_array(thing) {
if (thing === null) return [];
if (thing instanceof AST_BlockStatement) return thing.body;
@@ -1000,18 +1010,17 @@ merge(Compressor.prototype, {
if (statements.length < 2) return statements;
var seq = [], ret = [];
function push_seq() {
- seq = AST_Seq.from_array(seq);
- if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
- body: seq
- }));
+ if (!seq.length) return;
+ var body = make_sequence(seq[0], seq);
+ ret.push(make_node(AST_SimpleStatement, body, { body: body }));
seq = [];
};
statements.forEach(function(stat){
if (stat instanceof AST_SimpleStatement) {
- if (seqLength(seq) >= compressor.sequences_limit) push_seq();
+ if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
if (seq.length > 0) body = body.drop_side_effect_free(compressor);
- if (body) seq.push(body);
+ if (body) merge_sequence(seq, body);
} else {
push_seq();
ret.push(stat);
@@ -1023,27 +1032,16 @@ merge(Compressor.prototype, {
return ret;
};
- function seqLength(a) {
- for (var len = 0, i = 0; i < a.length; ++i) {
- var stat = a[i];
- if (stat instanceof AST_Seq) {
- len += stat.len();
- } else {
- len++;
- }
- }
- return len;
- };
-
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
ret.pop();
var left = prev.body;
- if (left instanceof AST_Seq) {
- left.add(right);
- } else {
- left = AST_Seq.cons(left, right);
+ if (!(left instanceof AST_Sequence)) {
+ left = make_node(AST_Sequence, left, {
+ expressions: [ left ]
+ });
}
+ merge_sequence(left.expressions, right);
return left.transform(compressor);
};
var ret = [], prev = null;
@@ -1202,8 +1200,8 @@ merge(Compressor.prototype, {
return this.consequent._eq_null(pure_getters)
|| this.alternative._eq_null(pure_getters);
})
- def(AST_Seq, function(pure_getters) {
- return this.cdr._eq_null(pure_getters);
+ def(AST_Sequence, function(pure_getters) {
+ return this.expressions[this.expressions.length - 1]._eq_null(pure_getters);
});
def(AST_SymbolRef, function(pure_getters) {
if (this.is_undefined) return true;
@@ -1236,8 +1234,8 @@ merge(Compressor.prototype, {
def(AST_Assign, function(){
return this.operator == "=" && this.right.is_boolean();
});
- def(AST_Seq, function(){
- return this.cdr.is_boolean();
+ def(AST_Sequence, function(){
+ return this.expressions[this.expressions.length - 1].is_boolean();
});
def(AST_True, return_true);
def(AST_False, return_true);
@@ -1263,8 +1261,8 @@ merge(Compressor.prototype, {
return binary(this.operator.slice(0, -1))
|| this.operator == "=" && this.right.is_number(compressor);
});
- def(AST_Seq, function(compressor){
- return this.cdr.is_number(compressor);
+ def(AST_Sequence, function(compressor){
+ return this.expressions[this.expressions.length - 1].is_number(compressor);
});
def(AST_Conditional, function(compressor){
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
@@ -1287,8 +1285,8 @@ merge(Compressor.prototype, {
def(AST_Assign, function(compressor){
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
- def(AST_Seq, function(compressor){
- return this.cdr.is_string(compressor);
+ def(AST_Sequence, function(compressor){
+ return this.expressions[this.expressions.length - 1].is_string(compressor);
});
def(AST_Conditional, function(compressor){
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
@@ -1616,10 +1614,10 @@ merge(Compressor.prototype, {
return this.expression;
return basic_negation(this);
});
- def(AST_Seq, function(compressor){
- var self = this.clone();
- self.cdr = self.cdr.negate(compressor);
- return self;
+ def(AST_Sequence, function(compressor){
+ var expressions = this.expressions.slice();
+ expressions.push(expressions.pop().negate(compressor));
+ return make_sequence(this, expressions);
});
def(AST_Conditional, function(compressor, first_in_statement){
var self = this.clone();
@@ -1763,9 +1761,10 @@ merge(Compressor.prototype, {
|| this.expression.has_side_effects(compressor)
|| this.property.has_side_effects(compressor);
});
- def(AST_Seq, function(compressor){
- return this.car.has_side_effects(compressor)
- || this.cdr.has_side_effects(compressor);
+ def(AST_Sequence, function(compressor){
+ return this.expressions.some(function(expression, index) {
+ return expression.has_side_effects(compressor);
+ });
});
})(function(node, func){
node.DEFMETHOD("has_side_effects", func);
@@ -2015,12 +2014,12 @@ merge(Compressor.prototype, {
for (var i = 0; i < def.length;) {
var x = def[i];
if (x._unused_side_effects) {
- side_effects.push(x._unused_side_effects);
+ merge_sequence(side_effects, x._unused_side_effects);
def.splice(i, 1);
} else {
if (side_effects.length > 0) {
- side_effects.push(x.value);
- x.value = AST_Seq.from_array(side_effects);
+ merge_sequence(side_effects, x.value);
+ x.value = make_sequence(x.value, side_effects);
side_effects = [];
}
++i;
@@ -2029,7 +2028,7 @@ merge(Compressor.prototype, {
if (side_effects.length > 0) {
side_effects = make_node(AST_BlockStatement, node, {
body: [ make_node(AST_SimpleStatement, node, {
- body: AST_Seq.from_array(side_effects)
+ body: make_sequence(node, side_effects)
}) ]
});
} else {
@@ -2179,8 +2178,8 @@ merge(Compressor.prototype, {
self.body.splice(i, 1);
continue;
}
- if (expr instanceof AST_Seq
- && (assign = expr.car) instanceof AST_Assign
+ if (expr instanceof AST_Sequence
+ && (assign = expr.expressions[0]) instanceof AST_Assign
&& assign.operator == "="
&& (sym = assign.left) instanceof AST_Symbol
&& vars.has(sym.name))
@@ -2190,7 +2189,7 @@ merge(Compressor.prototype, {
def.value = assign.right;
remove(defs, def);
defs.push(def);
- self.body[i].body = expr.cdr;
+ self.body[i].body = make_sequence(expr, expr.expressions.slice(1));
continue;
}
}
@@ -2224,12 +2223,14 @@ merge(Compressor.prototype, {
// if all elements were dropped. Note: original array may be
// returned if nothing changed.
function trim(nodes, compressor, first_in_statement) {
+ var len = nodes.length;
+ if (!len) return null;
var ret = [], changed = false;
- for (var i = 0, len = nodes.length; i < len; i++) {
+ for (var i = 0; i < len; i++) {
var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
changed |= node !== nodes[i];
if (node) {
- ret.push(node);
+ merge_sequence(ret, node);
first_in_statement = false;
}
}
@@ -2254,7 +2255,7 @@ merge(Compressor.prototype, {
this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
}
var args = trim(this.args, compressor, first_in_statement);
- return args && AST_Seq.from_array(args);
+ return args && make_sequence(this, args);
});
def(AST_Function, return_null);
def(AST_Binary, function(compressor, first_in_statement){
@@ -2270,10 +2271,7 @@ merge(Compressor.prototype, {
default:
var left = this.left.drop_side_effect_free(compressor, first_in_statement);
if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
- return make_node(AST_Seq, this, {
- car: left,
- cdr: right
- });
+ return make_sequence(this, [ left, right ]);
}
});
def(AST_Assign, return_this);
@@ -2316,14 +2314,14 @@ merge(Compressor.prototype, {
});
def(AST_Object, function(compressor, first_in_statement){
var values = trim(this.properties, compressor, first_in_statement);
- return values && AST_Seq.from_array(values);
+ return values && make_sequence(this, values);
});
def(AST_ObjectProperty, function(compressor, first_in_statement){
return this.value.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Array, function(compressor, first_in_statement){
var values = trim(this.elements, compressor, first_in_statement);
- return values && AST_Seq.from_array(values);
+ return values && make_sequence(this, values);
});
def(AST_Dot, function(compressor, first_in_statement){
if (this.expression.may_eq_null(compressor)) return this;
@@ -2335,19 +2333,15 @@ merge(Compressor.prototype, {
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
var property = this.property.drop_side_effect_free(compressor);
if (!property) return expression;
- return make_node(AST_Seq, this, {
- car: expression,
- cdr: property
- });
+ return make_sequence(this, [ expression, property ]);
});
- def(AST_Seq, function(compressor){
- var cdr = this.cdr.drop_side_effect_free(compressor);
- if (cdr === this.cdr) return this;
- if (!cdr) return this.car;
- return make_node(AST_Seq, this, {
- car: this.car,
- cdr: cdr
- });
+ def(AST_Sequence, function(compressor){
+ var last = this.expressions[this.expressions.length - 1];
+ var expr = last.drop_side_effect_free(compressor);
+ if (expr === last) return this;
+ var expressions = this.expressions.slice(0, -1);
+ if (expr) merge_sequence(expressions, expr);
+ return make_sequence(this, expressions);
});
})(function(node, func){
node.DEFMETHOD("drop_side_effect_free", func);
@@ -2737,7 +2731,7 @@ merge(Compressor.prototype, {
return a;
}, []);
if (assignments.length == 0) return null;
- return AST_Seq.from_array(assignments);
+ return make_sequence(this, assignments);
});
OPT(AST_Definitions, function(self, compressor){
@@ -2979,12 +2973,12 @@ merge(Compressor.prototype, {
var value = exp.body[0].value;
if (!value || value.is_constant()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
- return AST_Seq.from_array(args).transform(compressor);
+ return make_sequence(self, args).transform(compressor);
}
}
if (compressor.option("side_effects") && all(exp.body, is_empty)) {
var args = self.args.concat(make_node(AST_Undefined, self));
- return AST_Seq.from_array(args).transform(compressor);
+ return make_sequence(self, args).transform(compressor);
}
}
if (compressor.option("drop_console")) {
@@ -3025,40 +3019,85 @@ merge(Compressor.prototype, {
return self;
});
- OPT(AST_Seq, function(self, compressor){
- if (!compressor.option("side_effects"))
+ OPT(AST_Sequence, function(self, compressor){
+ if (!compressor.option("side_effects")) return self;
+ var expressions = [];
+ filter_for_side_effects();
+ var end = expressions.length - 1;
+ trim_right_for_undefined();
+ if (end > 0 && compressor.option("cascade")) trim_left_for_assignment();
+ if (end == 0) {
+ self = maintain_this_binding(compressor.parent(), self, expressions[0]);
+ if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
return self;
- self.car = self.car.drop_side_effect_free(compressor, first_in_statement(compressor));
- if (!self.car) return maintain_this_binding(compressor.parent(), self, self.cdr);
- if (compressor.option("cascade")) {
- var left;
- if (self.car instanceof AST_Assign
- && !self.car.left.has_side_effects(compressor)) {
- left = self.car.left;
- } else if (self.car instanceof AST_Unary
- && (self.car.operator == "++" || self.car.operator == "--")) {
- left = self.car.expression;
- }
- if (left
- && !(left instanceof AST_SymbolRef
- && left.definition().orig[0] instanceof AST_SymbolLambda)) {
- var parent, field;
- var cdr = self.cdr;
+ }
+ self.expressions = expressions;
+ return self;
+
+ function filter_for_side_effects() {
+ var first = first_in_statement(compressor);
+ var last = self.expressions.length - 1;
+ self.expressions.forEach(function(expr, index) {
+ if (index < last) expr = expr.drop_side_effect_free(compressor, first);
+ if (expr) {
+ merge_sequence(expressions, expr);
+ first = false;
+ }
+ });
+ }
+
+ function trim_right_for_undefined() {
+ while (end > 0 && is_undefined(expressions[end], compressor)) end--;
+ if (end < expressions.length - 1) {
+ expressions[end] = make_node(AST_UnaryPrefix, self, {
+ operator : "void",
+ expression : expressions[end]
+ });
+ expressions.length = end + 1;
+ }
+ }
+
+ function trim_left_for_assignment() {
+ for (var i = 0, j = 1; j <= end; j++) {
+ var left = expressions[i];
+ var cdr = expressions[j];
+ if (left instanceof AST_Assign
+ && !left.left.has_side_effects(compressor)) {
+ left = left.left;
+ } else if (left instanceof AST_Unary
+ && (left.operator == "++" || left.operator == "--")) {
+ left = left.expression;
+ } else left = null;
+ if (!left ||
+ left instanceof AST_SymbolRef
+ && left.definition().orig[0] instanceof AST_SymbolLambda) {
+ expressions[++i] = cdr;
+ continue;
+ }
+ var parent = null, field;
while (true) {
if (cdr.equivalent_to(left)) {
- var car = self.car instanceof AST_UnaryPostfix ? make_node(AST_UnaryPrefix, self.car, {
- operator: self.car.operator,
- expression: left
- }) : self.car;
+ var car = expressions[i];
+ if (car instanceof AST_UnaryPostfix) {
+ car = make_node(AST_UnaryPrefix, car, {
+ operator: car.operator,
+ expression: left
+ });
+ }
if (parent) {
parent[field] = car;
- return self.cdr;
+ expressions[i] = expressions[j];
+ } else {
+ expressions[i] = car;
}
- return car;
+ break;
}
if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
if (cdr.left.is_constant()) {
- if (cdr.operator == "||" || cdr.operator == "&&") break;
+ if (cdr.operator == "||" || cdr.operator == "&&") {
+ expressions[++i] = expressions[j];
+ break;
+ }
field = "right";
} else {
field = "left";
@@ -3066,31 +3105,27 @@ merge(Compressor.prototype, {
} else if (cdr instanceof AST_Call
|| cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
field = "expression";
- } else break;
+ } else {
+ expressions[++i] = expressions[j];
+ break;
+ }
parent = cdr;
cdr = cdr[field];
}
}
+ end = i;
+ expressions.length = end + 1;
}
- if (is_undefined(self.cdr, compressor)) {
- return make_node(AST_UnaryPrefix, self, {
- operator : "void",
- expression : self.car
- });
- }
- return self;
});
AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) {
- if (this.expression instanceof AST_Seq) {
- var seq = this.expression;
- var x = seq.to_array();
+ if (this.expression instanceof AST_Sequence) {
+ var x = this.expression.expressions.slice();
var e = this.clone();
e.expression = x.pop();
x.push(e);
- seq = AST_Seq.from_array(x).transform(compressor);
- return seq;
+ return make_sequence(this, x).optimize(compressor);
}
}
return this;
@@ -3108,15 +3143,12 @@ merge(Compressor.prototype, {
|| e instanceof AST_NaN
|| e instanceof AST_Infinity
|| e instanceof AST_Undefined)) {
- if (e instanceof AST_Seq) {
- e = e.to_array();
+ if (e instanceof AST_Sequence) {
+ e = e.expressions.slice();
e.push(make_node(AST_True, self));
- return AST_Seq.from_array(e).optimize(compressor);
+ return make_sequence(self, e).optimize(compressor);
}
- return make_node(AST_Seq, self, {
- car: e,
- cdr: make_node(AST_True, self)
- }).optimize(compressor);
+ return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor);
}
var seq = self.lift_sequences(compressor);
if (seq !== self) {
@@ -3146,10 +3178,10 @@ 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 (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_node(AST_Seq, self, {
- car: e,
- cdr: make_node(AST_True, self)
- })).optimize(compressor);
+ return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
+ e,
+ make_node(AST_True, self)
+ ])).optimize(compressor);
}
}
if (self.operator == "-" && e instanceof AST_Infinity) {
@@ -3181,29 +3213,32 @@ merge(Compressor.prototype, {
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) {
- if (this.left instanceof AST_Seq) {
- var seq = this.left;
- var x = seq.to_array();
+ if (this.left instanceof AST_Sequence) {
+ var x = this.left.expressions.slice();
var e = this.clone();
e.left = x.pop();
x.push(e);
- return AST_Seq.from_array(x).optimize(compressor);
+ return make_sequence(this, x).optimize(compressor);
}
- if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
+ if (this.right instanceof AST_Sequence && !this.left.has_side_effects(compressor)) {
var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
- var root = this.right.clone();
- var cursor, seq = root;
- while (assign || !seq.car.has_side_effects(compressor)) {
- cursor = seq;
- if (seq.cdr instanceof AST_Seq) {
- seq = seq.cdr = seq.cdr.clone();
- } else break;
- }
- if (cursor) {
+ var x = this.right.expressions;
+ var last = x.length - 1;
+ for (var i = 0; i < last; i++) {
+ if (!assign && x[i].has_side_effects(compressor)) break;
+ }
+ if (i == last) {
+ x = x.slice();
var e = this.clone();
- e.right = cursor.cdr;
- cursor.cdr = e;
- return root.optimize(compressor);
+ e.right = x.pop();
+ x.push(e);
+ return make_sequence(this, x).optimize(compressor);
+ } else if (i > 0) {
+ var e = this.clone();
+ e.right = make_sequence(this.right, x.slice(i));
+ x = x.slice(0, i);
+ x.push(e);
+ return make_sequence(this, x).optimize(compressor);
}
}
}
@@ -3272,17 +3307,17 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
- return make_node(AST_Seq, self, {
- car: self.right,
- cdr: make_node(AST_True, self)
- }).optimize(compressor);
+ return make_sequence(self, [
+ self.right,
+ make_node(AST_True, self)
+ ]).optimize(compressor);
}
if (rr && typeof rr == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
- return make_node(AST_Seq, self, {
- car: self.left,
- cdr: make_node(AST_True, self)
- }).optimize(compressor);
+ return make_sequence(self, [
+ self.left,
+ make_node(AST_True, self)
+ ]).optimize(compressor);
}
}
if (compressor.option("comparisons") && self.is_boolean()) {
@@ -3336,10 +3371,10 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor);
if (!rr) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
- return make_node(AST_Seq, self, {
- car: self.left,
- cdr: make_node(AST_False, self)
- }).optimize(compressor);
+ return make_sequence(self, [
+ self.left,
+ make_node(AST_False, self)
+ ]).optimize(compressor);
} else if (rr !== self.right) {
compressor.warn("Dropping side-effect-free && in boolean context [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
@@ -3362,10 +3397,10 @@ merge(Compressor.prototype, {
return self.left.optimize(compressor);
} else if (rr !== self.right) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
- return make_node(AST_Seq, self, {
- car: self.left,
- cdr: make_node(AST_True, self)
- }).optimize(compressor);
+ return make_sequence(self, [
+ self.left,
+ make_node(AST_True, self)
+ ]).optimize(compressor);
}
}
break;
@@ -3709,10 +3744,12 @@ merge(Compressor.prototype, {
OPT(AST_Conditional, function(self, compressor){
if (!compressor.option("conditionals")) return self;
- if (self.condition instanceof AST_Seq) {
- var car = self.condition.car;
- self.condition = self.condition.cdr;
- return AST_Seq.cons(car, self);
+ // This looks like lift_sequences(), should probably be under "sequences"
+ if (self.condition instanceof AST_Sequence) {
+ var expressions = self.condition.expressions.slice();
+ self.condition = expressions.pop();
+ expressions.push(self);
+ return make_sequence(self, expressions);
}
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
@@ -3795,10 +3832,10 @@ merge(Compressor.prototype, {
}
// x ? y : y --> x, y
if (consequent.equivalent_to(alternative)) {
- return make_node(AST_Seq, self, {
- car: self.condition,
- cdr: consequent
- }).optimize(compressor);
+ return make_sequence(self, [
+ self.condition,
+ consequent
+ ]).optimize(compressor);
}
if (is_true(self.consequent)) {
@@ -3968,10 +4005,10 @@ merge(Compressor.prototype, {
function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
- return best_of(compressor, self, make_node(AST_Seq, self, {
- car: self,
- cdr: make_node(AST_True, self)
- }).optimize(compressor));
+ return best_of(compressor, self, make_sequence(self, [
+ self,
+ make_node(AST_True, self)
+ ]).optimize(compressor));
}
return self;
};