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
This commit is contained in:
Alex Lam S.L
2017-04-18 13:38:42 +08:00
committed by GitHub
parent d1aa09c5c7
commit 5d9f1da3ab
3 changed files with 145 additions and 30 deletions

View File

@@ -267,7 +267,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var d = node.definition(); var d = node.definition();
d.references.push(node); 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()))) { || is_modified(node, 0, is_immutable(node.fixed_value()))) {
d.fixed = false; d.fixed = false;
} }
@@ -277,7 +277,7 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_VarDef) { if (node instanceof AST_VarDef) {
var d = node.name.definition(); var d = node.name.definition();
if (d.fixed == null) { if (d.fixed === undefined || safe_to_assign(d, node.value)) {
if (node.value) { if (node.value) {
d.fixed = function() { d.fixed = function() {
return node.value; return node.value;
@@ -297,7 +297,8 @@ merge(Compressor.prototype, {
&& node.operator == "=" && node.operator == "="
&& node.left instanceof AST_SymbolRef) { && node.left instanceof AST_SymbolRef) {
var d = node.left.definition(); 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() { d.fixed = function() {
return node.right; return node.right;
}; };
@@ -309,7 +310,7 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_Defun) { if (node instanceof AST_Defun) {
var d = node.name.definition(); var d = node.name.definition();
if (!toplevel && d.global || is_safe(d)) { if (!toplevel && d.global || safe_to_read(d)) {
d.fixed = false; d.fixed = false;
} else { } else {
d.fixed = node; d.fixed = node;
@@ -321,13 +322,12 @@ merge(Compressor.prototype, {
safe_ids = save_ids; safe_ids = save_ids;
return true; return true;
} }
var iife; if (node instanceof AST_Function) {
if (node instanceof AST_Function push();
&& (iife = tw.parent()) instanceof AST_Call var iife;
&& iife.expression === node) { if (!node.name
if (node.name) { && (iife = tw.parent()) instanceof AST_Call
node.name.definition().fixed = node; && iife.expression === node) {
} else {
// Virtually turn IIFE parameters into variable definitions: // Virtually turn IIFE parameters into variable definitions:
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})() // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them. // So existing transformation rules can work on them.
@@ -339,6 +339,9 @@ merge(Compressor.prototype, {
mark(d, true); mark(d, true);
}); });
} }
descend();
pop();
return true;
} }
if (node instanceof AST_Binary if (node instanceof AST_Binary
&& (node.operator == "&&" || node.operator == "||")) { && (node.operator == "&&" || node.operator == "||")) {
@@ -385,11 +388,19 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_For) { if (node instanceof AST_For) {
if (node.init) node.init.walk(tw); if (node.init) node.init.walk(tw);
if (node.condition) {
push();
node.condition.walk(tw);
pop();
}
push(); push();
if (node.condition) node.condition.walk(tw);
node.body.walk(tw); node.body.walk(tw);
if (node.step) node.step.walk(tw);
pop(); pop();
if (node.step) {
push();
node.step.walk(tw);
pop();
}
return true; return true;
} }
if (node instanceof AST_ForIn) { if (node instanceof AST_ForIn) {
@@ -426,7 +437,7 @@ merge(Compressor.prototype, {
safe_ids[def.id] = safe; safe_ids[def.id] = safe;
} }
function is_safe(def) { function safe_to_read(def) {
if (safe_ids[def.id]) { if (safe_ids[def.id]) {
if (def.fixed == null) { if (def.fixed == null) {
var orig = def.orig[0]; 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() { function push() {
safe_ids = Object.create(safe_ids); safe_ids = Object.create(safe_ids);
} }

View File

@@ -1592,3 +1592,25 @@ var_side_effects_3: {
} }
expect_stdout: true 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"
}

View File

@@ -66,7 +66,7 @@ modified: {
conditionals : true, conditionals : true,
evaluate : true, evaluate : true,
reduce_vars : true, reduce_vars : true,
unused : true unused : true,
} }
input: { input: {
function f0() { function f0() {
@@ -136,12 +136,11 @@ modified: {
} }
function f2() { function f2() {
var b = 2; 3;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4); console.log(4);
console.log(1 + b + 3); console.log(6);
console.log(4);
console.log(7);
} }
function f3() { function f3() {
@@ -375,12 +374,11 @@ passes: {
} }
expect: { expect: {
function f() { function f() {
var b = 2; 3;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4); 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 = { options = {
evaluate: true, evaluate: true,
reduce_vars: 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: { inner_var_for_in_1: {
options = { options = {
evaluate: true, evaluate: true,
@@ -1828,10 +1849,7 @@ redefine_farg_3: {
console.log(function(a) { console.log(function(a) {
var a; var a;
return typeof a; return typeof a;
}([]), "number", function(a) { }([]), "number", "undefined");
var a = void 0;
return typeof a;
}([]));
} }
expect_stdout: "object number undefined" expect_stdout: "object number undefined"
} }
@@ -2115,6 +2133,27 @@ var_assign_5: {
expect_stdout: "2 undefined" 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: { immutable: {
options = { options = {
evaluate: true, evaluate: true,
@@ -2263,3 +2302,34 @@ cond_assign: {
} }
expect_stdout: "undefined" 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"
}