From: Alex Lam S.L Date: Wed, 15 Mar 2017 10:44:13 +0000 (+0800) Subject: fix `AST_Node.optimize()` (#1602) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=8223b2e0db4cc41d467d9b94b05511a36c320184;p=UglifyJS.git fix `AST_Node.optimize()` (#1602) Liberal use of `Compressor.transform()` and `AST_Node.optimize()` presents an issue for look-up operations like `TreeWalker.in_boolean_context()` and `TreeWalker.parent()`. This is an incremental fix such that `AST_Node.optimize()` would now contain the correct stack information when called correctly. --- diff --git a/lib/compress.js b/lib/compress.js index ab4c3c2f..59a96684 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -152,13 +152,14 @@ merge(Compressor.prototype, { was_scope = true; } descend(node, this); - node = node.optimize(this); - if (was_scope && node instanceof AST_Scope) { - node.drop_unused(this); - descend(node, this); + descend(node, this); + var opt = node.optimize(this); + if (was_scope && opt instanceof AST_Scope) { + opt.drop_unused(this); + descend(opt, this); } - node._squeezed = true; - return node; + if (opt === node) opt._squeezed = true; + return opt; } }); @@ -171,8 +172,7 @@ merge(Compressor.prototype, { 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; }); }; @@ -914,7 +914,7 @@ merge(Compressor.prototype, { if (stat instanceof AST_LoopControl) { var lct = compressor.loopcontrol_target(stat.label); if ((stat instanceof AST_Break - && lct instanceof AST_BlockStatement + && !(lct instanceof AST_IterationStatement) && loop_body(lct) === self) || (stat instanceof AST_Continue && loop_body(lct) === self)) { if (stat.label) { @@ -1646,8 +1646,8 @@ merge(Compressor.prototype, { return thing && thing.aborts(); }; (function(def){ - def(AST_Statement, function(){ return null }); - def(AST_Jump, function(){ return this }); + def(AST_Statement, return_null); + def(AST_Jump, return_this); function block_aborts(){ var n = this.body.length; return n > 0 && aborts(this.body[n - 1]); @@ -2077,14 +2077,6 @@ merge(Compressor.prototype, { // drop_side_effect_free() // remove side-effect-free parts which only affects return value (function(def){ - function return_this() { - return this; - } - - function return_null() { - return null; - } - // Drop side-effect-free elements from an array of expressions. // Returns an array of expressions with side-effects or null // if all elements were dropped. Note: original array may be @@ -2358,7 +2350,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 }).transform(compressor); + return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor); } } else { compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start); @@ -2366,7 +2358,7 @@ 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 }).transform(compressor); + return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor); } } } @@ -2385,8 +2377,8 @@ merge(Compressor.prototype, { } if (is_empty(self.body) && is_empty(self.alternative)) { return make_node(AST_SimpleStatement, self.condition, { - body: self.condition - }).transform(compressor); + body: self.condition.clone() + }).optimize(compressor); } if (self.body instanceof AST_SimpleStatement && self.alternative instanceof AST_SimpleStatement) { @@ -2396,7 +2388,7 @@ merge(Compressor.prototype, { consequent : statement_to_expression(self.body), alternative : statement_to_expression(self.alternative) }) - }).transform(compressor); + }).optimize(compressor); } if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) { if (self_condition_length === negated_length && !negated_is_best @@ -2412,14 +2404,14 @@ merge(Compressor.prototype, { left : negated, right : statement_to_expression(self.body) }) - }).transform(compressor); + }).optimize(compressor); return make_node(AST_SimpleStatement, self, { body: make_node(AST_Binary, self, { operator : "&&", left : self.condition, right : statement_to_expression(self.body) }) - }).transform(compressor); + }).optimize(compressor); } if (self.body instanceof AST_EmptyStatement && self.alternative @@ -2430,7 +2422,7 @@ merge(Compressor.prototype, { left : self.condition, right : statement_to_expression(self.alternative) }) - }).transform(compressor); + }).optimize(compressor); } if (self.body instanceof AST_Exit && self.alternative instanceof AST_Exit @@ -2440,18 +2432,21 @@ merge(Compressor.prototype, { condition : self.condition, consequent : self.body.value || make_node(AST_Undefined, self.body), alternative : self.alternative.value || make_node(AST_Undefined, self.alternative) - }) - }).transform(compressor); + }).transform(compressor) + }).optimize(compressor); } if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) { - self.condition = make_node(AST_Binary, self.condition, { - operator: "&&", - left: self.condition, - right: self.body.condition - }).transform(compressor); - self.body = self.body.body; + self = make_node(AST_If, self, { + condition: make_node(AST_Binary, self.condition, { + operator: "&&", + left: self.condition, + right: self.body.condition + }), + body: self.body.body, + alternative: null + }); } if (aborts(self.body)) { if (self.alternative) { @@ -2459,7 +2454,7 @@ merge(Compressor.prototype, { self.alternative = null; return make_node(AST_BlockStatement, self, { body: [ self, alt ] - }).transform(compressor); + }).optimize(compressor); } } if (aborts(self.alternative)) { @@ -2469,7 +2464,7 @@ merge(Compressor.prototype, { self.alternative = null; return make_node(AST_BlockStatement, self, { body: [ self, body ] - }).transform(compressor); + }).optimize(compressor); } return self; }); diff --git a/lib/utils.js b/lib/utils.js index da663546..fdb20471 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -126,9 +126,11 @@ function merge(obj, ext) { return count; }; -function noop() {}; +function noop() {} function return_false() { return false; } function return_true() { return true; } +function return_this() { return this; } +function return_null() { return null; } var MAP = (function(){ function MAP(a, f, backwards) { diff --git a/test/compress/transform.js b/test/compress/transform.js new file mode 100644 index 00000000..7b616e40 --- /dev/null +++ b/test/compress/transform.js @@ -0,0 +1,129 @@ +booleans_evaluate: { + options = { + booleans: true, + evaluate: true, + } + input: { + console.log(typeof void 0 != "undefined"); + console.log(1 == 1, 1 === 1) + console.log(1 != 1, 1 !== 1) + } + expect: { + console.log(!1); + console.log(!0, !0); + console.log(!1, !1); + } +} + +booleans_global_defs: { + options = { + booleans: true, + evaluate: true, + global_defs: { + A: true, + }, + } + input: { + console.log(A == 1); + } + expect: { + console.log(!0); + } +} + +condition_evaluate: { + options = { + booleans: true, + dead_code: false, + evaluate: true, + loops: false, + } + input: { + while (1 === 2); + for (; 1 == true;); + if (void 0 == null); + } + expect: { + while (!1); + for (; !0;); + if (!0); + } +} + +if_else_empty: { + options = { + conditionals: true, + } + input: { + if ({} ? a : b); else {} + } + expect: { + !{} ? b : a; + } +} + +label_if_break: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + } + input: { + L: if (true) { + a; + break L; + } + } + expect: { + a; + } +} + +while_if_break: { + options = { + conditionals: true, + loops: true, + sequences: true, + } + input: { + while (a) { + if (b) if(c) d; + if (e) break; + } + } + expect: { + for(; a && (b && c && d, !e);); + } +} + +if_return: { + options = { + booleans: true, + conditionals: true, + if_return: true, + sequences: true, + } + input: { + function f(w, x, y, z) { + if (x) return; + if (w) { + if (y) return; + } else if (z) return; + if (x == y) return true; + + if (x) w(); + if (y) z(); + return true; + } + } + expect: { + function f(w, x, y, z) { + if (!x) { + if (w) { + if (y) return; + } else if (z) return; + return x == y || (x && w(), y && z(), !0); + } + } + } +}