perform reduce_vars on safe literals (#2351)
- constant expression - single reference - same scope - not across loop body
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {});
|
})();
|
||||||
});
|
}();
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user