From 168ae747ad8c8c48a0318eaaffd25e084521fb60 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 28 Nov 2019 03:57:10 +0800 Subject: [PATCH] enhance `collapse_vars` (#3611) --- lib/compress.js | 35 ++++-- test/compress/collapse_vars.js | 187 ++++++++++++++++++++++++++++++++- 2 files changed, 209 insertions(+), 13 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 19fba0b6..686a8d42 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1415,7 +1415,9 @@ merge(Compressor.prototype, { function extract_candidates(expr) { hit_stack.push(expr); - if (expr instanceof AST_Assign) { + if (expr instanceof AST_Array) { + expr.elements.forEach(extract_candidates); + } else if (expr instanceof AST_Assign) { candidates.push(hit_stack.slice()); extract_candidates(expr.left); extract_candidates(expr.right); @@ -1462,6 +1464,14 @@ merge(Compressor.prototype, { if (expr.alternative && !(expr.alternative instanceof AST_Block)) { extract_candidates(expr.alternative); } + } else if (expr instanceof AST_Object) { + expr.properties.forEach(function(prop) { + if (prop instanceof AST_ObjectKeyVal) { + hit_stack.push(prop); + extract_candidates(prop.value); + hit_stack.pop(); + } + }); } else if (expr instanceof AST_Sequence) { expr.expressions.forEach(extract_candidates); } else if (expr instanceof AST_SimpleStatement) { @@ -1492,6 +1502,7 @@ merge(Compressor.prototype, { function find_stop(node, level) { var parent = scanner.parent(level); + if (parent instanceof AST_Array) return node; if (parent instanceof AST_Assign) return node; if (parent instanceof AST_Binary) return node; if (parent instanceof AST_Call) return node; @@ -1501,6 +1512,7 @@ merge(Compressor.prototype, { if (parent instanceof AST_Exit) return node; if (parent instanceof AST_If) return node; if (parent instanceof AST_IterationStatement) return node; + if (parent instanceof AST_ObjectKeyVal) return node; if (parent instanceof AST_PropAccess) return node; if (parent instanceof AST_Sequence) { return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1); @@ -1516,6 +1528,7 @@ merge(Compressor.prototype, { var parent = scanner.parent(level); if (is_last_node(node, parent)) return node; if (in_conditional(node, parent)) return node; + if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1); if (parent instanceof AST_Assign) return find_stop_unused(parent, level + 1); if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1); if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1); @@ -1525,6 +1538,7 @@ merge(Compressor.prototype, { if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1); if (parent instanceof AST_If) return find_stop_unused(parent, level + 1); if (parent instanceof AST_IterationStatement) return node; + if (parent instanceof AST_ObjectKeyVal) return find_stop_unused(scanner.parent(level + 1), level + 2); if (parent instanceof AST_PropAccess) { var exp = parent.expression; if (exp === node) return find_stop_unused(parent, level + 1); @@ -1668,20 +1682,21 @@ merge(Compressor.prototype, { var found = false; return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) { if (found) return node; + if (node instanceof AST_Scope) return node; if (node !== expr && node.body !== expr) return; + found = true; if (node instanceof AST_VarDef) { - found = true; node.value = null; return node; } - if (in_list) { - found = true; - return MAP.skip; - } - if (!this.parent()) { - found = true; - return null; - } + var parent = this.parent(); + if (!parent) return in_list ? MAP.skip : null; + if (parent instanceof AST_Sequence) return MAP.skip; + var value = expr; + do { + value = get_rvalue(value); + } while (value instanceof AST_Assign); + return value; }, function(node) { if (node instanceof AST_Sequence) switch (node.expressions.length) { case 0: return null; diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 0f1e59a3..175ab9c2 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -669,7 +669,7 @@ collapse_vars_throw: { expect_stdout: "13" } -collapse_vars_switch: { +collapse_vars_switch_1: { options = { booleans: true, collapse_vars: true, @@ -721,6 +721,31 @@ collapse_vars_switch: { } } +collapse_vars_switch_2: { + options = { + collapse_vars: true, + } + input: { + var c = 0; + (function(b) { + switch (b && [ b = 0, (c++, 0) ]) { + case c = 1 + c: + } + })(); + console.log(c); + } + expect: { + var c = 0; + (function(b) { + switch (b && [ b = 0, (c++, 0) ]) { + case c = 1 + c: + } + })(); + console.log(c); + } + expect_stdout: "1" +} + collapse_vars_assignment: { options = { booleans: true, @@ -1234,7 +1259,7 @@ collapse_vars_try: { } } -collapse_vars_array: { +collapse_vars_array_1: { options = { booleans: true, collapse_vars: true, @@ -1280,7 +1305,58 @@ collapse_vars_array: { } } -collapse_vars_object: { +collapse_vars_array_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return [ (b = a, b.g()) ]; + } + console.log(f({ + g: function() { + return "PASS"; + } + })[0]); + } + expect: { + function f(a) { + return [ a.g() ]; + } + console.log(f({ + g: function() { + return "PASS"; + } + })[0]); + } + expect_stdout: "PASS" +} + +collapse_vars_array_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return [ b = a, b, b ]; + } + console.log(f().length); + } + expect: { + function f(a) { + var b; + return [ b = a, b, b ]; + } + console.log(f().length); + } + expect_stdout: "3" +} + +collapse_vars_object_1: { options = { booleans: true, collapse_vars: true, @@ -1360,6 +1436,69 @@ collapse_vars_object: { } } +collapse_vars_object_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return { + p: (b = a, b.g()) + }; + } + console.log(f({ + g: function() { + return "PASS"; + } + }).p); + } + expect: { + function f(a) { + return { + p: a.g() + }; + } + console.log(f({ + g: function() { + return "PASS"; + } + }).p); + } + expect_stdout: "PASS" +} + +collapse_vars_object_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return { + p: b = a, + q: b, + r: b, + }; + } + console.log(f("PASS").r); + } + expect: { + function f(a) { + var b; + return { + p: b = a, + q: b, + r: b, + }; + } + console.log(f("PASS").r); + } + expect_stdout: "PASS" +} + collapse_vars_eval_and_with: { options = { booleans: true, @@ -6624,3 +6763,45 @@ local_value_replacement: { } expect_stdout: "PASS" } + +array_in_object_1: { + options = { + collapse_vars: true, + } + input: { + var a = 2; + console.log({ + p: [ a, a-- ], + q: a, + }.q, a); + } + expect: { + var a = 2; + console.log({ + p: [ a, a-- ], + q: a, + }.q, a); + } + expect_stdout: "1 1" +} + +array_in_object_2: { + options = { + collapse_vars: true, + } + input: { + var a = 2; + console.log({ + p: [ a, (a--, 42) ], + q: a, + }.q, a); + } + expect: { + var a = 2; + console.log({ + p: [ a, 42 ], + q: --a, + }.q, a); + } + expect_stdout: "1 1" +} -- 2.34.1