diff options
Diffstat (limited to 'lib/compress.js')
-rw-r--r-- | lib/compress.js | 375 |
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; }; |