From 3acb5a329e47b753248a3568c24794140ccf50a0 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 2 Nov 2021 11:33:24 +0800 Subject: [PATCH] enhance `join_vars` (#5162) --- lib/compress.js | 64 +++++++++++++++++++--------- test/compress/join_vars.js | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 19 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index edc45b88..dbd75a6b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3476,15 +3476,26 @@ merge(Compressor.prototype, { var exprs = extract_exprs(body); if (!exprs) return; var trimmed = false; - for (var i = exprs.length - 1; --i >= 0;) { + for (var i = exprs.length; --i >= 0;) { var expr = exprs[i]; - if (!(expr instanceof AST_Assign)) continue; - if (expr.operator != "=") continue; - if (!(expr.left instanceof AST_SymbolRef)) continue; - var tail = exprs.slice(i + 1); + if (!can_trim(expr)) continue; + var tail; + if (expr.left instanceof AST_SymbolRef) { + tail = exprs.slice(i + 1); + } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) { + tail = exprs.slice(i + 1); + var flattened = expr.clone(); + expr = expr.left.expression; + flattened.left = flattened.left.clone(); + flattened.left.expression = expr.left.clone(); + tail.unshift(flattened); + } else { + continue; + } + if (tail.length == 0) continue; if (!trim_assigns(expr.left, expr.right, tail)) continue; trimmed = true; - exprs = exprs.slice(0, i + 1).concat(tail); + exprs = exprs.slice(0, i).concat(expr, tail); } if (defn instanceof AST_Definitions) { keep = keep || 0; @@ -3498,6 +3509,10 @@ merge(Compressor.prototype, { if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true; } return trimmed && exprs; + + function can_trim(node) { + return node instanceof AST_Assign && node.operator == "="; + } } function merge_assigns(prev, defn) { @@ -3554,22 +3569,35 @@ merge(Compressor.prototype, { } function trim_assigns(name, value, exprs) { + var names = Object.create(null); + names[name.name] = true; + while (value instanceof AST_Assign && value.operator == "=") { + if (value.left instanceof AST_SymbolRef) names[value.left.name] = true; + value = value.right; + } if (!(value instanceof AST_Object)) return; var trimmed = false; do { - var node = exprs[0]; - if (!(node instanceof AST_Assign)) break; - if (node.operator != "=") break; - if (!(node.left instanceof AST_PropAccess)) break; + if (!try_join(exprs[0])) break; + exprs.shift(); + trimmed = true; + } while (exprs.length); + return trimmed; + + function try_join(node) { + if (!(node instanceof AST_Assign)) return; + if (node.operator != "=") return; + if (!(node.left instanceof AST_PropAccess)) return; var sym = node.left.expression; - if (!(sym instanceof AST_SymbolRef)) break; - if (name.name != sym.name) break; - if (!node.right.is_constant_expression(scope)) break; + if (!(sym instanceof AST_SymbolRef)) return; + if (!names[sym.name]) return; + if (!node.right.is_constant_expression(scope)) return; var prop = node.left.property; if (prop instanceof AST_Node) { + if (try_join(prop)) prop = node.left.property = prop.right; prop = prop.evaluate(compressor); } - if (prop instanceof AST_Node) break; + if (prop instanceof AST_Node) return; prop = "" + prop; var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) { var key = node.key; @@ -3581,15 +3609,13 @@ merge(Compressor.prototype, { } return key !== "__proto__"; }; - if (!all(value.properties, diff)) break; + if (!all(value.properties, diff)) return; value.properties.push(make_node(AST_ObjectKeyVal, node, { key: prop, value: node.right })); - exprs.shift(); - trimmed = true; - } while (exprs.length); - return trimmed; + return true; + } } function join_consecutive_vars(statements) { diff --git a/test/compress/join_vars.js b/test/compress/join_vars.js index 65e18f88..f7dd4ae3 100644 --- a/test/compress/join_vars.js +++ b/test/compress/join_vars.js @@ -489,6 +489,91 @@ join_object_assignments_regex: { expect_stdout: "1" } +chained_assignments: { + options = { + join_vars: true, + } + input: { + var a, b = a = {}; + b.p = "PASS"; + console.log(a.p); + } + expect: { + var a, b = a = { + p: "PASS", + }; + console.log(a.p); + } + expect_stdout: "PASS" +} + +folded_assignments: { + options = { + evaluate: true, + join_vars: true, + } + input: { + var a = {}; + a[a.PASS = 42] = "PASS"; + console.log(a[42], a.PASS); + } + expect: { + var a = { + PASS: 42, + 42: "PASS", + }; + console.log(a[42], a.PASS); + } + expect_stdout: "PASS 42" +} + +inlined_assignments: { + options = { + join_vars: true, + unused: true, + } + input: { + var a; + (a = {}).p = "PASS"; + console.log(a.p); + } + expect: { + var a = { + p: "PASS", + }; + console.log(a.p); + } + expect_stdout: "PASS" +} + +typescript_enum: { + rename = true + options = { + assignments: true, + evaluate: true, + hoist_props: true, + inline: true, + join_vars: true, + passes: 4, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var Enum; + (function (Enum) { + Enum[Enum.PASS = 42] = "PASS"; + })(Enum || (Enum = {})); + console.log(Enum[42], Enum.PASS); + } + expect: { + console.log("PASS", 42); + } + expect_stdout: "PASS 42" +} + issue_2816: { options = { join_vars: true,