perform reduce_vars on safe literals (#2351)

- constant expression
- single reference
- same scope
- not across loop body
This commit is contained in:
Alex Lam S.L
2017-10-09 12:25:06 +08:00
committed by GitHub
parent 1abe14296e
commit b810e2f8da
5 changed files with 286 additions and 36 deletions

View File

@@ -297,6 +297,8 @@ merge(Compressor.prototype, {
if (node instanceof AST_SymbolRef) d.references.push(node); if (node instanceof AST_SymbolRef) d.references.push(node);
d.fixed = false; d.fixed = false;
}); });
var in_loop = null;
var loop_ids = Object.create(null);
var tw = new TreeWalker(function(node, descend) { var tw = new TreeWalker(function(node, descend) {
node._squeezed = false; node._squeezed = false;
node._optimized = false; node._optimized = false;
@@ -307,7 +309,7 @@ merge(Compressor.prototype, {
var d = node.definition(); var d = node.definition();
d.references.push(node); d.references.push(node);
if (d.fixed === undefined || !safe_to_read(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))) {
d.fixed = false; d.fixed = false;
} else { } else {
var parent = tw.parent(); var parent = tw.parent();
@@ -329,6 +331,7 @@ merge(Compressor.prototype, {
d.fixed = function() { d.fixed = function() {
return node.value; return node.value;
}; };
loop_ids[d.id] = in_loop;
mark(d, false); mark(d, false);
descend(); descend();
} else { } else {
@@ -384,6 +387,7 @@ merge(Compressor.prototype, {
d.fixed = function() { d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife); return iife.args[i] || make_node(AST_Undefined, iife);
}; };
loop_ids[d.id] = in_loop;
mark(d, true); mark(d, true);
} else { } else {
d.fixed = false; d.fixed = false;
@@ -431,10 +435,13 @@ merge(Compressor.prototype, {
return true; return true;
} }
if (node instanceof AST_DWLoop) { if (node instanceof AST_DWLoop) {
var saved_loop = in_loop;
in_loop = node;
push(); push();
node.condition.walk(tw); node.condition.walk(tw);
node.body.walk(tw); node.body.walk(tw);
pop(); pop();
in_loop = saved_loop;
return true; return true;
} }
if (node instanceof AST_LabeledStatement) { if (node instanceof AST_LabeledStatement) {
@@ -445,6 +452,8 @@ 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);
var saved_loop = in_loop;
in_loop = node;
if (node.condition) { if (node.condition) {
push(); push();
node.condition.walk(tw); node.condition.walk(tw);
@@ -458,14 +467,18 @@ merge(Compressor.prototype, {
node.step.walk(tw); node.step.walk(tw);
pop(); pop();
} }
in_loop = saved_loop;
return true; return true;
} }
if (node instanceof AST_ForIn) { if (node instanceof AST_ForIn) {
node.init.walk(suppressor); node.init.walk(suppressor);
node.object.walk(tw); node.object.walk(tw);
var saved_loop = in_loop;
in_loop = node;
push(); push();
node.body.walk(tw); node.body.walk(tw);
pop(); pop();
in_loop = saved_loop;
return true; return true;
} }
if (node instanceof AST_Try) { if (node instanceof AST_Try) {
@@ -535,10 +548,23 @@ merge(Compressor.prototype, {
} }
def.references = []; def.references = [];
def.should_replace = undefined; def.should_replace = undefined;
def.single_use = undefined;
} }
function is_immutable(value) { function is_immutable(node) {
return value && value.is_constant() || value instanceof AST_Lambda; var value = node.fixed_value();
if (!value) return false;
if (value.is_constant()) return true;
if (compressor.option("unused")) {
var d = node.definition();
if (d.single_use === undefined) {
d.single_use = loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression();
}
if (d.references.length == 1 && d.single_use) return true;
}
return value instanceof AST_Lambda;
} }
function is_modified(node, level, immutable) { function is_modified(node, level, immutable) {
@@ -2115,6 +2141,22 @@ merge(Compressor.prototype, {
} }
def(AST_Node, return_false); def(AST_Node, return_false);
def(AST_Constant, return_true); def(AST_Constant, return_true);
def(AST_Function, function(){
var self = this;
var result = true;
self.walk(new TreeWalker(function(node) {
if (!result) return true;
if (node instanceof AST_SymbolRef) {
var def = node.definition();
if (self.enclosed.indexOf(def) >= 0
&& self.variables.get(def.name) !== def) {
result = false;
return true;
}
}
}));
return result;
});
def(AST_Unary, function(){ def(AST_Unary, function(){
return this.expression.is_constant_expression(); return this.expression.is_constant_expression();
}); });
@@ -4078,12 +4120,13 @@ merge(Compressor.prototype, {
d.fixed = fixed = make_node(AST_Function, fixed, fixed); d.fixed = fixed = make_node(AST_Function, fixed, fixed);
} }
if (compressor.option("unused") if (compressor.option("unused")
&& fixed instanceof AST_Function
&& d.references.length == 1 && d.references.length == 1
&& (d.single_use || fixed instanceof AST_Function
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
&& !d.scope.uses_eval && !d.scope.uses_eval
&& compressor.find_parent(AST_Scope) === fixed.parent_scope) { && compressor.find_parent(AST_Scope) === fixed.parent_scope)) {
return fixed.clone(true); var value = fixed.optimize(compressor);
return value === fixed ? fixed.clone(true) : value;
} }
if (compressor.option("evaluate") && fixed) { if (compressor.option("evaluate") && fixed) {
if (d.should_replace === undefined) { if (d.should_replace === undefined) {

View File

@@ -1268,22 +1268,21 @@ collapse_vars_short_circuited_conditions: {
collapse_vars_regexp: { collapse_vars_regexp: {
options = { options = {
collapse_vars: true,
loops: false,
sequences: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true, booleans: true,
unused: true, cascade: true,
hoist_funs: true, collapse_vars: true,
keep_fargs: true, comparisons: true,
conditionals: true,
dead_code: true,
evaluate: true,
if_return: true, if_return: true,
join_vars: true, join_vars: true,
cascade: true, hoist_funs: true,
side_effects: true, keep_fargs: true,
loops: false,
reduce_vars: true, reduce_vars: true,
side_effects: true,
unused: true,
} }
input: { input: {
function f1() { function f1() {
@@ -1292,12 +1291,12 @@ collapse_vars_regexp: {
return [rx, k]; return [rx, k];
} }
function f2() { function f2() {
var rx = /[abc123]+/; var rx = /ab*/g;
return function(s) { return function(s) {
return rx.exec(s); return rx.exec(s);
}; };
} }
(function(){ (function() {
var result; var result;
var s = 'acdabcdeabbb'; var s = 'acdabcdeabbb';
var rx = /ab*/g; var rx = /ab*/g;
@@ -1305,22 +1304,35 @@ collapse_vars_regexp: {
console.log(result[0]); console.log(result[0]);
} }
})(); })();
(function() {
var result;
var s = 'acdabcdeabbb';
var rx = f2();
while (result = rx(s)) {
console.log(result[0]);
}
})();
} }
expect: { expect: {
function f1() { function f1() {
return [/[A-Z]+/, 9]; return [/[A-Z]+/, 9];
} }
function f2() { function f2() {
var rx = /[abc123]+/; var rx = /ab*/g;
return function(s) { return function(s) {
return rx.exec(s); return rx.exec(s);
}; };
} }
(function(){ (function() {
var result, rx = /ab*/g; var result, rx = /ab*/g;
while (result = rx.exec("acdabcdeabbb")) while (result = rx.exec("acdabcdeabbb"))
console.log(result[0]); console.log(result[0]);
})(); })();
(function() {
var result, rx = f2();
while (result = rx("acdabcdeabbb"))
console.log(result[0]);
})();
} }
expect_stdout: true expect_stdout: true
} }

View File

@@ -751,12 +751,12 @@ issue_1583: {
expect: { expect: {
function m(t) { function m(t) {
(function(e) { (function(e) {
t = e(); t = function() {
})(function() {
return (function(a) { return (function(a) {
return a; return function(a) {};
})(function(a) {}); })();
}); }();
})();
} }
} }
} }

View File

@@ -1021,6 +1021,7 @@ issue_1964_1: {
input: { input: {
function f() { function f() {
var long_variable_name = /\s/; var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1]; return "a b c".split(long_variable_name)[1];
} }
console.log(f()); console.log(f());
@@ -1028,11 +1029,15 @@ issue_1964_1: {
expect: { expect: {
function f() { function f() {
var long_variable_name = /\s/; var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1]; return "a b c".split(long_variable_name)[1];
} }
console.log(f()); console.log(f());
} }
expect_stdout: "b" expect_stdout: [
"\\s",
"b",
]
} }
issue_1964_2: { issue_1964_2: {
@@ -1045,17 +1050,22 @@ issue_1964_2: {
input: { input: {
function f() { function f() {
var long_variable_name = /\s/; var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1]; return "a b c".split(long_variable_name)[1];
} }
console.log(f()); console.log(f());
} }
expect: { expect: {
function f() { function f() {
console.log(/\s/.source);
return "a b c".split(/\s/)[1]; return "a b c".split(/\s/)[1];
} }
console.log(f()); console.log(f());
} }
expect_stdout: "b" expect_stdout: [
"\\s",
"b",
]
} }
array_slice_index: { array_slice_index: {

View File

@@ -172,6 +172,7 @@ unsafe_evaluate: {
options = { options = {
evaluate : true, evaluate : true,
reduce_vars : true, reduce_vars : true,
side_effects : true,
unsafe : true, unsafe : true,
unused : true unused : true
} }
@@ -1898,10 +1899,7 @@ redefine_farg_3: {
console.log(f([]), g([]), h([])); console.log(f([]), g([]), h([]));
} }
expect: { expect: {
console.log(function(a) { console.log(typeof [], "number", "undefined");
var a;
return typeof a;
}([]), "number", "undefined");
} }
expect_stdout: "object number undefined" expect_stdout: "object number undefined"
} }
@@ -2629,3 +2627,190 @@ for_in_prop: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
obj_var_1: {
options = {
evaluate: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: "2"
}
obj_var_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(2);
}
expect_stdout: "2"
}
obj_arg_1: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
function f(obj) {
return obj.bar();
}
console.log(f({
bar: function() {
return C + C;
}
}));
}
expect: {
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: "2"
}
obj_arg_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
function f(obj) {
return obj.bar();
}
console.log(f({
bar: function() {
return C + C;
}
}));
}
expect: {
console.log(2);
}
expect_stdout: "2"
}
func_arg_1: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 42;
!function(a) {
console.log(a());
}(function() {
return a;
});
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
func_arg_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 42;
!function(a) {
console.log(a());
}(function(a) {
return a;
});
}
expect: {
console.log(void 0);
}
expect_stdout: "undefined"
}
regex_loop: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(x) {
for (var r, s = "acdabcdeabbb"; r = x().exec(s);)
console.log(r[0]);
}
var a = /ab*/g;
f(function() {
return a;
});
}
expect: {
var a = /ab*/g;
(function(x) {
for (var r, s = "acdabcdeabbb"; r = x().exec(s);)
console.log(r[0]);
})(function() {
return a;
});
}
expect_stdout: true
}