From 06f17bd0ec454f8e8d0482c459eef11c6d56f300 Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Sun, 12 Mar 2017 16:55:24 +0800 Subject: [PATCH] simplify optimisation loops --- lib/ast.js | 6 +- lib/compress.js | 225 +++++++++++++++++++++++++----------------------- 2 files changed, 119 insertions(+), 112 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index f7ab52e2..092a9590 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -984,8 +984,8 @@ TreeWalker.prototype = { push: function (node) { if (node instanceof AST_Lambda) { this.directives = Object.create(this.directives); - } else if (node instanceof AST_Directive) { - this.directives[node.value] = this.directives[node.value] ? "up" : true; + } else if (node instanceof AST_Directive && !this.directives[node.value]) { + this.directives[node.value] = node; } this.stack.push(node); }, @@ -1013,7 +1013,7 @@ TreeWalker.prototype = { for (var i = 0; i < node.body.length; ++i) { var st = node.body[i]; if (!(st instanceof AST_Directive)) break; - if (st.value == type) return true; + if (st.value == type) return st; } } }, diff --git a/lib/compress.js b/lib/compress.js index 07e21f89..d897b9d2 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -121,8 +121,8 @@ merge(Compressor.prototype, { node = node.process_expression(true); } var passes = +this.options.passes || 1; - for (var pass = 0; pass < passes && pass < 3; ++pass) { - if (pass > 0 || this.option("reduce_vars")) + for (var pass = 0; pass < passes; ++pass) { + if (this.option("reduce_vars")) node.reset_opt_flags(this, true); node = node.transform(this); } @@ -145,7 +145,6 @@ merge(Compressor.prototype, { this.warnings_produced = {}; }, before: function(node, descend, in_list) { - if (node._squeezed) return node; var was_scope = false; if (node instanceof AST_Scope) { node = node.hoist_declarations(this); @@ -157,7 +156,6 @@ merge(Compressor.prototype, { node.drop_unused(this); descend(node, this); } - node._squeezed = true; return node; } }); @@ -167,12 +165,9 @@ merge(Compressor.prototype, { function OPT(node, optimizer) { node.DEFMETHOD("optimize", function(compressor){ var self = this; - if (self._optimized) return self; if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); - opt._optimized = true; - if (opt === self) return opt; - return opt.transform(compressor); + return opt; }); }; @@ -235,10 +230,6 @@ merge(Compressor.prototype, { } }); var tw = new TreeWalker(function(node, descend){ - if (!(node instanceof AST_Directive || node instanceof AST_Constant)) { - node._squeezed = false; - node._optimized = false; - } if (reduce_vars) { if (node instanceof AST_Toplevel) node.globals.each(reset_def); if (node instanceof AST_Scope) node.variables.each(reset_def); @@ -424,15 +415,17 @@ merge(Compressor.prototype, { // func(something) because that changes the meaning of // the func (becomes lexical instead of global). function maintain_this_binding(parent, orig, val) { - if (parent instanceof AST_Call && parent.expression === orig) { - if (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 - }); - } + if (parent instanceof AST_Call && parent.expression === orig + && (val instanceof AST_PropAccess + || val instanceof AST_SymbolRef && val.name === "eval")) { + return orig instanceof AST_Seq + && orig.car instanceof AST_Number + && orig.car.value == 0 ? orig : make_node(AST_Seq, orig, { + car: make_node(AST_Number, orig, { + value: 0 + }), + cdr: val + }); } return val; } @@ -649,8 +642,6 @@ merge(Compressor.prototype, { statements[prev_stat_index] = make_node(AST_EmptyStatement, self); var_defs_removed = true; } - // Further optimize statement after substitution. - stat.reset_opt_flags(compressor); compressor.warn("Collapsing " + (is_constant ? "constant" : "variable") + " " + var_name + " [{file}:{line},{col}]", node.start); @@ -817,7 +808,7 @@ merge(Compressor.prototype, { body: body }); stat.alternative = null; - ret = funs.concat([ stat.transform(compressor) ]); + ret = funs.concat([ stat ]); continue loop; } @@ -834,7 +825,7 @@ merge(Compressor.prototype, { CHANGED = true; ret.push(make_node(AST_Return, ret[0], { value: null - }).transform(compressor)); + })); ret.unshift(stat); continue loop; } @@ -858,7 +849,7 @@ merge(Compressor.prototype, { stat.alternative = make_node(AST_BlockStatement, stat, { body: body }); - ret = [ stat.transform(compressor) ]; + ret = [ stat ]; continue loop; } @@ -878,7 +869,7 @@ merge(Compressor.prototype, { stat.alternative = make_node(AST_BlockStatement, stat.alternative, { body: as_statement_array(stat.alternative).slice(0, -1) }); - ret = [ stat.transform(compressor) ]; + ret = [ stat ]; continue loop; } @@ -982,7 +973,7 @@ merge(Compressor.prototype, { } else { left = AST_Seq.cons(left, right); } - return left.transform(compressor); + return left; }; var ret = [], prev = null; statements.forEach(function(stat){ @@ -1261,6 +1252,14 @@ merge(Compressor.prototype, { return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2); } + function best_of_constant(compressor, ev, ast) { + var self = compressor.self(); + compressor.pop(); + ev = ev.transform(compressor); + compressor.push(self); + return best_of(compressor, ev, ast); + } + // methods to evaluate a constant expression (function (def){ // The evaluate method returns an array with one or two @@ -1289,8 +1288,8 @@ merge(Compressor.prototype, { }); AST_Node.DEFMETHOD("evaluate_self", function(compressor){ var ev = this.evaluate(compressor); - if (ev.length > 1 && !ev[0].equivalent_to(this)) { - return best_of(compressor, ev[0].optimize(compressor), this); + if (ev.length > 1) { + return best_of_constant(compressor, ev[0], this); } return this; }); @@ -1675,7 +1674,7 @@ merge(Compressor.prototype, { /* -----[ optimizers ]----- */ OPT(AST_Directive, function(self, compressor){ - if (compressor.has_directive(self.value) === "up") { + if (compressor.has_directive(self.value) !== self) { return make_node(AST_EmptyStatement, self); } return self; @@ -2120,9 +2119,12 @@ merge(Compressor.prototype, { if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) { if (this.expression instanceof AST_Function && (!this.expression.name || !this.expression.name.definition().references.length)) { - var node = this.clone(); - node.expression = node.expression.process_expression(false); - return node; + var expression = this.expression.process_expression(false); + if (!this.expression.equivalent_to(expression)) { + var node = this.clone(); + node.expression = expression; + return node; + } } return this; } @@ -2140,6 +2142,7 @@ merge(Compressor.prototype, { switch (this.operator) { case "&&": case "||": + if (right === this.right) return this; var node = this.clone(); node.right = right; return node; @@ -2254,7 +2257,7 @@ merge(Compressor.prototype, { OPT(AST_DWLoop, function(self, compressor){ var cond = self.condition.evaluate(compressor); if (cond.length > 1) { - self.condition = best_of_expression(cond[0].transform(compressor), self.condition); + self.condition = best_of_constant(compressor, cond[0], self.condition); } if (!compressor.option("loops")) return self; if (cond.length > 1) { @@ -2274,7 +2277,7 @@ merge(Compressor.prototype, { } } if (self instanceof AST_While) { - return make_node(AST_For, self, self).optimize(compressor); + return make_node(AST_For, self, self); } return self; }); @@ -2285,11 +2288,10 @@ merge(Compressor.prototype, { if (self.body instanceof AST_BlockStatement) { self.body = self.body.clone(); self.body.body = rest.concat(self.body.body.slice(1)); - self.body = self.body.transform(compressor); } else { self.body = make_node(AST_BlockStatement, self.body, { body: rest - }).transform(compressor); + }); } if_break_in_loop(self, compressor); } @@ -2329,7 +2331,7 @@ merge(Compressor.prototype, { if (cond) { cond = cond.evaluate(compressor); if (cond.length > 1) { - self.condition = best_of_expression(cond[0].transform(compressor), self.condition); + self.condition = best_of_constant(compressor, cond[0], self.condition); } } if (!compressor.option("loops")) return self; @@ -2372,7 +2374,7 @@ merge(Compressor.prototype, { extract_declarations_from_unreachable_code(compressor, self.alternative, a); } a.push(self.body); - return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor); + return make_node(AST_BlockStatement, self, { body: a }); } } else { compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start); @@ -2380,10 +2382,10 @@ merge(Compressor.prototype, { var a = []; extract_declarations_from_unreachable_code(compressor, self.body, a); if (self.alternative) a.push(self.alternative); - return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor); + return make_node(AST_BlockStatement, self, { body: a }); } } - self.condition = best_of_expression(cond[0].transform(compressor), self.condition); + self.condition = best_of_constant(compressor, cond[0], self.condition); } var negated = self.condition.negate(compressor); var self_condition_length = self.condition.print_to_string().length; @@ -2401,7 +2403,7 @@ merge(Compressor.prototype, { if (is_empty(self.body) && is_empty(self.alternative)) { return make_node(AST_SimpleStatement, self.condition, { body: self.condition - }).optimize(compressor); + }); } if (self.body instanceof AST_SimpleStatement && self.alternative instanceof AST_SimpleStatement) { @@ -2411,7 +2413,7 @@ merge(Compressor.prototype, { consequent : statement_to_expression(self.body), alternative : statement_to_expression(self.alternative) }) - }).optimize(compressor); + }); } if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) { if (self_condition_length === negated_length && !negated_is_best @@ -2427,14 +2429,14 @@ merge(Compressor.prototype, { left : negated, right : statement_to_expression(self.body) }) - }).optimize(compressor); + }); return make_node(AST_SimpleStatement, self, { body: make_node(AST_Binary, self, { operator : "&&", left : self.condition, right : statement_to_expression(self.body) }) - }).optimize(compressor); + }); } if (self.body instanceof AST_EmptyStatement && self.alternative @@ -2445,7 +2447,7 @@ merge(Compressor.prototype, { left : self.condition, right : statement_to_expression(self.alternative) }) - }).optimize(compressor); + }); } if (self.body instanceof AST_Exit && self.alternative instanceof AST_Exit @@ -2456,7 +2458,7 @@ merge(Compressor.prototype, { consequent : self.body.value || make_node(AST_Undefined, self.body), alternative : self.alternative.value || make_node(AST_Undefined, self.alternative) }) - }).optimize(compressor); + }); } if (self.body instanceof AST_If && !self.body.alternative @@ -2465,7 +2467,7 @@ merge(Compressor.prototype, { operator: "&&", left: self.condition, right: self.body.condition - }).transform(compressor); + }); self.body = self.body.body; } if (aborts(self.body)) { @@ -2474,7 +2476,7 @@ merge(Compressor.prototype, { self.alternative = null; return make_node(AST_BlockStatement, self, { body: [ self, alt ] - }).optimize(compressor); + }); } } if (aborts(self.alternative)) { @@ -2484,7 +2486,7 @@ merge(Compressor.prototype, { self.alternative = null; return make_node(AST_BlockStatement, self, { body: [ self, body ] - }).optimize(compressor); + }); } return self; }); @@ -2493,7 +2495,7 @@ merge(Compressor.prototype, { if (self.body.length == 0 && compressor.option("conditionals")) { return make_node(AST_SimpleStatement, self, { body: self.expression - }).optimize(compressor); + }); } for(;;) { var last_branch = self.body[self.body.length - 1]; @@ -2511,7 +2513,7 @@ merge(Compressor.prototype, { var exp = self.expression.evaluate(compressor); out: if (exp.length == 2) try { // constant expression - self.expression = best_of_expression(exp[0].transform(compressor), self.expression); + self.expression = best_of_constant(compressor, exp[0], self.expression); if (!compressor.option("dead_code")) break out; var value = exp[1]; var in_if = false; @@ -2531,7 +2533,7 @@ merge(Compressor.prototype, { body: node.body.reduce(function(a, branch){ return a.concat(branch.body); }, []) - }).optimize(compressor); + }); } else if (node instanceof AST_If || node instanceof AST_Try) { var save = in_if; @@ -2671,7 +2673,7 @@ merge(Compressor.prototype, { if (self.args.length != 1) { return make_node(AST_Array, self, { elements: self.args - }).optimize(compressor); + }); } break; case "Object": @@ -2689,7 +2691,7 @@ merge(Compressor.prototype, { left: self.args[0], operator: "+", right: make_node(AST_String, self, { value: "" }) - }).optimize(compressor); + }); break; case "Number": if (self.args.length == 0) return make_node(AST_Number, self, { @@ -2698,7 +2700,7 @@ merge(Compressor.prototype, { if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { expression: self.args[0], operator: "+" - }).optimize(compressor); + }); case "Boolean": if (self.args.length == 0) return make_node(AST_False, self); if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { @@ -2707,7 +2709,7 @@ merge(Compressor.prototype, { operator: "!" }), operator: "!" - }).optimize(compressor); + }); break; case "Function": // new Function() => function(){} @@ -2772,7 +2774,7 @@ merge(Compressor.prototype, { left: make_node(AST_String, self, { value: "" }), operator: "+", right: exp.expression - }).optimize(compressor); + }); } else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: { var separator; @@ -2827,7 +2829,7 @@ merge(Compressor.prototype, { left : prev, right : el }); - }, first).optimize(compressor); + }, first); } // need this awkward cloning to not affect original element // best_of_expression will decide which one to get through. @@ -2843,13 +2845,13 @@ 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).optimize(compressor); + return AST_Seq.from_array(args); } } if (compressor.option("side_effects")) { if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) { var args = self.args.concat(make_node(AST_Undefined, self)); - return AST_Seq.from_array(args).optimize(compressor); + return AST_Seq.from_array(args); } } } @@ -2862,7 +2864,7 @@ merge(Compressor.prototype, { if (name instanceof AST_SymbolRef && name.name == "console" && name.undeclared()) { - return make_node(AST_Undefined, self).optimize(compressor); + return make_node(AST_Undefined, self); } } } @@ -2884,7 +2886,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); } } } @@ -2894,8 +2896,12 @@ merge(Compressor.prototype, { OPT(AST_Seq, function(self, compressor){ if (!compressor.option("side_effects")) 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); + var car = self.car.drop_side_effect_free(compressor, first_in_statement(compressor)); + if (car) { + self.car = car; + } else { + return maintain_this_binding(compressor.parent(), self, self.cdr); + } if (compressor.option("cascade")) { var left; if (self.car instanceof AST_Assign @@ -2955,15 +2961,12 @@ merge(Compressor.prototype, { }); OPT(AST_UnaryPostfix, function(self, compressor){ - var seq = self.lift_sequences(compressor); - return seq !== self ? seq.optimize(compressor) : self; + return self.lift_sequences(compressor); }); OPT(AST_UnaryPrefix, function(self, compressor){ var seq = self.lift_sequences(compressor); - if (seq !== self) { - return seq.optimize(compressor); - } + if (seq !== self) return seq; var e = self.expression; if (compressor.option("side_effects") && self.operator == "void") { e = e.drop_side_effect_free(compressor); @@ -2971,7 +2974,7 @@ merge(Compressor.prototype, { self.expression = e; return self; } else { - return make_node(AST_Undefined, self).optimize(compressor); + return make_node(AST_Undefined, self); } } if (compressor.option("booleans") && compressor.in_boolean_context()) { @@ -2992,9 +2995,18 @@ merge(Compressor.prototype, { return make_node(AST_Seq, self, { car: e, cdr: make_node(AST_True, self) - }).optimize(compressor); + }); } } + if (e instanceof AST_Infinity && self.operator == "-") { + self.expression = e.transform(compressor); + return self; + } else if (e instanceof AST_Number + && (self.operator == "-" + || self.operator == "!" + && (e.value == 0 || e.value == 1))) { + return self; + } return self.evaluate_self(compressor); }); @@ -3087,9 +3099,7 @@ merge(Compressor.prototype, { } } var seq = self.lift_sequences(compressor); - if (seq !== self) { - return seq.optimize(compressor); - } + if (seq !== self) return seq; if (compressor.option("comparisons")) switch (self.operator) { case "===": case "!==": @@ -3110,7 +3120,7 @@ merge(Compressor.prototype, { if (expr instanceof AST_SymbolRef ? !expr.undeclared() : !(expr instanceof AST_PropAccess) || compressor.option("screw_ie8")) { self.right = expr; - self.left = make_node(AST_Undefined, self.left).transform(compressor); + self.left = make_node(AST_Undefined, self.left); if (self.operator.length == 2) self.operator += "="; } } @@ -3123,15 +3133,15 @@ merge(Compressor.prototype, { 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_Seq, self, { - car: self.left.transform(compressor), - cdr: make_node(AST_False, self).transform(compressor) - }).optimize(compressor); + car: self.left, + cdr: make_node(AST_False, self) + }); } if (ll.length > 1 && ll[1]) { - return best_of(compressor, rr[0].optimize(compressor), self.right); + return best_of_constant(compressor, rr[0], self.right); } if (rr.length > 1 && rr[1]) { - return best_of(compressor, ll[0].optimize(compressor), self.left); + return best_of_constant(compressor, ll[0], self.left); } break; case "||": @@ -3140,15 +3150,15 @@ merge(Compressor.prototype, { 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_Seq, self, { - car: self.left.transform(compressor), - cdr: make_node(AST_True, self).transform(compressor) - }).optimize(compressor); + car: self.left, + cdr: make_node(AST_True, self) + }); } if (ll.length > 1 && !ll[1]) { - return best_of(compressor, rr[0].optimize(compressor), self.right); + return best_of_constant(compressor, rr[0], self.right); } if (rr.length > 1 && !rr[1]) { - return best_of(compressor, ll[0].optimize(compressor), self.left); + return best_of_constant(compressor, ll[0], self.left); } break; case "+": @@ -3157,16 +3167,16 @@ merge(Compressor.prototype, { if (ll.length > 1 && ll[0] instanceof AST_String && ll[1]) { compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); return make_node(AST_Seq, self, { - car: self.right.transform(compressor), - cdr: make_node(AST_True, self).transform(compressor) - }).optimize(compressor); + car: self.right, + cdr: make_node(AST_True, self) + }); } if (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_Seq, self, { - car: self.left.transform(compressor), - cdr: make_node(AST_True, self).transform(compressor) - }).optimize(compressor); + car: self.left, + cdr: make_node(AST_True, self) + }); } break; } @@ -3203,7 +3213,7 @@ merge(Compressor.prototype, { && self.left.left.getValue() == "" && self.right.is_string(compressor)) { self.left = self.left.right; - return self.optimize(compressor); + return self; } } if (compressor.option("evaluate")) { @@ -3424,8 +3434,8 @@ merge(Compressor.prototype, { operator : self.operator, left : self.left, right : self.right.left - }).transform(compressor); - self.right = self.right.right.transform(compressor); + }); + self.right = self.right.right; } return self.evaluate_self(compressor); }); @@ -3442,11 +3452,11 @@ merge(Compressor.prototype, { && (!self.scope.uses_with || !compressor.find_parent(AST_With))) { 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); case "Infinity": - return make_node(AST_Infinity, self).optimize(compressor); + return make_node(AST_Infinity, self); } } if (compressor.option("evaluate") && compressor.option("reduce_vars")) { @@ -3455,7 +3465,7 @@ merge(Compressor.prototype, { if (d.should_replace === undefined) { var init = d.fixed.evaluate(compressor); if (init.length > 1) { - init = best_of(compressor, init[0].optimize(compressor), d.fixed); + init = best_of_constant(compressor, init[0], d.fixed); var value = init.print_to_string().length; var name = d.name.length; var freq = d.references.length; @@ -3502,9 +3512,7 @@ merge(Compressor.prototype, { var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ]; OPT(AST_Assign, function(self, compressor){ var seq = self.lift_sequences(compressor); - if (seq !== self) { - return seq.optimize(compressor); - } + if (seq !== self) return seq; if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) { // x = expr1 OP expr2 if (self.right.left instanceof AST_SymbolRef @@ -3542,7 +3550,6 @@ merge(Compressor.prototype, { compressor.warn("Condition always false [{file}:{line},{col}]", self.start); return maintain_this_binding(compressor.parent(), self, self.alternative); } - self.condition = best_of(compressor, cond[0].transform(compressor), self.condition); } var negated = self.condition.negate(compressor, first_in_statement(compressor)); if (best_of(compressor, self.condition, negated) === negated) { @@ -3609,7 +3616,7 @@ merge(Compressor.prototype, { return make_node(AST_Seq, self, { car: self.condition, cdr: consequent - }).optimize(compressor); + }); } if (is_true(self.consequent)) { @@ -3716,7 +3723,7 @@ merge(Compressor.prototype, { return make_node(AST_Dot, self, { expression : self.expression, property : prop - }).optimize(compressor); + }); } var v = parseFloat(prop); if (!isNaN(v) && v.toString() == prop) { @@ -3740,7 +3747,7 @@ merge(Compressor.prototype, { property : make_node(AST_String, self, { value: prop }) - }).optimize(compressor); + }); } if (compressor.option("unsafe_proto") && self.expression instanceof AST_Dot @@ -3769,10 +3776,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, { + return best_of_constant(compressor, make_node(AST_Seq, self, { car: self, cdr: make_node(AST_True, self) - }).optimize(compressor)); + }), self); } return self; };