From: Mihai Bazon Date: Mon, 10 Sep 2012 12:54:17 +0000 (+0300) Subject: more progress on the compressor (WIP) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=a41e6cfabb0befc0b71c95aa55ac0568cce36518;p=UglifyJS.git more progress on the compressor (WIP) --- diff --git a/lib/compress.js b/lib/compress.js index 3ce06298..e615699c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -289,8 +289,8 @@ function Compressor(options, false_by_default) { }); function best_of(ast1, ast2) { - return ast1.print_to_string({ beautify: false }).length > - ast2.print_to_string({ beautify: false }).length + return ast1.print_to_string().length > + ast2.print_to_string().length ? ast2 : ast1; }; @@ -448,14 +448,12 @@ function Compressor(options, false_by_default) { self.operator = "||"; self.left = self.left.negate(compressor); self.right = self.right.negate(compressor); - //return best_of(basic_negation(this), self); - return self; + return best_of(basic_negation(this), self); case "||": self.operator = "&&"; self.left = self.left.negate(compressor); self.right = self.right.negate(compressor); - //return best_of(basic_negation(this), self); - return self; + return best_of(basic_negation(this), self); } return basic_negation(this); }); @@ -692,18 +690,19 @@ function Compressor(options, false_by_default) { } } } - if (self.condition instanceof AST_UnaryPrefix - && self.condition.operator == "!") { - self.condition = self.condition.expression; + var negated = self.condition.negate(compressor); + var negated_is_best = best_of(self.condition, negated) === negated; + if (self.alternative && negated_is_best) { + self.condition = negated; var tmp = self.body; - self.body = self.alternative || make_node(AST_EmptyStatement, self); + self.body = self.alternative || new AST_EmptyStatement(); self.alternative = tmp; } if (self.body instanceof AST_EmptyStatement && self.alternative instanceof AST_EmptyStatement) { return make_node(AST_SimpleStatement, self.condition, { body: self.condition - }).optimize(compressor); + }); } if (self.body instanceof AST_SimpleStatement && self.alternative instanceof AST_SimpleStatement) { @@ -718,7 +717,14 @@ function Compressor(options, false_by_default) { if ((!self.alternative || self.alternative instanceof AST_EmptyStatement) && self.body instanceof AST_SimpleStatement) { - return make_node(AST_SimpleStatement, self, { + if (negated_is_best) return make_node(AST_SimpleStatement, self, { + body: make_node(AST_Binary, self, { + operator : "||", + left : negated, + right : self.body.body + }).optimize(compressor) + }); + else return make_node(AST_SimpleStatement, self, { body: make_node(AST_Binary, self, { operator : "&&", left : self.condition, @@ -843,7 +849,61 @@ function Compressor(options, false_by_default) { self = self.clone(); self.expression = self.expression.squeeze(compressor); self.args = do_list(self.args, compressor); - return self; + return self.optimize(compressor); + }); + + AST_Call.DEFMETHOD("optimize", function(compressor){ + if (compressor.option("unsafe")) { + var exp = this.expression; + if (exp instanceof AST_SymbolRef && exp.undeclared) { + switch (exp.name) { + case "Array": + if (this.args.length != 1) { + return make_node(AST_Array, this, { + elements: this.args + }).optimize(compressor); + } + break; + case "Object": + if (this.args.length == 0) { + return make_node(AST_Object, this, { + properties: [] + }).optimize(compressor); + } + break; + case "String": + return make_node(AST_Binary, this, { + left: this.args[0], + operator: "+", + right: make_node(AST_String, this, { value: "" }) + }); + } + } + else if (exp instanceof AST_Dot && exp.property == "toString" && this.args.length == 0) { + return make_node(AST_Binary, this, { + left: exp.expression, + operator: "+", + right: make_node(AST_String, this, { value: "" }) + }); + } + } + return this; + }); + + AST_New.DEFMETHOD("optimize", function(compressor){ + if (compressor.option("unsafe")) { + var exp = this.expression; + if (exp instanceof AST_SymbolRef && exp.undeclared) { + switch (exp.name) { + case "Object": + case "RegExp": + case "Function": + case "Error": + case "Array": + return make_node(AST_Call, this, this).optimize(compressor); + } + } + } }); SQUEEZE(AST_Seq, function(self, compressor){ @@ -914,6 +974,15 @@ function Compressor(options, false_by_default) { }); AST_Binary.DEFMETHOD("optimize", function(compressor){ + if (compressor.option("comparations")) switch (this.operator) { + case "===": + case "!==": + if ((this.left.is_string() && this.right.is_string()) || + (this.left.is_boolean() && this.right.is_boolean())) { + this.operator = this.operator.substr(0, 2); + } + break; + } if (compressor.option("booleans") && compressor.in_boolean_context()) switch (this.operator) { case "&&": var ll = this.left.evaluate(compressor), left = ll[0]; @@ -984,12 +1053,39 @@ function Compressor(options, false_by_default) { return self.alternative; } } - var rev = self.clone(); - rev.condition = cond[0].negate(compressor); - var tmp = rev.consequent; - rev.consequent = rev.alternative; - rev.alternative = tmp; - return best_of(self, rev); + var negated = cond[0].negate(compressor); + if (best_of(cond[0], negated) === negated) { + self = make_node(AST_Conditional, self, { + condition: negated, + consequent: self.alternative, + alternative: self.consequent + }); + } + var consequent = self.consequent; + var alternative = self.alternative; + if (consequent instanceof AST_Assign + && alternative instanceof AST_Assign + && consequent.operator == alternative.operator + // XXX: this is a rather expensive way to test two node's equivalence: + && consequent.left.print_to_string() == alternative.left.print_to_string() + ) { + /* + * Stuff like this: + * if (foo) exp = something; else exp = something_else; + * ==> + * exp = foo ? something : something_else; + */ + self = make_node(AST_Assign, self, { + operator: consequent.operator, + left: consequent.left, + right: make_node(AST_Conditional, self, { + condition: self.condition, + consequent: consequent.right, + alternative: alternative.right + }) + }); + } + return self; }); SQUEEZE(AST_Array, function(self, compressor){ diff --git a/lib/output.js b/lib/output.js index c803fbae..35c7ade7 100644 --- a/lib/output.js +++ b/lib/output.js @@ -313,6 +313,9 @@ function OutputStream(options) { }; AST_Node.DEFMETHOD("print_to_string", function(options){ + options = defaults(options, { + beautify: false + }); var s = OutputStream(options); this.print(s); return s.get(); diff --git a/lib/utils.js b/lib/utils.js index 519e6962..79e612c6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -97,9 +97,9 @@ function repeat_string(str, i) { }; function defaults(args, defs) { - var ret = {}; if (args === true) args = {}; + var ret = args || {}; for (var i in defs) if (HOP(defs, i)) { ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; } diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js index ba5e674d..b9c348a0 100644 --- a/test/compress/conditionals.js +++ b/test/compress/conditionals.js @@ -72,3 +72,18 @@ ifs_3_should_warn: { var jj; foo(); // 2 } } + +ifs_4: { + options = { + conditionals: true + }; + input: { + if (foo && bar) { + x(foo)[10].bar.baz = something(); + } else + x(foo)[10].bar.baz = something_else(); + } + expect: { + x(foo)[10].bar.baz = (foo && bar) ? something() : something_else(); + } +}