From: Alex Lam S.L Date: Thu, 28 May 2020 23:05:47 +0000 (+0100) Subject: enhance `evaluate`, `functions` & `inline` (#3931) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=6a5c63e1e324ba502b060911d54825a93e437da4;p=UglifyJS.git enhance `evaluate`, `functions` & `inline` (#3931) --- diff --git a/lib/compress.js b/lib/compress.js index 9ddbfa43..daf60ee8 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -450,6 +450,11 @@ merge(Compressor.prototype, { tw.safe_ids[def.id] = safe; } + function push_ref(def, ref) { + def.references.push(ref); + def.last_ref = ref; + } + function add_assign(tw, def, node) { if (def.fixed === false) return; tw.assigns.add(def.id, node); @@ -550,7 +555,7 @@ merge(Compressor.prototype, { if (!(node instanceof AST_Symbol)) return; var d = node.definition(); if (!d) return; - if (node instanceof AST_SymbolRef) d.references.push(node); + if (node instanceof AST_SymbolRef) push_ref(d, node); d.fixed = false; }); def(AST_Accessor, function(tw, descend, compressor) { @@ -588,7 +593,7 @@ merge(Compressor.prototype, { }); }; if (!safe) return; - d.references.push(sym); + push_ref(d, sym); mark(tw, d, false); node.right.walk(tw); mark(tw, d, true); @@ -784,7 +789,7 @@ merge(Compressor.prototype, { def(AST_SymbolRef, function(tw, descend, compressor) { if (this.fixed) delete this.fixed; var d = this.definition(); - d.references.push(this); + push_ref(d, this); if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) { @@ -886,7 +891,7 @@ merge(Compressor.prototype, { }); }; if (!safe) return; - d.references.push(exp); + push_ref(d, exp); mark(tw, d, true); add_assign(tw, d, node); return true; @@ -3280,12 +3285,30 @@ merge(Compressor.prototype, { return this.value; }); def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) { - if (!ignore_side_effects) return this; - if (this.operator != "=") return this; - var node = this.right; + var lhs = this.left; + if (!ignore_side_effects) { + if (!(lhs instanceof AST_SymbolRef)) return this; + if (!HOP(lhs, "_eval") && !lhs.fixed) return this; + var def = lhs.definition(); + if (def.undeclared) return this; + if (def.last_ref !== lhs) return this; + if (def.single_use == "m") return this; + } + var op = this.operator; + var node; + if (HOP(lhs, "_eval") || !lhs.fixed) { + node = op == "=" ? this.right : make_node(AST_Binary, this, { + operator: op.slice(0, -1), + left: lhs, + right: this.right, + }); + } else { + node = lhs; + } var value = node._eval(compressor, ignore_side_effects, cached, depth); - modified(this.left); - return value === node ? this : value; + if (value === node) return this; + modified(lhs); + return value; }); def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) { if (!ignore_side_effects) return this; @@ -3377,8 +3400,7 @@ merge(Compressor.prototype, { case "++": case "--": if (!(e instanceof AST_SymbolRef)) return this; - var refs = e.definition().references; - if (!ignore_side_effects && refs[refs.length - 1] !== e) return this; + if (!ignore_side_effects && e.definition().last_ref !== e) return this; if (HOP(e, "_eval")) v = +(op[0] + 1) + +v; modified(e); return v; @@ -3388,11 +3410,11 @@ merge(Compressor.prototype, { def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) { var e = this.expression; if (!e.fixed) return this; - var refs = e.definition().references; - if (!ignore_side_effects && refs[refs.length - 1] !== e) return this; + if (!ignore_side_effects && e.definition().last_ref !== e) return this; var v = e._eval(compressor, ignore_side_effects, cached, depth + 1); + if (v === e) return this; modified(e); - return v === e ? this : +v; + return v; }); var non_converting_binary = makePredicate("&& || === !=="); def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) { @@ -4385,13 +4407,11 @@ merge(Compressor.prototype, { var defun = make_node(AST_Defun, def, def.value); defun.name = make_node(AST_SymbolDefun, def.name, def.name); var name_def = def.name.scope.resolve().def_function(defun.name); - if (old_def) def.value.walk(new TreeWalker(function(node) { - if (node instanceof AST_SymbolRef && node.definition() === old_def) { - node.name = name_def.name; - node.thedef = name_def; - node.reference({}); - } - })); + if (old_def) old_def.references.forEach(function(node) { + node.name = name_def.name; + node.thedef = name_def; + node.reference({}); + }); body.push(defun); } else { if (var_defs.length > 1 && sym.orig.indexOf(def.name) > sym.eliminated) { @@ -6242,17 +6262,19 @@ merge(Compressor.prototype, { && !fn.uses_arguments && !fn.pinned() && !(fn.name && fn instanceof AST_Function) - && (exp === fn - || !recursive_ref(compressor, def = exp.definition()) && fn.is_constant_expression(exp.scope)) + && (exp === fn || !recursive_ref(compressor, def = exp.definition()) + && fn.is_constant_expression(compressor.find_parent(AST_Scope))) && (value = can_flatten_body(stat)) && !fn.contains_this()) { + var replacing = exp === fn || compressor.option("unused") && def.references.length - def.replaced == 1; if (can_substitute_directly()) { var args = self.args.slice(); + var refs = []; args.push(value.clone(true).transform(new TreeTransformer(function(node) { if (node instanceof AST_SymbolRef) { var def = node.definition(); if (fn.variables.get(node.name) !== def) { - if (exp !== fn) def.references.push(node); + refs.push(node); return node; } var index = resolve_index(def); @@ -6267,11 +6289,17 @@ merge(Compressor.prototype, { return arg; })).optimize(compressor); node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node); - if (best_of(compressor, self, node) === node) return node; + if (replacing || best_of(compressor, self, node) === node) { + refs.forEach(function(ref) { + var def = ref.definition(); + def.references.push(ref); + if (replacing) def.replaced++; + }); + return node; + } } var scope, in_loop, level = -1; - if ((exp === fn || compressor.option("unused") && def.references.length - def.replaced == 1) - && can_inject_symbols()) { + if (replacing && can_inject_symbols()) { fn._squeezed = true; if (exp !== fn) fn.parent_scope = exp.scope; var node = make_sequence(self, flatten_fn()).optimize(compressor); @@ -6360,7 +6388,7 @@ merge(Compressor.prototype, { var begin; var in_order = []; var side_effects = false; - value.walk(new TreeWalker(function(node) { + value.walk(new TreeWalker(function(node, descend) { if (abort) return true; if (node instanceof AST_Binary && lazy_op[node.operator] || node instanceof AST_Conditional) { @@ -6386,7 +6414,11 @@ merge(Compressor.prototype, { } return; } - if (node.has_side_effects(compressor)) side_effects = true; + if (node.has_side_effects(compressor)) { + descend(); + side_effects = true; + return true; + } })); if (abort) return; var end = self.args.length; @@ -6395,8 +6427,8 @@ merge(Compressor.prototype, { while (end-- > begin && fn.argnames[end] === in_order.pop()); end++; } - var scope = side_effects && compressor.find_parent(AST_Scope); - return end <= begin || all(self.args.slice(begin, end), side_effects ? function(funarg) { + var scope = side_effects && !in_order && compressor.find_parent(AST_Scope); + return end <= begin || all(self.args.slice(begin, end), scope ? function(funarg) { return funarg.is_constant_expression(scope); } : function(funarg) { return !funarg.has_side_effects(compressor); @@ -6685,7 +6717,7 @@ merge(Compressor.prototype, { var seq = lift_sequence_in_expression(self, compressor); if (seq !== self) return seq.optimize(compressor); } - return self; + return try_evaluate(compressor, self); }); var SIGN_OPS = makePredicate("+ -"); @@ -7832,7 +7864,7 @@ merge(Compressor.prototype, { expression: self.left }); } - return self; + return try_evaluate(compressor, self); function in_try(level, node) { var right = self.right; diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 737697a8..82fb057e 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -62,7 +62,7 @@ collapse_vars_side_effects_1: { expect: { function f1() { var s = "abcdef", i = 2; - console.log.bind(console)(s.charAt(i++), s.charAt(i++), s.charAt(i++), 7); + console.log.bind(console)(s.charAt(i++), s.charAt(i++), s.charAt(4), 7); } function f2() { var s = "abcdef", i = 2; @@ -74,13 +74,14 @@ collapse_vars_side_effects_1: { log = console.log.bind(console), x = s.charAt(i++), y = s.charAt(i++); - log(x, s.charAt(i++), y, 7); + log(x, s.charAt(4), y, 7); } function f4() { - var i = 10, - x = i += 2, - y = i += 3; - console.log.bind(console)(x, i += 4, y, 19); + var i = 10; + i += 2, + i += 3, + i += 4; + console.log.bind(console)(12, 19, 15, 19); } f1(), f2(), f3(), f4(); } diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index db321450..868ad154 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -2485,3 +2485,62 @@ issue_3920: { } expect_stdout: "false" } + +inlined_increment_prefix: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + (function() { + ++a; + })(); + console.log(a += 0); + } + expect: { + var a = 0; + void ++a; + console.log(a += 0); + } + expect_stdout: "1" +} + +inlined_increment_postfix: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + (function() { + a++; + })(); + console.log(a += 0); + } + expect: { + var a = 0; + void a++; + console.log(a += 0); + } + expect_stdout: "1" +} + +compound_assignment_to_property: { + options = { + evaluate: true, + unsafe: true, + } + input: { + 1 + (0..p >>= 0) && console.log("PASS"); + } + expect: { + 1 + (0..p >>= 0), + console.log("PASS"); + } + expect_stdout: "PASS" +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 94852d45..9168e81b 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2724,8 +2724,8 @@ issue_1814_1: { expect: { !function() { !function(a) { - console.log(a++, 42); - }(0); + console.log(0, 42); + }(); }(); } expect_stdout: "0 42" @@ -2751,8 +2751,8 @@ issue_1814_2: { expect: { !function() { !function(a) { - console.log("321", a++); - }(0); + console.log("321", 0); + }(); }(); } expect_stdout: "321 0"