From cc6aa3e5ac13c0da9f2481181f5b4f11275ca8c8 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 7 Apr 2017 03:42:17 +0800 Subject: [PATCH] fix incorrect context in variable substitution (#1791) `AST_Node.optimize()` is context-aware, so don't cache its results to be used elsewhere. Also fixed a few cases of AST corruption and beef up safety of `pure_getters`. --- lib/compress.js | 80 +++++++++++++++++++++++++++--------- test/compress/reduce_vars.js | 72 ++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 19 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 22c79b81..de3b4db2 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -283,10 +283,16 @@ merge(Compressor.prototype, { if (node instanceof AST_VarDef) { var d = node.name.definition(); if (d.fixed == null) { - d.fixed = node.value && function() { - return node.value; - }; + if (node.value) { + d.fixed = function() { + return node.value; + }; + descend(); + } else { + d.fixed = null; + } mark_as_safe(d); + return true; } else if (node.value) { d.fixed = false; } @@ -1160,13 +1166,42 @@ merge(Compressor.prototype, { && !node.expression.has_side_effects(compressor); } + // may_eq_null() + // returns true if this node may evaluate to null or undefined (function(def) { - def(AST_Node, return_false); + def(AST_Node, return_true); def(AST_Null, return_true); def(AST_Undefined, return_true); + def(AST_Constant, return_false); + def(AST_Array, return_false); + def(AST_Object, return_false); + def(AST_Function, return_false); + def(AST_UnaryPostfix, return_false); def(AST_UnaryPrefix, function() { return this.operator == "void"; }); + def(AST_Binary, function(compressor) { + switch (this.operator) { + case "&&": + return this.left.may_eq_null(compressor); + case "||": + return this.left.may_eq_null(compressor) + && this.right.may_eq_null(compressor); + default: + return false; + } + }) + def(AST_Assign, function(compressor) { + return this.operator == "=" + && this.right.may_eq_null(compressor); + }) + def(AST_Conditional, function(compressor) { + return this.consequent.may_eq_null(compressor) + || this.alternative.may_eq_null(compressor); + }) + def(AST_Seq, function(compressor) { + return this.cdr.may_eq_null(compressor); + }); def(AST_PropAccess, function(compressor) { return !compressor.option("unsafe"); }); @@ -3055,8 +3090,9 @@ merge(Compressor.prototype, { if (this.expression instanceof AST_Seq) { var seq = this.expression; var x = seq.to_array(); - this.expression = x.pop(); - x.push(this); + var e = this.clone(); + e.expression = x.pop(); + x.push(e); seq = AST_Seq.from_array(x).transform(compressor); return seq; } @@ -3110,9 +3146,14 @@ merge(Compressor.prototype, { if (e instanceof AST_Binary && (self.operator == "+" || self.operator == "-") && (e.operator == "*" || e.operator == "/" || e.operator == "%")) { - self.expression = e.left; - e.left = self; - return e; + return make_node(AST_Binary, self, { + operator: e.operator, + left: make_node(AST_UnaryPrefix, e.left, { + operator: self.operator, + expression: e.left + }), + right: e.right + }); } // avoids infinite recursion of numerals if (self.operator != "-" @@ -3131,23 +3172,25 @@ merge(Compressor.prototype, { if (this.left instanceof AST_Seq) { var seq = this.left; var x = seq.to_array(); - this.left = x.pop(); - x.push(this); + var e = this.clone(); + e.left = x.pop(); + x.push(e); return AST_Seq.from_array(x).optimize(compressor); } if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) { var assign = this.operator == "=" && this.left instanceof AST_SymbolRef; - var root = this.right; + 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 = seq.cdr = seq.cdr.clone(); } else break; } if (cursor) { - this.right = cursor.cdr; - cursor.cdr = this; + var e = this.clone(); + e.right = cursor.cdr; + cursor.cdr = e; return root.optimize(compressor); } } @@ -3547,9 +3590,8 @@ merge(Compressor.prototype, { if (d.should_replace === undefined) { var init = fixed.evaluate(compressor); if (init !== fixed) { - init = make_node_from_constant(init, fixed).optimize(compressor); - init = best_of_expression(init, fixed); - var value = init.print_to_string().length; + init = make_node_from_constant(init, fixed); + var value = best_of_expression(init.optimize(compressor), fixed).print_to_string().length; var name = d.name.length; var freq = d.references.length; var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq; @@ -3559,7 +3601,7 @@ merge(Compressor.prototype, { } } if (d.should_replace) { - return d.should_replace.clone(true); + return best_of_expression(d.should_replace.optimize(compressor), fixed).clone(true); } } } diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index cdc4ef20..fdfec99e 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1866,3 +1866,75 @@ delay_def: { } expect_stdout: true } + +booleans: { + options = { + booleans: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function(a) { + if (a != 0); + switch (a) { + case 0: + return "FAIL"; + case false: + return "PASS"; + } + }(false)); + } + expect: { + console.log(function(a) { + if (!1); + switch (!1) { + case 0: + return "FAIL"; + case !1: + return "PASS"; + } + }(!1)); + } + expect_stdout: "PASS" +} + +side_effects_assign: { + options = { + evaluate: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var a = typeof void (a && a.in == 1, 0); + console.log(a); + } + expect: { + var a = typeof void (a && a.in); + console.log(a); + } + expect_stdout: "undefined" +} + +pure_getters: { + options = { + pure_getters: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + try { + var a = (a.b, 2); + } catch (e) {} + console.log(a); + } + expect: { + try { + var a = (a.b, 2); + } catch (e) {} + console.log(a); + } + expect_stdout: "undefined" +} -- 2.34.1