improve reduce_vars (#3112)

fixes #3110
This commit is contained in:
Alex Lam S.L
2018-05-02 15:11:45 +08:00
committed by GitHub
parent 6fcbd5e217
commit 1a314e9f60
3 changed files with 191 additions and 42 deletions

View File

@@ -374,6 +374,15 @@ merge(Compressor.prototype, {
}); });
} }
function walk_defuns(tw, compressor, node) {
node.functions.each(function(def) {
if (def.init instanceof AST_Defun && !(def.id in tw.defun_ids)) {
tw.defun_ids[def.id] = true;
def.init.walk(tw);
}
});
}
function push(tw) { function push(tw) {
tw.safe_ids = Object.create(tw.safe_ids); tw.safe_ids = Object.create(tw.safe_ids);
} }
@@ -398,7 +407,7 @@ merge(Compressor.prototype, {
return def.fixed instanceof AST_Defun; return def.fixed instanceof AST_Defun;
} }
function safe_to_assign(tw, def, value) { function safe_to_assign(tw, def, scope, value) {
if (def.fixed === undefined) return true; if (def.fixed === undefined) return true;
if (def.fixed === null && def.safe_ids) { if (def.fixed === null && def.safe_ids) {
def.safe_ids[def.id] = false; def.safe_ids[def.id] = false;
@@ -409,6 +418,9 @@ merge(Compressor.prototype, {
if (!safe_to_read(tw, def)) return false; if (!safe_to_read(tw, def)) return false;
if (def.fixed === false) return false; if (def.fixed === false) return false;
if (def.fixed != null && (!value || def.references.length > def.assignments)) return false; if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
if (def.fixed instanceof AST_Defun) {
return value instanceof AST_Node && def.fixed.parent_scope === scope;
}
return all(def.orig, function(sym) { return all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolDefun return !(sym instanceof AST_SymbolDefun
|| sym instanceof AST_SymbolLambda); || sym instanceof AST_SymbolLambda);
@@ -468,6 +480,7 @@ merge(Compressor.prototype, {
reset_variables(tw, compressor, this); reset_variables(tw, compressor, this);
descend(); descend();
pop(tw); pop(tw);
walk_defuns(tw, compressor, this);
return true; return true;
}); });
def(AST_Assign, function(tw) { def(AST_Assign, function(tw) {
@@ -476,7 +489,7 @@ merge(Compressor.prototype, {
var d = node.left.definition(); var d = node.left.definition();
var fixed = d.fixed; var fixed = d.fixed;
if (!fixed && node.operator != "=") return; if (!fixed && node.operator != "=") return;
if (!safe_to_assign(tw, d, node.right)) return; if (!safe_to_assign(tw, d, node.left.scope, node.right)) return;
d.references.push(node.left); d.references.push(node.left);
d.assignments++; d.assignments++;
if (node.operator != "=") d.chained = true; if (node.operator != "=") d.chained = true;
@@ -528,12 +541,15 @@ merge(Compressor.prototype, {
return true; return true;
}); });
def(AST_Defun, function(tw, descend, compressor) { def(AST_Defun, function(tw, descend, compressor) {
var id = this.name.definition().id;
if (!tw.defun_ids[id]) return true;
tw.defun_ids[id] = false;
this.inlined = false; this.inlined = false;
var save_ids = tw.safe_ids; push(tw);
tw.safe_ids = Object.create(null);
reset_variables(tw, compressor, this); reset_variables(tw, compressor, this);
descend(); descend();
tw.safe_ids = save_ids; pop(tw);
walk_defuns(tw, compressor, this);
return true; return true;
}); });
def(AST_Do, function(tw) { def(AST_Do, function(tw) {
@@ -606,6 +622,7 @@ merge(Compressor.prototype, {
} }
descend(); descend();
pop(tw); pop(tw);
walk_defuns(tw, compressor, node);
return true; return true;
}); });
def(AST_If, function(tw) { def(AST_If, function(tw) {
@@ -659,12 +676,21 @@ merge(Compressor.prototype, {
} }
} }
mark_escaped(tw, d, this.scope, this, value, 0, 1); mark_escaped(tw, d, this.scope, this, value, 0, 1);
if (d.scope === this.scope && !tw.in_loop && d.fixed instanceof AST_Defun && !(d.id in tw.defun_ids)) {
tw.defun_ids[d.id] = true;
d.fixed.walk(tw);
}
}); });
def(AST_Toplevel, function(tw, descend, compressor) { def(AST_Toplevel, function(tw, descend, compressor) {
this.globals.each(function(def) { this.globals.each(function(def) {
reset_def(compressor, def); reset_def(compressor, def);
}); });
push(tw);
reset_variables(tw, compressor, this); reset_variables(tw, compressor, this);
descend();
pop(tw);
walk_defuns(tw, compressor, this);
return true;
}); });
def(AST_Try, function(tw) { def(AST_Try, function(tw) {
push(tw); push(tw);
@@ -681,12 +707,13 @@ merge(Compressor.prototype, {
def(AST_Unary, function(tw, descend) { def(AST_Unary, function(tw, descend) {
var node = this; var node = this;
if (node.operator != "++" && node.operator != "--") return; if (node.operator != "++" && node.operator != "--") return;
if (!(node.expression instanceof AST_SymbolRef)) return; var exp = node.expression;
var d = node.expression.definition(); if (!(exp instanceof AST_SymbolRef)) return;
var d = exp.definition();
var fixed = d.fixed; var fixed = d.fixed;
if (!fixed) return; if (!fixed) return;
if (!safe_to_assign(tw, d, true)) return; if (!safe_to_assign(tw, d, exp.scope, true)) return;
d.references.push(node.expression); d.references.push(exp);
d.assignments++; d.assignments++;
d.chained = true; d.chained = true;
d.fixed = function() { d.fixed = function() {
@@ -708,7 +735,7 @@ merge(Compressor.prototype, {
var node = this; var node = this;
var d = node.name.definition(); var d = node.name.definition();
if (node.value) { if (node.value) {
if (safe_to_assign(tw, d, node.value)) { if (safe_to_assign(tw, d, node.name.scope, node.value)) {
d.fixed = function() { d.fixed = function() {
return node.value; return node.value;
}; };
@@ -736,19 +763,24 @@ merge(Compressor.prototype, {
}); });
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) { AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
var reduce_vars = compressor.option("reduce_vars"); var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
var tw = new TreeWalker(function(node, descend) { node._squeezed = false;
node._optimized = false;
return node.reduce_vars(tw, descend, compressor);
} : function(node) {
node._squeezed = false; node._squeezed = false;
node._optimized = false; node._optimized = false;
if (reduce_vars) return node.reduce_vars(tw, descend, compressor);
}); });
// Flow control for visiting `AST_Defun`s
tw.defun_ids = Object.create(null);
// Record the loop body in which `AST_SymbolDeclaration` is first encountered
tw.in_loop = null;
tw.loop_ids = Object.create(null);
// Stack of look-up tables to keep track of whether a `SymbolDef` has been // Stack of look-up tables to keep track of whether a `SymbolDef` has been
// properly assigned before use: // properly assigned before use:
// - `push()` & `pop()` when visiting conditional branches // - `push()` & `pop()` when visiting conditional branches
// - backup & restore via `save_ids` when visiting out-of-order sections // - backup & restore via `save_ids` when visiting out-of-order sections
tw.safe_ids = Object.create(null); tw.safe_ids = Object.create(null);
tw.in_loop = null;
tw.loop_ids = Object.create(null);
this.walk(tw); this.walk(tw);
}); });

View File

@@ -1476,18 +1476,18 @@ defun_redefine: {
}; };
return g() + h(); return g() + h();
} }
console.log(f());
} }
expect: { expect: {
function f() { function f() {
function g() { (function() {
return 1;
}
g = function() {
return 3; return 3;
}; });
return g() + 2; return 3 + 2;
} }
console.log(f());
} }
expect_stdout: "5"
} }
func_inline: { func_inline: {
@@ -1527,23 +1527,37 @@ func_modified: {
} }
input: { input: {
function f(a) { function f(a) {
function a() { return 1; } function a() {
function b() { return 2; } return 1;
function c() { return 3; } }
function b() {
return 2;
}
function c() {
return 3;
}
b.inject = []; b.inject = [];
c = function() { return 4; }; c = function() {
return 4;
};
return a() + b() + c(); return a() + b() + c();
} }
console.log(f());
} }
expect: { expect: {
function f(a) { function f(a) {
function b() { return 2; } function b() {
function c() { return 3; } return 2;
}
b.inject = []; b.inject = [];
c = function() { return 4; }; (function() {
return 1 + 2 + c(); return 4;
});
return 1 + 2 + 4;
} }
console.log(f());
} }
expect_stdout: "7"
} }
defun_label: { defun_label: {
@@ -5054,9 +5068,7 @@ defun_var_1: {
console.log(typeof a, typeof b); console.log(typeof a, typeof b);
} }
expect: { expect: {
var a = 42; console.log("number", "function");
function a() {}
console.log(typeof a, "function");
} }
expect_stdout: "number function" expect_stdout: "number function"
} }
@@ -5076,9 +5088,7 @@ defun_var_2: {
console.log(typeof a, typeof b); console.log(typeof a, typeof b);
} }
expect: { expect: {
function a() {} console.log("number", "function");
var a = 42;
console.log(typeof a, "function");
} }
expect_stdout: "number function" expect_stdout: "number function"
} }
@@ -5710,3 +5720,116 @@ issue_3068_2: {
} }
expect_stdout: true expect_stdout: true
} }
issue_3110_1: {
options = {
conditionals: true,
evaluate: true,
inline: true,
passes: 3,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
var isDev = true;
var obj = {
foo: foo
};
console.log(foo());
console.log(obj.foo());
})();
}
expect: {
console.log("foo"),
console.log("foo");
}
expect_stdout: [
"foo",
"foo",
]
}
issue_3110_2: {
options = {
conditionals: true,
evaluate: true,
inline: true,
passes: 4,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
var isDev = true;
console.log(foo());
var obj = {
foo: foo
};
console.log(obj.foo());
})();
}
expect: {
console.log("foo"),
console.log("foo");
}
expect_stdout: [
"foo",
"foo",
]
}
issue_3110_3: {
options = {
conditionals: true,
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
console.log(foo());
var isDev = true;
var obj = {
foo: foo
};
console.log(obj.foo());
})();
}
expect: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
console.log(foo());
var isDev = true;
var obj = {
foo: foo
};
console.log(obj.foo());
})();
}
expect_stdout: [
"bar",
"foo",
]
}

View File

@@ -90,17 +90,11 @@ typeof_defun_1: {
"function" == typeof h && h(); "function" == typeof h && h();
} }
expect: { expect: {
function g() {
h = 42;
console.log("NOPE");
}
function h() { function h() {
console.log("YUP"); console.log("YUP");
} }
g = 42;
console.log("YES"); console.log("YES");
"function" == typeof g && g(); h();
"function" == typeof h && h();
} }
expect_stdout: [ expect_stdout: [
"YES", "YES",