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:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user