From 5d9f1da3abc58bce95dd240bd586bedb4eb04771 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 18 Apr 2017 13:38:42 +0800 Subject: [PATCH] support safe reassignments in `reduce_vars` (#1823) `var a=1;a=2;x(a)` => `x(2)` fix pre-existing issues - reference counting on assignment - walking of anonymous functions - chained assignment --- lib/compress.js | 51 ++++++++++++----- test/compress/collapse_vars.js | 22 +++++++ test/compress/reduce_vars.js | 102 +++++++++++++++++++++++++++------ 3 files changed, 145 insertions(+), 30 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 0dfe2a3c..596b03fa 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -267,7 +267,7 @@ merge(Compressor.prototype, { if (node instanceof AST_SymbolRef) { var d = node.definition(); d.references.push(node); - if (d.fixed === undefined || !is_safe(d) + if (d.fixed === undefined || !safe_to_read(d) || is_modified(node, 0, is_immutable(node.fixed_value()))) { d.fixed = false; } @@ -277,7 +277,7 @@ merge(Compressor.prototype, { } if (node instanceof AST_VarDef) { var d = node.name.definition(); - if (d.fixed == null) { + if (d.fixed === undefined || safe_to_assign(d, node.value)) { if (node.value) { d.fixed = function() { return node.value; @@ -297,7 +297,8 @@ merge(Compressor.prototype, { && node.operator == "=" && node.left instanceof AST_SymbolRef) { var d = node.left.definition(); - if (HOP(safe_ids, d.id) && d.fixed == null) { + if (safe_to_assign(d, node.right)) { + d.references.push(node.left); d.fixed = function() { return node.right; }; @@ -309,7 +310,7 @@ merge(Compressor.prototype, { } if (node instanceof AST_Defun) { var d = node.name.definition(); - if (!toplevel && d.global || is_safe(d)) { + if (!toplevel && d.global || safe_to_read(d)) { d.fixed = false; } else { d.fixed = node; @@ -321,13 +322,12 @@ merge(Compressor.prototype, { safe_ids = save_ids; return true; } - var iife; - if (node instanceof AST_Function - && (iife = tw.parent()) instanceof AST_Call - && iife.expression === node) { - if (node.name) { - node.name.definition().fixed = node; - } else { + if (node instanceof AST_Function) { + push(); + var iife; + if (!node.name + && (iife = tw.parent()) instanceof AST_Call + && iife.expression === node) { // Virtually turn IIFE parameters into variable definitions: // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() // So existing transformation rules can work on them. @@ -339,6 +339,9 @@ merge(Compressor.prototype, { mark(d, true); }); } + descend(); + pop(); + return true; } if (node instanceof AST_Binary && (node.operator == "&&" || node.operator == "||")) { @@ -385,11 +388,19 @@ merge(Compressor.prototype, { } if (node instanceof AST_For) { if (node.init) node.init.walk(tw); + if (node.condition) { + push(); + node.condition.walk(tw); + pop(); + } push(); - if (node.condition) node.condition.walk(tw); node.body.walk(tw); - if (node.step) node.step.walk(tw); pop(); + if (node.step) { + push(); + node.step.walk(tw); + pop(); + } return true; } if (node instanceof AST_ForIn) { @@ -426,7 +437,7 @@ merge(Compressor.prototype, { safe_ids[def.id] = safe; } - function is_safe(def) { + function safe_to_read(def) { if (safe_ids[def.id]) { if (def.fixed == null) { var orig = def.orig[0]; @@ -437,6 +448,18 @@ merge(Compressor.prototype, { } } + function safe_to_assign(def, value) { + if (!HOP(safe_ids, def.id)) return false; + if (!safe_to_read(def)) return false; + if (def.fixed === false) return false; + if (def.fixed != null && (!value || def.references.length > 0)) return false; + return !def.orig.some(function(sym) { + return sym instanceof AST_SymbolConst + || sym instanceof AST_SymbolDefun + || sym instanceof AST_SymbolLambda; + }); + } + function push() { safe_ids = Object.create(safe_ids); } diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 2264783d..c01572dd 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1592,3 +1592,25 @@ var_side_effects_3: { } expect_stdout: true } + +reduce_vars_assign: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + !function() { + var a = 1; + a = [].length, + console.log(a); + }(); + } + expect: { + !function() { + var a = 1; + a = [].length, + console.log(a); + }(); + } + expect_stdout: "0" +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index ad2c90bc..c5f26904 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -66,7 +66,7 @@ modified: { conditionals : true, evaluate : true, reduce_vars : true, - unused : true + unused : true, } input: { function f0() { @@ -136,12 +136,11 @@ modified: { } function f2() { - var b = 2; - b = 3; - console.log(1 + b); - console.log(b + 3); + 3; console.log(4); - console.log(1 + b + 3); + console.log(6); + console.log(4); + console.log(7); } function f3() { @@ -375,12 +374,11 @@ passes: { } expect: { function f() { - var b = 2; - b = 3; - console.log(1 + b); - console.log(b + 3); + 3; console.log(4); - console.log(1 + b + 3); + console.log(6); + console.log(4); + console.log(7); } } } @@ -573,7 +571,7 @@ inner_var_label: { } } -inner_var_for: { +inner_var_for_1: { options = { evaluate: true, reduce_vars: true, @@ -602,6 +600,29 @@ inner_var_for: { } } +inner_var_for_2: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = 1; + for (var b = 1; --b;) var a = 2; + console.log(a); + }(); + } + expect: { + !function() { + a = 1; + for (var b = 1; --b;) var a = 2; + console.log(a); + }(); + } + expect_stdout: "1" +} + inner_var_for_in_1: { options = { evaluate: true, @@ -1828,10 +1849,7 @@ redefine_farg_3: { console.log(function(a) { var a; return typeof a; - }([]), "number", function(a) { - var a = void 0; - return typeof a; - }([])); + }([]), "number", "undefined"); } expect_stdout: "object number undefined" } @@ -2115,6 +2133,27 @@ var_assign_5: { expect_stdout: "2 undefined" } +var_assign_6: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = function(){}(a = 1); + console.log(a); + }(); + } + expect: { + !function() { + var a = function(){}(a = 1); + console.log(a); + }(); + } + expect_stdout: "undefined" +} + immutable: { options = { evaluate: true, @@ -2263,3 +2302,34 @@ cond_assign: { } expect_stdout: "undefined" } + +iife_assign: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = 1, b = 0; + !function() { + b++; + return; + a = 2; + }(); + console.log(a); + }(); + } + expect: { + !function() { + var a = 1, b = 0; + !function() { + b++; + return; + a = 2; + }(); + console.log(a); + }(); + } + expect_stdout: "1" +} -- 2.34.1